From e2b7f90cd75fe1981496ae121d6d3c16c41c1f8f Mon Sep 17 00:00:00 2001 From: Yegor Kozlov Date: Fri, 29 Aug 2008 14:01:04 +0000 Subject: [PATCH] initial support for embedded movies and controls. git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@690262 13f79535-47bb-0310-9956-ffa450edef68 --- .../content/xdocs/hslf/how-to-shapes.xml | 23 ++ .../src/org/apache/poi/hslf/blip/WMF.java | 10 +- .../org/apache/poi/hslf/dev/PPTXMLDump.java | 2 +- .../apache/poi/hslf/model/ActiveXShape.java | 152 ++++++++ .../org/apache/poi/hslf/model/MovieShape.java | 159 +++++++++ .../apache/poi/hslf/model/ShapeFactory.java | 54 ++- .../apache/poi/hslf/model/SimpleShape.java | 59 +++- .../src/org/apache/poi/hslf/model/Table.java | 2 +- .../apache/poi/hslf/model/TextPainter.java | 129 ++++--- .../org/apache/poi/hslf/model/TextRun.java | 334 ++++++++++-------- .../org/apache/poi/hslf/model/TextShape.java | 1 + .../apache/poi/hslf/record/AnimationInfo.java | 97 +++++ .../poi/hslf/record/AnimationInfoAtom.java | 278 +++++++++++++++ .../apache/poi/hslf/record/ExAviMovie.java | 53 +++ .../org/apache/poi/hslf/record/ExControl.java | 19 +- .../apache/poi/hslf/record/ExControlAtom.java | 25 +- .../org/apache/poi/hslf/record/ExEmbed.java | 48 ++- .../apache/poi/hslf/record/ExMCIMovie.java | 100 ++++++ .../apache/poi/hslf/record/ExMediaAtom.java | 172 +++++++++ .../apache/poi/hslf/record/ExOleObjAtom.java | 108 +++++- .../apache/poi/hslf/record/ExOleObjStg.java | 29 +- .../poi/hslf/record/ExVideoContainer.java | 108 ++++++ .../poi/hslf/record/InteractiveInfoAtom.java | 44 ++- .../apache/poi/hslf/record/OEShapeAtom.java | 110 ++++++ .../apache/poi/hslf/record/RecordTypes.java | 15 +- .../poi/hslf/record/StyleTextPropAtom.java | 10 +- .../apache/poi/hslf/record/TextRulerAtom.java | 17 +- .../poi/hslf/usermodel/RichTextRun.java | 1 + .../apache/poi/hslf/usermodel/SlideShow.java | 103 ++++-- .../apache/poi/hslf/model/TestMovieShape.java | 66 ++++ .../org/apache/poi/hslf/model/TestTable.java | 63 ++++ .../hslf/record/TestAnimationInfoAtom.java | 91 +++++ .../apache/poi/hslf/record/TestExControl.java | 127 +++++++ .../poi/hslf/record/TestExMediaAtom.java | 94 +++++ .../poi/hslf/record/TestExOleObjAtom.java | 78 ++++ .../poi/hslf/record/TestExOleObjStg.java | 144 ++++++++ .../poi/hslf/record/TestExVideoContainer.java | 90 +++++ .../poi/hslf/record/TestTextRulerAtom.java | 16 + 38 files changed, 2694 insertions(+), 337 deletions(-) create mode 100755 src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java create mode 100755 src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java create mode 100755 src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java diff --git a/src/documentation/content/xdocs/hslf/how-to-shapes.xml b/src/documentation/content/xdocs/hslf/how-to-shapes.xml index 37bf18373..db91bb72c 100644 --- a/src/documentation/content/xdocs/hslf/how-to-shapes.xml +++ b/src/documentation/content/xdocs/hslf/how-to-shapes.xml @@ -47,6 +47,7 @@
  • Shapes and Graphics2D
  • How to convert slides into images
  • Headers / Footers
  • +
  • How to embed movies
  • Features @@ -662,6 +663,28 @@ hdd.setFootersText("Created by POI-HSLF");
    + +
    How to embed movies + + + SlideShow ppt = new SlideShow(); + Slide slide = ppt.createSlide(); + + //UNC or local Returns UNC or local path to a video file + String moviePath = "card.mpg"; + int movieIdx = ppt.addMovie(moviePath, MovieShape.MOVIE_MPEG); + + String thumbnailPath = "card.png"; + int thumbnailIdx = ppt.addPicture(new File(thumbnailPath), Picture.PNG); + MovieShape shape = new MovieShape(movieIdx, thumbnailIdx); + shape.setAnchor(new Rectangle2D.Float(300,225,120,90)); + slide.addShape(shape); + + FileOutputStream out = new FileOutputStream("hslf-movie.ppt"); + ppt.write(out); + out.close(); + +
    diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java b/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java index fe18664d0..b7f22c137 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java +++ b/src/scratchpad/src/org/apache/poi/hslf/blip/WMF.java @@ -17,6 +17,7 @@ package org.apache.poi.hslf.blip; import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; import org.apache.poi.hslf.model.Picture; import org.apache.poi.hslf.model.Shape; import org.apache.poi.hslf.exceptions.HSLFException; @@ -76,7 +77,7 @@ public class WMF extends Metafile { Header header = new Header(); header.wmfsize = data.length - aldus.getSize(); header.bounds = new java.awt.Rectangle((short)aldus.left, (short)aldus.top, (short)aldus.right-(short)aldus.left, (short)aldus.bottom-(short)aldus.top); - //coefficiaent to translate from WMF dpi to 96pdi + //coefficient to translate from WMF dpi to 96pdi int coeff = 96*Shape.EMU_PER_POINT/aldus.inch; header.size = new java.awt.Dimension(header.bounds.width*coeff, header.bounds.height*coeff); header.zipsize = compressed.length; @@ -119,7 +120,7 @@ public class WMF extends Metafile { *
  • short Checksum; Checksum value for previous 10 shorts * */ - public static class AldusHeader{ + public class AldusHeader{ public static final int APMHEADER_KEY = 0x9AC6CDD7; public int handle; @@ -143,8 +144,9 @@ public class WMF extends Metafile { reserved = LittleEndian.getInt(data, pos); pos += LittleEndian.INT_SIZE; checksum = LittleEndian.getShort(data, pos); pos += LittleEndian.SHORT_SIZE; - if (checksum != getChecksum()) - throw new HSLFException("WMF checksum does not match the header data"); + if (checksum != getChecksum()){ + logger.log(POILogger.WARN, "WMF checksum does not match the header data"); + } } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java b/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java index 603361e4d..d02c4928e 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java +++ b/src/scratchpad/src/org/apache/poi/hslf/dev/PPTXMLDump.java @@ -125,7 +125,7 @@ public class PPTXMLDump { dump(data, pos, size, padding); } else { //dump first 100 bytes of the atom data - dump(out, data, pos, Math.min(size, 100), padding, true); + dump(out, data, pos, size, padding, true); } padding--; write(out, "" + CR, padding); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java new file mode 100755 index 000000000..4b1889a91 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ActiveXShape.java @@ -0,0 +1,152 @@ +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.poi.util.LittleEndian; + +import java.io.ByteArrayOutputStream; +import java.io.UnsupportedEncodingException; +import java.util.Iterator; + +/** + * Represents an ActiveX control in a PowerPoint document. + * + * TODO: finish + * @author Yegor Kozlov + */ +public class ActiveXShape extends Picture { + public static final int DEFAULT_ACTIVEX_THUMBNAIL = -1; + + /** + * Create a new Picture + * + * @param pictureIdx the index of the picture + */ + public ActiveXShape(int movieIdx, int pictureIdx){ + super(pictureIdx, null); + setActiveXIndex(movieIdx); + } + + /** + * Create a Picture object + * + * @param escherRecord the EscherSpContainer record which holds information about + * this picture in the Slide + * @param parent the parent shape of this picture + */ + protected ActiveXShape(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + /** + * Create a new Placeholder and initialize internal structures + * + * @return the created EscherContainerRecord which holds shape data + */ + protected EscherContainerRecord createSpContainer(int idx, boolean isChild) { + _escherContainer = super.createSpContainer(idx, isChild); + + EscherSpRecord spRecord = _escherContainer.getChildById(EscherSpRecord.RECORD_ID); + spRecord.setFlags(EscherSpRecord.FLAG_HAVEANCHOR | EscherSpRecord.FLAG_HASSHAPETYPE | EscherSpRecord.FLAG_OLESHAPE); + + setShapeType(ShapeTypes.HostControl); + setEscherProperty(EscherProperties.BLIP__PICTUREID, idx); + setEscherProperty(EscherProperties.LINESTYLE__COLOR, 0x8000001); + setEscherProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 0x80008); + setEscherProperty(EscherProperties.SHADOWSTYLE__COLOR, 0x8000002); + setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, -1); + + EscherClientDataRecord cldata = new EscherClientDataRecord(); + cldata.setOptions((short)0xF); + _escherContainer.getChildRecords().add(cldata); + + OEShapeAtom oe = new OEShapeAtom(); + + //convert hslf into ddf + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + oe.writeOut(out); + } catch(Exception e){ + throw new HSLFException(e); + } + cldata.setRemainingData(out.toByteArray()); + + return _escherContainer; + } + + /** + * Assign a control to this shape + * + * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)} + * @param idx the index of the movie + */ + public void setActiveXIndex(int idx){ + EscherContainerRecord spContainer = getSpContainer(); + for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) { + EscherRecord obj = (EscherRecord) it.next(); + if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) { + EscherClientDataRecord clientRecord = (EscherClientDataRecord)obj; + byte[] recdata = clientRecord.getRemainingData(); + LittleEndian.putInt(recdata, 8, idx); + } + } + } + + public int getControlIndex(){ + int idx = -1; + OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + if(oe != null) idx = oe.getOptions(); + return idx; + } + + /** + * Set a property of this ActiveX control + * @param key + * @param value + */ + public void setProperty(String key, String value){ + + } + + /** + * Document-level container that specifies information about an ActiveX control + * + * @return container that specifies information about an ActiveX control + */ + public ExControl getExControl(){ + int idx = getControlIndex(); + ExControl ctrl = null; + Document doc = getSheet().getSlideShow().getDocumentRecord(); + ExObjList lst = (ExObjList)doc.findFirstOfType(RecordTypes.ExObjList.typeID); + if(lst != null){ + Record[] ch = lst.getChildRecords(); + for (int i = 0; i < ch.length; i++) { + if(ch[i] instanceof ExControl){ + ExControl c = (ExControl)ch[i]; + if(c.getExOleObjAtom().getObjID() == idx){ + ctrl = c; + break; + } + } + } + } + return ctrl; + } + + protected void afterInsert(Sheet sheet){ + ExControl ctrl = getExControl(); + ctrl.getExControlAtom().setSlideId(sheet._getSheetNumber()); + + try { + String name = ctrl.getProgId() + "-" + getControlIndex(); + byte[] data = (name + '\u0000').getBytes("UTF-16LE"); + EscherComplexProperty prop = new EscherComplexProperty(EscherProperties.GROUPSHAPE__SHAPENAME, false, data); + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + opt.addEscherProperty(prop); + } catch (UnsupportedEncodingException e){ + throw new HSLFException(e); + } + + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java new file mode 100755 index 000000000..4cf7037d1 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/MovieShape.java @@ -0,0 +1,159 @@ +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.util.LittleEndian; + +import java.io.ByteArrayOutputStream; +import java.util.Iterator; + +/** + * Represents a movie in a PowerPoint document. + * + * @author Yegor Kozlov + */ +public class MovieShape extends Picture { + public static final int DEFAULT_MOVIE_THUMBNAIL = -1; + + public static final int MOVIE_MPEG = 1; + public static final int MOVIE_AVI = 2; + + /** + * Create a new Picture + * + * @param pictureIdx the index of the picture + */ + public MovieShape(int movieIdx, int pictureIdx){ + super(pictureIdx, null); + setMovieIndex(movieIdx); + setAutoPlay(true); + } + + /** + * Create a new Picture + * + * @param idx the index of the picture + * @param parent the parent shape + */ + public MovieShape(int movieIdx, int idx, Shape parent) { + super(idx, parent); + setMovieIndex(movieIdx); + } + + /** + * Create a Picture object + * + * @param escherRecord the EscherSpContainer record which holds information about + * this picture in the Slide + * @param parent the parent shape of this picture + */ + protected MovieShape(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + /** + * Create a new Placeholder and initialize internal structures + * + * @return the created EscherContainerRecord which holds shape data + */ + protected EscherContainerRecord createSpContainer(int idx, boolean isChild) { + _escherContainer = super.createSpContainer(idx, isChild); + + setEscherProperty(EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x1000100); + setEscherProperty(EscherProperties.FILL__NOFILLHITTEST, 0x10001); + + EscherClientDataRecord cldata = new EscherClientDataRecord(); + cldata.setOptions((short)0xF); + _escherContainer.getChildRecords().add(cldata); + + OEShapeAtom oe = new OEShapeAtom(); + InteractiveInfo info = new InteractiveInfo(); + InteractiveInfoAtom infoAtom = info.getInteractiveInfoAtom(); + infoAtom.setAction(InteractiveInfoAtom.ACTION_MEDIA); + infoAtom.setHyperlinkType(InteractiveInfoAtom.LINK_NULL); + + AnimationInfo an = new AnimationInfo(); + AnimationInfoAtom anAtom = an.getAnimationInfoAtom(); + anAtom.setFlag(AnimationInfoAtom.Automatic, true); + + //convert hslf into ddf + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + oe.writeOut(out); + an.writeOut(out); + info.writeOut(out); + } catch(Exception e){ + throw new HSLFException(e); + } + cldata.setRemainingData(out.toByteArray()); + + return _escherContainer; + } + + /** + * Assign a movie to this shape + * + * @see {@link org.apache.poi.hslf.usermodel.SlideShow#addMovie(String, int)} + * @param idx the index of the movie + */ + public void setMovieIndex(int idx){ + OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + oe.setOptions(idx); + + AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + if(an != null) { + AnimationInfoAtom ai = an.getAnimationInfoAtom(); + ai.setDimColor(0x07000000); + ai.setFlag(AnimationInfoAtom.Automatic, true); + ai.setFlag(AnimationInfoAtom.Play, true); + ai.setFlag(AnimationInfoAtom.Synchronous, true); + ai.setOrderID(idx + 1); + } + } + + public void setAutoPlay(boolean flag){ + AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + if(an != null){ + an.getAnimationInfoAtom().setFlag(AnimationInfoAtom.Automatic, flag); + updateClientData(); + } + } + + public boolean isAutoPlay(){ + AnimationInfo an = (AnimationInfo)getClientDataRecord(RecordTypes.AnimationInfo.typeID); + if(an != null){ + return an.getAnimationInfoAtom().getFlag(AnimationInfoAtom.Automatic); + } + return false; + } + + /** + * Returns UNC or local path to a video file + * + * @return UNC or local path to a video file + */ + public String getPath(){ + OEShapeAtom oe = (OEShapeAtom)getClientDataRecord(RecordTypes.OEShapeAtom.typeID); + int idx = oe.getOptions(); + + SlideShow ppt = getSheet().getSlideShow(); + ExObjList lst = (ExObjList)ppt.getDocumentRecord().findFirstOfType(RecordTypes.ExObjList.typeID); + if(lst == null) return null; + + Record[] r = lst.getChildRecords(); + for (int i = 0; i < r.length; i++) { + if(r[i] instanceof ExMCIMovie){ + ExMCIMovie mci = (ExMCIMovie)r[i]; + ExVideoContainer exVideo = mci.getExVideo(); + int objectId = exVideo.getExMediaAtom().getObjectId(); + if(objectId == idx){ + return exVideo.getPathAtom().getText(); + } + } + + } + return null; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java index 4bef9033d..8b4abd5e5 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -19,8 +19,10 @@ package org.apache.poi.hslf.model; import org.apache.poi.ddf.*; import org.apache.poi.util.POILogger; import org.apache.poi.util.POILogFactory; +import org.apache.poi.hslf.record.*; import java.util.List; +import java.util.Iterator; /** * Create a Shape object depending on its type @@ -45,14 +47,14 @@ public class ShapeFactory { public static ShapeGroup createShapeGroup(EscherContainerRecord spContainer, Shape parent){ ShapeGroup group = null; - UnknownEscherRecord opt = (UnknownEscherRecord)Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122); + EscherRecord opt = Shape.getEscherChild((EscherContainerRecord)spContainer.getChild(0), (short)0xF122); if(opt != null){ try { EscherPropertyFactory f = new EscherPropertyFactory(); - List props = f.createProperties( opt.getData(), 0, opt.getInstance() ); + List props = f.createProperties( opt.serialize(), 8, opt.getInstance() ); EscherSimpleProperty p = (EscherSimpleProperty)props.get(0); if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){ - group = new ShapeGroup(spContainer, parent); + group = new Table(spContainer, parent); } else { group = new ShapeGroup(spContainer, parent); } @@ -68,7 +70,7 @@ public class ShapeFactory { } public static Shape createSimpeShape(EscherContainerRecord spContainer, Shape parent){ - Shape shape; + Shape shape = null; EscherSpRecord spRecord = spContainer.getChildById(EscherSpRecord.RECORD_ID); int type = spRecord.getOptions() >> 4; @@ -76,14 +78,26 @@ public class ShapeFactory { case ShapeTypes.TextBox: shape = new TextBox(spContainer, parent); break; - case ShapeTypes.HostControl: + case ShapeTypes.HostControl: case ShapeTypes.PictureFrame: { - EscherOptRecord opt = (EscherOptRecord)Shape.getEscherChild(spContainer, EscherOptRecord.RECORD_ID); - EscherProperty prop = Shape.getEscherProperty(opt, EscherProperties.BLIP__PICTUREID); - if(prop != null) - shape = new OLEShape(spContainer, parent); //presence of BLIP__PICTUREID indicates it is an embedded object - else - shape = new Picture(spContainer, parent); + InteractiveInfo info = (InteractiveInfo)getClientDataRecord(spContainer, RecordTypes.InteractiveInfo.typeID); + OEShapeAtom oes = (OEShapeAtom)getClientDataRecord(spContainer, RecordTypes.OEShapeAtom.typeID); + if(info != null && info.getInteractiveInfoAtom() != null){ + switch(info.getInteractiveInfoAtom().getAction()){ + case InteractiveInfoAtom.ACTION_OLE: + shape = new OLEShape(spContainer, parent); + break; + case InteractiveInfoAtom.ACTION_MEDIA: + shape = new MovieShape(spContainer, parent); + break; + default: + break; + } + } else if (oes != null){ + shape = new OLEShape(spContainer, parent); + } + + if(shape == null) shape = new Picture(spContainer, parent); break; } case ShapeTypes.Line: @@ -108,4 +122,22 @@ public class ShapeFactory { return shape; } + + protected static Record getClientDataRecord(EscherContainerRecord spContainer, int recordType) { + Record oep = null; + for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) { + EscherRecord obj = (EscherRecord) it.next(); + if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) { + byte[] data = obj.serialize(); + Record[] records = Record.findChildRecords(data, 8, data.length - 8); + for (int j = 0; j < records.length; j++) { + if (records[j].getRecordType() == recordType) { + return records[j]; + } + } + } + } + return oep; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java index d336cb3f3..71d10948c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/SimpleShape.java @@ -21,11 +21,13 @@ import org.apache.poi.ddf.*; import org.apache.poi.util.LittleEndian; import org.apache.poi.hslf.record.ColorSchemeAtom; import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.exceptions.HSLFException; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.util.Iterator; +import java.io.ByteArrayOutputStream; /** * An abstract simple (non-group) shape. @@ -35,6 +37,12 @@ import java.util.Iterator; */ public class SimpleShape extends Shape { + /** + * Records stored in EscherClientDataRecord + */ + protected Record[] _clientRecords; + protected EscherClientDataRecord _clientData; + /** * Create a SimpleShape object and initialize it from the supplied Record container. * @@ -293,21 +301,46 @@ public class SimpleShape extends Shape { * @param recordType type of the record to search */ protected Record getClientDataRecord(int recordType) { - Record oep = null; - EscherContainerRecord spContainer = getSpContainer(); - for (Iterator it = spContainer.getChildRecords().iterator(); it.hasNext();) { - EscherRecord obj = (EscherRecord) it.next(); - if (obj.getRecordId() == EscherClientDataRecord.RECORD_ID) { - byte[] data = obj.serialize(); - Record[] records = Record.findChildRecords(data, 8, data.length - 8); - for (int j = 0; j < records.length; j++) { - if (records[j].getRecordType() == recordType) { - return records[j]; - } - } + + Record[] records = getClientRecords(); + if(records != null) for (int i = 0; i < records.length; i++) { + if(records[i].getRecordType() == recordType){ + return records[i]; } } - return oep; + return null; } + protected Record[] getClientRecords() { + if(_clientData == null){ + EscherRecord r = Shape.getEscherChild(getSpContainer(), EscherClientDataRecord.RECORD_ID); + //ddf can return EscherContainerRecord with recordId=EscherClientDataRecord.RECORD_ID + //convert in to EscherClientDataRecord on the fly + if(!(r instanceof EscherClientDataRecord)){ + byte[] data = r.serialize(); + r = new EscherClientDataRecord(); + r.fillFields(data, 0, new DefaultEscherRecordFactory()); + } + _clientData = (EscherClientDataRecord)r; + } + if(_clientData != null && _clientRecords == null){ + byte[] data = _clientData.getRemainingData(); + _clientRecords = Record.findChildRecords(data, 0, data.length); + } + return _clientRecords; + } + + protected void updateClientData() { + if(_clientData != null && _clientRecords != null){ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + try { + for (int i = 0; i < _clientRecords.length; i++) { + _clientRecords[i].writeOut(out); + } + } catch(Exception e){ + throw new HSLFException(e); + } + _clientData.setRemainingData(out.toByteArray()); + } + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Table.java b/src/scratchpad/src/org/apache/poi/hslf/model/Table.java index 43dab58af..113a2d8f2 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Table.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Table.java @@ -89,7 +89,7 @@ public class Table extends ShapeGroup { * @param escherRecord EscherSpContainer container which holds information about this shape * @param parent the parent of the shape */ - protected Table(EscherContainerRecord escherRecord, Shape parent) { + public Table(EscherContainerRecord escherRecord, Shape parent) { super(escherRecord, parent); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java index 6eff54329..9a86120e7 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextPainter.java @@ -23,9 +23,11 @@ import org.apache.poi.util.POILogFactory; import java.text.AttributedString; import java.text.AttributedCharacterIterator; +import java.text.BreakIterator; import java.awt.font.TextAttribute; import java.awt.font.LineBreakMeasurer; import java.awt.font.TextLayout; +import java.awt.font.FontRenderContext; import java.awt.*; import java.awt.geom.Rectangle2D; import java.awt.geom.Point2D; @@ -89,11 +91,69 @@ public class TextPainter { } public void paint(Graphics2D graphics){ + Rectangle2D anchor = _shape.getLogicalAnchor2D(); + TextElement[] elem = getTextElements((float)anchor.getWidth(), graphics.getFontRenderContext()); + if(elem == null) return; + + float textHeight = 0; + for (int i = 0; i < elem.length; i++) { + textHeight += elem[i].ascent + elem[i].descent; + } + + int valign = _shape.getVerticalAlignment(); + double y0 = anchor.getY(); + switch (valign){ + case TextShape.AnchorTopBaseline: + case TextShape.AnchorTop: + y0 += _shape.getMarginTop(); + break; + case TextShape.AnchorBottom: + y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom(); + break; + default: + case TextShape.AnchorMiddle: + float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom(); + y0 += _shape.getMarginTop() + delta/2; + break; + } + + //finally draw the text fragments + for (int i = 0; i < elem.length; i++) { + y0 += elem[i].ascent; + + Point2D.Double pen = new Point2D.Double(); + pen.y = y0; + switch (elem[i]._align) { + default: + case TextShape.AlignLeft: + pen.x = anchor.getX() + _shape.getMarginLeft(); + break; + case TextShape.AlignCenter: + pen.x = anchor.getX() + _shape.getMarginLeft() + + (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2; + break; + case TextShape.AlignRight: + pen.x = anchor.getX() + _shape.getMarginLeft() + + (anchor.getWidth() - elem[i].advance - _shape.getMarginLeft() - _shape.getMarginRight()); + break; + } + if(elem[i]._bullet != null){ + graphics.drawString(elem[i]._bullet.getIterator(), (float)(pen.x + elem[i]._bulletOffset), (float)pen.y); + } + AttributedCharacterIterator chIt = elem[i]._text.getIterator(); + if(chIt.getEndIndex() > chIt.getBeginIndex()) { + graphics.drawString(chIt, (float)(pen.x + elem[i]._textOffset), (float)pen.y); + } + y0 += elem[i].descent; + } + } + + public TextElement[] getTextElements(float textWidth, FontRenderContext frc){ TextRun run = _shape.getTextRun(); - if (run == null) return; + if (run == null) return null; String text = run.getText(); - if (text == null || text.equals("")) return; + if (text == null || text.equals("")) return null; AttributedString at = getAttributedString(run); @@ -101,11 +161,8 @@ public class TextPainter { int paragraphStart = it.getBeginIndex(); int paragraphEnd = it.getEndIndex(); - Rectangle2D anchor = _shape.getLogicalAnchor2D(); - - float textHeight = 0; ArrayList lines = new ArrayList(); - LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()); + LineBreakMeasurer measurer = new LineBreakMeasurer(it, frc); measurer.setPosition(paragraphStart); while (measurer.getPosition() < paragraphEnd) { int startIndex = measurer.getPosition(); @@ -120,7 +177,7 @@ public class TextPainter { break; } - float wrappingWidth = (float)anchor.getWidth() - _shape.getMarginLeft() - _shape.getMarginRight(); + float wrappingWidth = textWidth - _shape.getMarginLeft() - _shape.getMarginRight(); int bulletOffset = rt.getBulletOffset(); int textOffset = rt.getTextOffset(); int indent = rt.getIndentLevel(); @@ -138,7 +195,7 @@ public class TextPainter { if(text_val != 0) textOffset = text_val; } - wrappingWidth -= textOffset; + if(bulletOffset > 0 || prStart || startIndex == 0) wrappingWidth -= textOffset; if (_shape.getWordWrap() == TextShape.WrapNone) { wrappingWidth = _shape.getSheet().getSlideShow().getPageSize().width; @@ -147,7 +204,7 @@ public class TextPainter { TextLayout textLayout = measurer.nextLayout(wrappingWidth + 1, nextBreak == -1 ? paragraphEnd : nextBreak, true); if (textLayout == null) { - textLayout = measurer.nextLayout((float)anchor.getWidth(), + textLayout = measurer.nextLayout(textWidth, nextBreak == -1 ? paragraphEnd : nextBreak, false); } if(textLayout == null){ @@ -173,6 +230,8 @@ public class TextPainter { el.advance = textLayout.getAdvance(); el._textOffset = textOffset; el._text = new AttributedString(it, startIndex, endIndex); + el.textStartIndex = startIndex; + el.textEndIndex = endIndex; if (prStart){ int sp = rt.getSpaceBefore(); @@ -203,8 +262,6 @@ public class TextPainter { } el.descent = descent; - textHeight += el.ascent + el.descent; - if(rt.isBullet() && (prStart || startIndex == 0)){ it.setIndex(startIndex); @@ -236,56 +293,11 @@ public class TextPainter { lines.add(el); } - int valign = _shape.getVerticalAlignment(); - double y0 = anchor.getY(); - switch (valign){ - case TextShape.AnchorTopBaseline: - case TextShape.AnchorTop: - y0 += _shape.getMarginTop(); - break; - case TextShape.AnchorBottom: - y0 += anchor.getHeight() - textHeight - _shape.getMarginBottom(); - break; - default: - case TextShape.AnchorMiddle: - float delta = (float)anchor.getHeight() - textHeight - _shape.getMarginTop() - _shape.getMarginBottom(); - y0 += _shape.getMarginTop() + delta/2; - break; - } - //finally draw the text fragments - for (int i = 0; i < lines.size(); i++) { - TextElement elem = (TextElement)lines.get(i); - y0 += elem.ascent; - - Point2D.Double pen = new Point2D.Double(); - pen.y = y0; - switch (elem._align) { - default: - case TextShape.AlignLeft: - pen.x = anchor.getX() + _shape.getMarginLeft(); - break; - case TextShape.AlignCenter: - pen.x = anchor.getX() + _shape.getMarginLeft() + - (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()) / 2; - break; - case TextShape.AlignRight: - pen.x = anchor.getX() + _shape.getMarginLeft() + - (anchor.getWidth() - elem.advance - _shape.getMarginLeft() - _shape.getMarginRight()); - break; - } - if(elem._bullet != null){ - graphics.drawString(elem._bullet.getIterator(), (float)(pen.x + elem._bulletOffset), (float)pen.y); - } - AttributedCharacterIterator chIt = elem._text.getIterator(); - if(chIt.getEndIndex() > chIt.getBeginIndex()) { - graphics.drawString(chIt, (float)(pen.x + elem._textOffset), (float)pen.y); - } - y0 += elem.descent; - } + TextElement[] elems = new TextElement[lines.size()]; + return (TextElement[])lines.toArray(elems); } - public static class TextElement { public AttributedString _text; public int _textOffset; @@ -294,5 +306,6 @@ public class TextPainter { public int _align; public float ascent, descent; public float advance; + public int textStartIndex, textEndIndex; } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java index 27dbe1b2d..cf2db50e8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextRun.java @@ -22,6 +22,7 @@ package org.apache.poi.hslf.model; import java.util.LinkedList; import java.util.Vector; +import java.util.List; import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.*; @@ -45,7 +46,8 @@ public class TextRun protected TextBytesAtom _byteAtom; protected TextCharsAtom _charAtom; protected StyleTextPropAtom _styleAtom; - protected boolean _isUnicode; + protected TextRulerAtom _ruler; + protected boolean _isUnicode; protected RichTextRun[] _rtRuns; private SlideShow slideShow; private Sheet sheet; @@ -103,155 +105,159 @@ public class TextRun pStyles = _styleAtom.getParagraphStyles(); cStyles = _styleAtom.getCharacterStyles(); } - - // Handle case of no current style, with a default - if(pStyles.size() == 0 || cStyles.size() == 0) { - _rtRuns = new RichTextRun[1]; - _rtRuns[0] = new RichTextRun(this, 0, runRawText.length()); - } else { - // Build up Rich Text Runs, one for each - // character/paragraph style pair - Vector rtrs = new Vector(); - - int pos = 0; - - int curP = 0; - int curC = 0; - int pLenRemain = -1; - int cLenRemain = -1; - - // Build one for each run with the same style - while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) { - // Get the Props to use - TextPropCollection pProps = (TextPropCollection)pStyles.get(curP); - TextPropCollection cProps = (TextPropCollection)cStyles.get(curC); - - int pLen = pProps.getCharactersCovered(); - int cLen = cProps.getCharactersCovered(); - - // Handle new pass - boolean freshSet = false; - if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; } - if(pLenRemain == -1) { pLenRemain = pLen; } - if(cLenRemain == -1) { cLenRemain = cLen; } - - // So we know how to build the eventual run - int runLen = -1; - boolean pShared = false; - boolean cShared = false; - - // Same size, new styles - neither shared - if(pLen == cLen && freshSet) { - runLen = cLen; - pShared = false; - cShared = false; - curP++; - curC++; - pLenRemain = -1; - cLenRemain = -1; - } else { - // Some sharing - - // See if we are already in a shared block - if(pLenRemain < pLen) { - // Existing shared p block - pShared = true; - - // Do we end with the c block, or either side of it? - if(pLenRemain == cLenRemain) { - // We end at the same time - cShared = false; - runLen = pLenRemain; - curP++; - curC++; - pLenRemain = -1; - cLenRemain = -1; - } else if(pLenRemain < cLenRemain) { - // We end before the c block - cShared = true; - runLen = pLenRemain; - curP++; - cLenRemain -= pLenRemain; - pLenRemain = -1; - } else { - // We end after the c block - cShared = false; - runLen = cLenRemain; - curC++; - pLenRemain -= cLenRemain; - cLenRemain = -1; - } - } else if(cLenRemain < cLen) { - // Existing shared c block - cShared = true; - - // Do we end with the p block, or either side of it? - if(pLenRemain == cLenRemain) { - // We end at the same time - pShared = false; - runLen = cLenRemain; - curP++; - curC++; - pLenRemain = -1; - cLenRemain = -1; - } else if(cLenRemain < pLenRemain) { - // We end before the p block - pShared = true; - runLen = cLenRemain; - curC++; - pLenRemain -= cLenRemain; - cLenRemain = -1; - } else { - // We end after the p block - pShared = false; - runLen = pLenRemain; - curP++; - cLenRemain -= pLenRemain; - pLenRemain = -1; - } - } else { - // Start of a shared block - if(pLenRemain < cLenRemain) { - // Shared c block - pShared = false; - cShared = true; - runLen = pLenRemain; - curP++; - cLenRemain -= pLenRemain; - pLenRemain = -1; - } else { - // Shared p block - pShared = true; - cShared = false; - runLen = cLenRemain; - curC++; - pLenRemain -= cLenRemain; - cLenRemain = -1; - } - } - } - - // Wind on - int prevPos = pos; - pos += runLen; - // Adjust for end-of-run extra 1 length - if(pos > runRawText.length()) { - runLen--; - } - - // Save - RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared); - rtrs.add(rtr); - } - - // Build the array - _rtRuns = new RichTextRun[rtrs.size()]; - rtrs.copyInto(_rtRuns); - } + buildRichTextRuns(pStyles, cStyles, runRawText); } - - // Update methods follow + public void buildRichTextRuns(LinkedList pStyles, LinkedList cStyles, String runRawText){ + + // Handle case of no current style, with a default + if(pStyles.size() == 0 || cStyles.size() == 0) { + _rtRuns = new RichTextRun[1]; + _rtRuns[0] = new RichTextRun(this, 0, runRawText.length()); + } else { + // Build up Rich Text Runs, one for each + // character/paragraph style pair + Vector rtrs = new Vector(); + + int pos = 0; + + int curP = 0; + int curC = 0; + int pLenRemain = -1; + int cLenRemain = -1; + + // Build one for each run with the same style + while(pos <= runRawText.length() && curP < pStyles.size() && curC < cStyles.size()) { + // Get the Props to use + TextPropCollection pProps = (TextPropCollection)pStyles.get(curP); + TextPropCollection cProps = (TextPropCollection)cStyles.get(curC); + + int pLen = pProps.getCharactersCovered(); + int cLen = cProps.getCharactersCovered(); + + // Handle new pass + boolean freshSet = false; + if(pLenRemain == -1 && cLenRemain == -1) { freshSet = true; } + if(pLenRemain == -1) { pLenRemain = pLen; } + if(cLenRemain == -1) { cLenRemain = cLen; } + + // So we know how to build the eventual run + int runLen = -1; + boolean pShared = false; + boolean cShared = false; + + // Same size, new styles - neither shared + if(pLen == cLen && freshSet) { + runLen = cLen; + pShared = false; + cShared = false; + curP++; + curC++; + pLenRemain = -1; + cLenRemain = -1; + } else { + // Some sharing + + // See if we are already in a shared block + if(pLenRemain < pLen) { + // Existing shared p block + pShared = true; + + // Do we end with the c block, or either side of it? + if(pLenRemain == cLenRemain) { + // We end at the same time + cShared = false; + runLen = pLenRemain; + curP++; + curC++; + pLenRemain = -1; + cLenRemain = -1; + } else if(pLenRemain < cLenRemain) { + // We end before the c block + cShared = true; + runLen = pLenRemain; + curP++; + cLenRemain -= pLenRemain; + pLenRemain = -1; + } else { + // We end after the c block + cShared = false; + runLen = cLenRemain; + curC++; + pLenRemain -= cLenRemain; + cLenRemain = -1; + } + } else if(cLenRemain < cLen) { + // Existing shared c block + cShared = true; + + // Do we end with the p block, or either side of it? + if(pLenRemain == cLenRemain) { + // We end at the same time + pShared = false; + runLen = cLenRemain; + curP++; + curC++; + pLenRemain = -1; + cLenRemain = -1; + } else if(cLenRemain < pLenRemain) { + // We end before the p block + pShared = true; + runLen = cLenRemain; + curC++; + pLenRemain -= cLenRemain; + cLenRemain = -1; + } else { + // We end after the p block + pShared = false; + runLen = pLenRemain; + curP++; + cLenRemain -= pLenRemain; + pLenRemain = -1; + } + } else { + // Start of a shared block + if(pLenRemain < cLenRemain) { + // Shared c block + pShared = false; + cShared = true; + runLen = pLenRemain; + curP++; + cLenRemain -= pLenRemain; + pLenRemain = -1; + } else { + // Shared p block + pShared = true; + cShared = false; + runLen = cLenRemain; + curC++; + pLenRemain -= cLenRemain; + cLenRemain = -1; + } + } + } + + // Wind on + int prevPos = pos; + pos += runLen; + // Adjust for end-of-run extra 1 length + if(pos > runRawText.length()) { + runLen--; + } + + // Save + RichTextRun rtr = new RichTextRun(this, prevPos, runLen, pProps, cProps, pShared, cShared); + rtrs.add(rtr); + } + + // Build the array + _rtRuns = new RichTextRun[rtrs.size()]; + rtrs.copyInto(_rtRuns); + } + + } + + // Update methods follow /** * Adds the supplied text onto the end of the TextRun, @@ -379,7 +385,7 @@ public class TextRun * @param run * @param s */ - public synchronized void changeTextInRichTextRun(RichTextRun run, String s) { + public void changeTextInRichTextRun(RichTextRun run, String s) { // Figure out which run it is int runID = -1; for(int i=0; i<_rtRuns.length; i++) { @@ -457,7 +463,7 @@ public class TextRun * as the the first character has. * If you care about styling, do setText on a RichTextRun instead */ - public synchronized void setRawText(String s) { + public void setRawText(String s) { // Save the new text to the atoms storeText(s); RichTextRun fst = _rtRuns[0]; @@ -491,7 +497,7 @@ public class TextRun * Changes the text. * Converts '\r' into '\n' */ - public synchronized void setText(String s) { + public void setText(String s) { String text = normalize(s); setRawText(text); } @@ -500,7 +506,7 @@ public class TextRun * Ensure a StyleTextPropAtom is present for this run, * by adding if required. Normally for internal TextRun use. */ - public synchronized void ensureStyleAtomPresent() { + public void ensureStyleAtomPresent() { if(_styleAtom != null) { // All there return; @@ -669,11 +675,26 @@ public class TextRun } public TextRulerAtom getTextRuler(){ - for (int i = 0; i < _records.length; i++) { - if(_records[i] instanceof TextRulerAtom) return (TextRulerAtom)_records[i]; - } - return null; + if(_ruler == null){ + if(_records != null) for (int i = 0; i < _records.length; i++) { + if(_records[i] instanceof TextRulerAtom) { + _ruler = (TextRulerAtom)_records[i]; + break; + } + } + } + return _ruler; + + } + + public TextRulerAtom createTextRuler(){ + _ruler = getTextRuler(); + if(_ruler == null){ + _ruler = TextRulerAtom.getParagraphInstance(); + _headerAtom.getParentRecord().appendChildRecord(_ruler); + } + return _ruler; } /** @@ -692,4 +713,5 @@ public class TextRun public Record[] getRecords(){ return _records; } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java index de78b6265..751753b22 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextShape.java @@ -133,6 +133,7 @@ public abstract class TextShape extends SimpleShape { _txtbox.appendChildRecord(sta); _txtrun = new TextRun(tha,tca,sta); + _txtrun._records = new Record[]{tha, tca, sta}; _txtrun.setText(""); _escherContainer.addChildRecord(_txtbox.getEscherRecord()); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java new file mode 100755 index 000000000..37dae8fec --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfo.java @@ -0,0 +1,97 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * A container record that specifies information about animation information for a shape. + * + * @author Yegor Kozlov + */ +public class AnimationInfo extends RecordContainer { + private byte[] _header; + + // Links to our more interesting children + private AnimationInfoAtom animationAtom; + + /** + * Set things up, and find our more interesting children + */ + protected AnimationInfo(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); + findInterestingChildren(); + } + + /** + * Go through our child records, picking out the ones that are + * interesting, and saving those for use by the easy helper + * methods. + */ + private void findInterestingChildren() { + + // First child should be the ExMediaAtom + if(_children[0] instanceof AnimationInfoAtom) { + animationAtom = (AnimationInfoAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a AnimationInfoAtom, was of type " + _children[0].getRecordType()); + } + } + + /** + * Create a new AnimationInfo, with blank fields + */ + public AnimationInfo() { + // Setup our header block + _header = new byte[8]; + _header[0] = 0x0f; // We are a container record + LittleEndian.putShort(_header, 2, (short)getRecordType()); + + _children = new Record[1]; + _children[0] = animationAtom = new AnimationInfoAtom(); + } + + /** + * We are of type 4103 + */ + public long getRecordType() { return RecordTypes.AnimationInfo.typeID; } + + /** + * 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],getRecordType(),_children,out); + } + + /** + * Returns the AnimationInfo + */ + public AnimationInfoAtom getAnimationInfoAtom() { + return animationAtom; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java new file mode 100755 index 000000000..66982ea20 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/AnimationInfoAtom.java @@ -0,0 +1,278 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.apache.poi.hslf.util.SystemTimeUtils; +import org.apache.poi.util.LittleEndian; + +/** + * An atom record that specifies the animation information for a shape. + * + * @author Yegor Kozlov + */ +public class AnimationInfoAtom extends RecordAtom +{ + + /** + * whether the animation plays in the reverse direction + */ + public static final int Reverse = 1; + /** + * whether the animation starts automatically + */ + public static final int Automatic = 4; + /** + * whether the animation has an associated sound + */ + public static final int Sound = 16; + /** + * whether all playing sounds are stopped when this animation begins + */ + public static final int StopSound = 64; + /** + * whether an associated sound, media or action verb is activated when the shape is clicked. + */ + public static final int Play = 256; + /** + * specifies that the animation, while playing, stops other slide show actions. + */ + public static final int Synchronous = 1024; + /** + * whether the shape is hidden while the animation is not playing + */ + public static final int Hide = 4096; + /** + * whether the background of the shape is animated + */ + public static final int AnimateBg = 16384; + + /** + * Record header. + */ + private byte[] _header; + + /** + * record data + */ + private byte[] _recdata; + + /** + * Constructs a brand new link related atom record. + */ + protected AnimationInfoAtom() { + _recdata = new byte[28]; + + _header = new byte[8]; + LittleEndian.putShort(_header, 0, (short)0x01); + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _recdata.length); + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected AnimationInfoAtom(byte[] source, int start, int len) { + // Get the header + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Grab the record data + _recdata = new byte[len-8]; + System.arraycopy(source,start+8,_recdata,0,len-8); + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { + return RecordTypes.AnimationInfoAtom.typeID; + } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_recdata); + } + + /** + * A rgb structure that specifies a color for the dim effect after the animation is complete. + * + * @return color for the dim effect after the animation is complete + */ + public int getDimColor(){ + return LittleEndian.getInt(_recdata, 0); + } + + /** + * A rgb structure that specifies a color for the dim effect after the animation is complete. + * + * @param rgb color for the dim effect after the animation is complete + */ + public void setDimColor(int rgb){ + LittleEndian.putInt(_recdata, 0, rgb); + } + + /** + * A bit mask specifying options for displaying headers and footers + * + * @return A bit mask specifying options for displaying headers and footers + */ + public int getMask(){ + return LittleEndian.getInt(_recdata, 4); + } + + /** + * A bit mask specifying options for displaying video + * + * @param mask A bit mask specifying options for displaying video + */ + public void setMask(int mask){ + LittleEndian.putInt(_recdata, 4, mask); + } + + /** + * @param bit the bit to check + * @return whether the specified flag is set + */ + public boolean getFlag(int bit){ + return (getMask() & bit) != 0; + } + + /** + * @param bit the bit to set + * @param value whether the specified bit is set + */ + public void setFlag(int bit, boolean value){ + int mask = getMask(); + if(value) mask |= bit; + else mask &= ~bit; + setMask(mask); + } + + /** + * A 4-byte unsigned integer that specifies a reference to a sound + * in the SoundCollectionContainer record to locate the embedded audio + * + * @return reference to a sound + */ + public int getSoundIdRef(){ + return LittleEndian.getInt(_recdata, 8); + } + + /** + * A 4-byte unsigned integer that specifies a reference to a sound + * in the SoundCollectionContainer record to locate the embedded audio + * + * @param id reference to a sound + */ + public void setSoundIdRef(int id){ + LittleEndian.putInt(_recdata, 8, id); + } + + /** + * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play. + * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored. + */ + public int getDelayTime(){ + return LittleEndian.getInt(_recdata, 12); + } + /** + * A signed integer that specifies the delay time, in milliseconds, before the animation starts to play. + * If {@link Automatic} is 0x1, this value MUST be greater than or equal to 0; otherwise, this field MUST be ignored. + */ + public void setDelayTime(int id){ + LittleEndian.putInt(_recdata, 12, id); + } + + /** + * A signed integer that specifies the order of the animation in the slide. + * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of + * the corresponding placeholder shape on the main master slide or title master slide. + * The value -1 SHOULD NOT <105> be used. + */ + public int getOrderID(){ + return LittleEndian.getInt(_recdata, 16); + } + + /** + * A signed integer that specifies the order of the animation in the slide. + * It MUST be greater than or equal to -2. The value -2 specifies that this animation follows the order of + * the corresponding placeholder shape on the main master slide or title master slide. + * The value -1 SHOULD NOT <105> be used. + */ + public void setOrderID(int id){ + LittleEndian.putInt(_recdata, 16, id); + } + + /** + * An unsigned integer that specifies the number of slides that this animation continues playing. + * This field is utilized only in conjunction with media. + * The value 0xFFFFFFFF specifies that the animation plays for one slide. + */ + public int getSlideCount(){ + return LittleEndian.getInt(_recdata, 18); + } + + /** + * An unsigned integer that specifies the number of slides that this animation continues playing. + * This field is utilized only in conjunction with media. + * The value 0xFFFFFFFF specifies that the animation plays for one slide. + */ + public void setSlideCount(int id){ + LittleEndian.putInt(_recdata, 18, id); + } + + public String toString(){ + StringBuffer buf = new StringBuffer(); + buf.append("AnimationInfoAtom\n"); + buf.append("\tDimColor: " + getDimColor() + "\n"); + int mask = getMask(); + buf.append("\tMask: " + mask + ", 0x"+Integer.toHexString(mask)+"\n"); + buf.append("\t Reverse: " + getFlag(Reverse)+"\n"); + buf.append("\t Automatic: " + getFlag(Automatic)+"\n"); + buf.append("\t Sound: " + getFlag(Sound)+"\n"); + buf.append("\t StopSound: " + getFlag(StopSound)+"\n"); + buf.append("\t Play: " + getFlag(Play)+"\n"); + buf.append("\t Synchronous: " + getFlag(Synchronous)+"\n"); + buf.append("\t Hide: " + getFlag(Hide)+"\n"); + buf.append("\t AnimateBg: " + getFlag(AnimateBg)+"\n"); + buf.append("\tSoundIdRef: " + getSoundIdRef() + "\n"); + buf.append("\tDelayTime: " + getDelayTime() + "\n"); + buf.append("\tOrderID: " + getOrderID() + "\n"); + buf.append("\tSlideCount: " + getSlideCount() + "\n"); + return buf.toString(); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java new file mode 100755 index 000000000..81ad98d9b --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExAviMovie.java @@ -0,0 +1,53 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * A container record that specifies information about a movie stored externally. + * + * @author Yegor Kozlov + */ +public class ExAviMovie extends ExMCIMovie { + + /** + * Set things up, and find our more interesting children + */ + protected ExAviMovie(byte[] source, int start, int len) { + super(source, start, len); + } + + /** + * Create a new ExAviMovie, with blank fields + */ + public ExAviMovie() { + super(); + + } + /** + * We are of type 4102 + */ + public long getRecordType() { + return RecordTypes.ExAviMovie.typeID; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java index e6e7da893..603cbad76 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExControl.java @@ -24,7 +24,7 @@ import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogger; /** - * Container for OLE Control object. It contains: + * A container record that specifies information about an ActiveX control. It contains: *

    * 1. ExControlAtom (4091) * 2. ExOleObjAtom (4035) @@ -40,9 +40,6 @@ import org.apache.poi.util.POILogger; */ public class ExControl extends ExEmbed { - // Links to our more interesting children - private ExControlAtom ctrlAtom; - /** * Set things up, and find our more interesting children * @@ -60,7 +57,7 @@ public class ExControl extends ExEmbed { public ExControl() { super(); - _children[0] = ctrlAtom = new ExControlAtom(); + _children[0] = embedAtom = new ExControlAtom(); } /** @@ -70,7 +67,7 @@ public class ExControl extends ExEmbed { */ public ExControlAtom getExControlAtom() { - return ctrlAtom; + return (ExControlAtom)_children[0]; } /** @@ -82,14 +79,4 @@ public class ExControl extends ExEmbed { public long getRecordType() { return RecordTypes.ExControl.typeID; } - - protected RecordAtom getEmbedAtom(Record[] children){ - RecordAtom atom = null; - if(_children[0] instanceof ExControlAtom) { - atom = (ExControlAtom)_children[0]; - } else { - logger.log(POILogger.ERROR, "First child record wasn't a ExControlAtom, was of type " + _children[0].getRecordType()); - } - return atom; - } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java index ae99d0a23..0204803ea 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExControlAtom.java @@ -23,8 +23,7 @@ import java.io.OutputStream; import org.apache.poi.util.LittleEndian; /** - * Contains a long integer, slideID, which stores the unique slide identifier of the slide - * where this control resides. + * An atom record that specifies an ActiveX control. * * @author Yegor Kozlov */ @@ -67,10 +66,32 @@ public class ExControlAtom extends RecordAtom { _id = LittleEndian.getInt(source, start + 8); } + /** + * An integer that specifies which presentation slide is associated with the ActiveX control. + *

    + * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record. + * The value 0x00000000 specifies a null reference. + *

    + * + * @return an integer that specifies which presentation slide is associated with the ActiveX control + */ public int getSlideId() { return _id; } + /** + * Sets which presentation slide is associated with the ActiveX control. + * + * @param id an integer that specifies which presentation slide is associated with the ActiveX control + *

    + * It MUST be 0x00000000 or equal to the value of the slideId field of a SlidePersistAtom record. + * The value 0x00000000 specifies a null reference. + *

    + */ + public void setSlideId(int id) { + _id = id; + } + /** * Gets the record type. * @return the record type. diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java index 6d61f2ef7..09e95abe4 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExEmbed.java @@ -36,7 +36,7 @@ public class ExEmbed extends RecordContainer { private byte[] _header; // Links to our more interesting children - private RecordAtom embedAtom; + protected RecordAtom embedAtom; private ExOleObjAtom oleObjAtom; private CString menuName; private CString progId; @@ -72,10 +72,11 @@ public class ExEmbed extends RecordContainer { // Setup our child records CString cs1 = new CString(); + cs1.setOptions(0x1 << 4); CString cs2 = new CString(); + cs2.setOptions(0x2 << 4); CString cs3 = new CString(); -// cs1.setOptions(0x00); -// cs2.setOptions(0x10); + cs3.setOptions(0x3 << 4); _children[0] = new ExEmbedAtom(); _children[1] = new ExOleObjAtom(); _children[2] = cs1; @@ -91,7 +92,11 @@ public class ExEmbed extends RecordContainer { private void findInterestingChildren() { // First child should be the ExHyperlinkAtom - embedAtom = getEmbedAtom(_children); + if(_children[0] instanceof ExEmbedAtom) { + embedAtom = (ExEmbedAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType()); + } // Second child should be the ExOleObjAtom if (_children[1] instanceof ExOleObjAtom) { @@ -102,25 +107,17 @@ public class ExEmbed extends RecordContainer { for (int i = 2; i < _children.length; i++) { if (_children[i] instanceof CString){ - if (menuName == null) menuName = (CString)_children[i]; - else if (progId == null) progId = (CString)_children[i]; - else if (clipboardName == null) clipboardName = (CString)_children[i]; - } else { - logger.log(POILogger.ERROR, "Record after atoms wasn't a CString, was of type " + _children[i].getRecordType()); + CString cs = (CString)_children[i]; + int opts = cs.getOptions() >> 4; + switch(opts){ + case 0x1: menuName = cs; break; + case 0x2: progId = cs; break; + case 0x3: clipboardName = cs; break; + } } } } - protected RecordAtom getEmbedAtom(Record[] children){ - RecordAtom atom = null; - if(_children[0] instanceof ExEmbedAtom) { - atom = (ExEmbedAtom)_children[0]; - } else { - logger.log(POILogger.ERROR, "First child record wasn't a ExEmbedAtom, was of type " + _children[0].getRecordType()); - } - return atom; - } - /** * Gets the {@link ExEmbedAtom}. * @@ -151,6 +148,11 @@ public class ExEmbed extends RecordContainer { return menuName == null ? null : menuName.getText(); } + public void setMenuName(String s) + { + if(menuName != null) menuName.setText(s); + } + /** * Gets the OLE Programmatic Identifier. * @@ -161,6 +163,10 @@ public class ExEmbed extends RecordContainer { return progId == null ? null : progId.getText(); } + public void setProgId(String s) + { + if(progId != null) progId.setText(s); + } /** * Gets the name that appears in the paste special dialog. * @@ -171,6 +177,10 @@ public class ExEmbed extends RecordContainer { return clipboardName == null ? null : clipboardName.getText(); } + public void setClipboardName(String s) + { + if(clipboardName != null) clipboardName.setText(s); + } /** * Returns the type (held as a little endian in bytes 3 and 4) * that this class handles. diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java new file mode 100755 index 000000000..c22609206 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExMCIMovie.java @@ -0,0 +1,100 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * A container record that specifies information about a movie stored externally. + * + * @author Yegor Kozlov + */ +public class ExMCIMovie extends RecordContainer { + private byte[] _header; + + //An ExVideoContainer record that specifies information about the MCI movie + private ExVideoContainer exVideo; + + /** + * Set things up, and find our more interesting children + */ + protected ExMCIMovie(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); + findInterestingChildren(); + } + + /** + * Create a new ExMCIMovie, with blank fields + */ + public ExMCIMovie() { + _header = new byte[8]; + // Setup our header block + _header[0] = 0x0f; // We are a container record + LittleEndian.putShort(_header, 2, (short) getRecordType()); + + exVideo = new ExVideoContainer(); + _children = new Record[]{exVideo}; + + } + + /** + * Go through our child records, picking out the ones that are + * interesting, and saving those for use by the easy helper + * methods. + */ + private void findInterestingChildren() { + + // First child should be the ExVideoContainer + if (_children[0] instanceof ExVideoContainer) { + exVideo = (ExVideoContainer) _children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExVideoContainer, was of type " + _children[0].getRecordType()); + } + } + + /** + * We are of type 4103 + */ + public long getRecordType() { + return RecordTypes.ExMCIMovie.typeID; + } + + /** + * 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], getRecordType(), _children, out); + } + + /** + * Returns the ExVideoContainer that specifies information about the MCI movie + */ + public ExVideoContainer getExVideo() { + return exVideo; } + + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java new file mode 100755 index 000000000..e80768f30 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExMediaAtom.java @@ -0,0 +1,172 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.apache.poi.hslf.util.SystemTimeUtils; +import org.apache.poi.util.LittleEndian; + +/** + * An atom record that specifies information about external audio or video data. + * + * @author Yegor Kozlov + */ +public class ExMediaAtom extends RecordAtom +{ + + /** + * A bit that specifies whether the audio or video data is repeated continuously during playback. + */ + public static final int fLoop = 1; + /** + * A bit that specifies whether the audio or video data is rewound after playing. + */ + public static final int fRewind = 2; + /** + * A bit that specifies whether the audio data is recorded narration for the slide show. It MUST be FALSE if this ExMediaAtom record is contained by an ExVideoContainer record. + */ + public static final int fNarration = 4; + + /** + * Record header. + */ + private byte[] _header; + + /** + * record data + */ + private byte[] _recdata; + + /** + * Constructs a brand new link related atom record. + */ + protected ExMediaAtom() { + _recdata = new byte[8]; + + _header = new byte[8]; + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _recdata.length); + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected ExMediaAtom(byte[] source, int start, int len) { + // Get the header + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Grab the record data + _recdata = new byte[len-8]; + System.arraycopy(source,start+8,_recdata,0,len-8); + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { return RecordTypes.ExMediaAtom.typeID; } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_recdata); + } + + /** + * A 4-byte unsigned integer that specifies an ID for an external object. + * + * @return A 4-byte unsigned integer that specifies an ID for an external object. + */ + public int getObjectId(){ + return LittleEndian.getInt(_recdata, 0); + } + + /** + * A 4-byte unsigned integer that specifies an ID for an external object. + * + * @param id A 4-byte unsigned integer that specifies an ID for an external object. + */ + public void setObjectId(int id){ + LittleEndian.putInt(_recdata, 0, id); + } + + /** + * A bit mask specifying options for displaying headers and footers + * + * @return A bit mask specifying options for displaying headers and footers + */ + public int getMask(){ + return LittleEndian.getInt(_recdata, 4); + } + + /** + * A bit mask specifying options for displaying video + * + * @param mask A bit mask specifying options for displaying video + */ + public void setMask(int mask){ + LittleEndian.putInt(_recdata, 4, mask); + } + + /** + * @param bit the bit to check + * @return whether the specified flag is set + */ + public boolean getFlag(int bit){ + return (getMask() & bit) != 0; + } + + /** + * @param bit the bit to set + * @param value whether the specified bit is set + */ + public void setFlag(int bit, boolean value){ + int mask = getMask(); + if(value) mask |= bit; + else mask &= ~bit; + setMask(mask); + } + + public String toString(){ + StringBuffer buf = new StringBuffer(); + buf.append("ExMediaAtom\n"); + buf.append("\tObjectId: " + getObjectId() + "\n"); + buf.append("\tMask : " + getMask() + "\n"); + buf.append("\t fLoop : " + getFlag(fLoop) + "\n"); + buf.append("\t fRewind : " + getFlag(fRewind) + "\n"); + buf.append("\t fNarration : " + getFlag(fNarration) + "\n"); + return buf.toString(); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java index ae96275cd..9f412d249 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjAtom.java @@ -65,11 +65,35 @@ import org.apache.poi.util.LittleEndian; */ public class ExOleObjAtom extends RecordAtom { + /** + * The object) is displayed as an embedded object inside of a container, + */ public static final int DRAW_ASPECT_VISIBLE = 1; + /** + * The object is displayed as a thumbnail image. + */ + public static final int DRAW_ASPECT_THUMBNAIL = 2; + /** + * The object is displayed as an icon. + */ public static final int DRAW_ASPECT_ICON = 4; + /** + * The object is displayed on the screen as though it were printed to a printer. + */ + public static final int DRAW_ASPECT_DOCPRINT = 8; + /** + * An embedded OLE object; the object is serialized and saved within the file. + */ public static final int TYPE_EMBEDDED = 0; + /** + * A linked OLE object; the object is saved outside of the file. + */ public static final int TYPE_LINKED = 1; + /** + * The OLE object is an ActiveX control. + */ + public static final int TYPE_CONTROL = 2; public static final int SUBTYPE_DEFAULT = 0; public static final int SUBTYPE_CLIPART_GALLERY = 1; @@ -101,14 +125,13 @@ public class ExOleObjAtom extends RecordAtom { /** * Constructs a brand new link related atom record. */ - protected ExOleObjAtom() { + public ExOleObjAtom() { _header = new byte[8]; - _data = new byte[18]; + _data = new byte[24]; + LittleEndian.putShort(_header, 0, (short)1); //MUST be 0x1 LittleEndian.putShort(_header, 2, (short)getRecordType()); LittleEndian.putInt(_header, 4, _data.length); - - // I hope it is fine for the other values to be zero. } /** @@ -144,6 +167,16 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 0); } + /** + * Sets whether the object can be completely seen, or if only the + * icon is visible. + * + * @param aspect the draw aspect, one of the {@code DRAW_ASPECT_*} constants. + */ + public void setDrawAspect(int aspect) { + LittleEndian.putInt(_data, 0, aspect); + } + /** * Gets whether the object is embedded or linked. * @@ -153,6 +186,15 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 4); } + /** + * Sets whether the object is embedded or linked. + * + * @param type the type, one of the {@code TYPE_EMBEDDED_*} constants. + */ + public void setType(int type) { + LittleEndian.putInt(_data, 4, type); + } + /** * Gets the unique identifier for the OLE object. * @@ -162,6 +204,15 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 8); } + /** + * Sets the unique identifier for the OLE object. + * + * @param id the object ID. + */ + public void setObjID(int id) { + LittleEndian.putInt(_data, 8, id); + } + /** * Gets the type of OLE object. * @@ -171,6 +222,15 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 12); } + /** + * Sets the type of OLE object. + * + * @param type the sub-type, one of the {@code SUBTYPE_*} constants. + */ + public void setSubType(int type) { + LittleEndian.putInt(_data, 12, type); + } + /** * Gets the reference to the persistent object * @@ -181,6 +241,16 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 16); } + /** + * Sets the reference to the persistent object + * + * @param ref the reference to the persistent object, corresponds with an + * {@code ExOleObjStg} storage container. + */ + public void setObjStgDataRef(int ref) { + LittleEndian.putInt(_data, 16, ref); + } + /** * Gets whether the object's image is blank. * @@ -191,6 +261,24 @@ public class ExOleObjAtom extends RecordAtom { return LittleEndian.getInt(_data, 20) != 0; } + /** + * Gets misc options (the last four bytes in the atom). + * + * @return {@code true} if the object's image is blank. + */ + public int getOptions() { + // Even though this is a mere boolean, KOffice's code says it's an int. + return LittleEndian.getInt(_data, 20); + } + + /** + * Sets misc options (the last four bytes in the atom). + */ + public void setOptions(int opts) { + // Even though this is a mere boolean, KOffice's code says it's an int. + LittleEndian.putInt(_data, 20, opts); + } + /** * Returns the type (held as a little endian in bytes 3 and 4) * that this class handles. @@ -210,4 +298,16 @@ public class ExOleObjAtom extends RecordAtom { out.write(_header); out.write(_data); } + + public String toString(){ + StringBuffer buf = new StringBuffer(); + buf.append("ExOleObjAtom\n"); + buf.append(" drawAspect: " + getDrawAspect() + "\n"); + buf.append(" type: " + getType() + "\n"); + buf.append(" objID: " + getObjID() + "\n"); + buf.append(" subType: " + getSubType() + "\n"); + buf.append(" objStgDataRef: " + getObjStgDataRef() + "\n"); + buf.append(" options: " + getOptions() + "\n"); + return buf.toString(); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java index c7408b0fe..36fbebf4c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExOleObjStg.java @@ -20,6 +20,7 @@ package org.apache.poi.hslf.record; import java.io.*; import java.util.zip.InflaterInputStream; import java.util.zip.DeflaterOutputStream; +import java.util.Hashtable; import org.apache.poi.util.LittleEndian; @@ -28,7 +29,7 @@ import org.apache.poi.util.LittleEndian; * * @author Daniel Noll */ -public class ExOleObjStg extends RecordAtom implements PersistRecord { +public class ExOleObjStg extends RecordAtom implements PositionDependentRecord, PersistRecord { private int _persistId; // Found from PersistPtrHolder @@ -45,10 +46,11 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord { /** * Constructs a new empty storage container. */ - protected ExOleObjStg() { + public ExOleObjStg() { _header = new byte[8]; _data = new byte[0]; + LittleEndian.putShort(_header, 0, (short)0x10); LittleEndian.putShort(_header, 2, (short)getRecordType()); LittleEndian.putInt(_header, 4, _data.length); } @@ -90,6 +92,10 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord { return new InflaterInputStream(compressedStream); } + public byte[] getRawData() { + return _data; + } + /** * Sets the embedded data. * @@ -144,4 +150,23 @@ public class ExOleObjStg extends RecordAtom implements PersistRecord { public void setPersistId(int id) { _persistId = id; } + + /** 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 setLastOnDiskOffset(int offset) { + myLastOnDiskOffset = offset; + } + + public void updateOtherRecordReferences(Hashtable oldToNewReferencesLookup) { + return; + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java new file mode 100755 index 000000000..36f9fda1a --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/ExVideoContainer.java @@ -0,0 +1,108 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one or more +* contributor license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright ownership. +* The ASF licenses this file to You under the Apache License, Version 2.0 +* (the "License"); you may not use this file except in compliance with +* the License. You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.util.LittleEndian; +import org.apache.poi.util.POILogger; + +/** + * A container record that specifies information about external video data. + * + * @author Yegor Kozlov + */ +public class ExVideoContainer extends RecordContainer { + private byte[] _header; + + // Links to our more interesting children + private ExMediaAtom mediaAtom; + //the UNC or local path to a video file. + private CString pathAtom; + + /** + * Set things up, and find our more interesting children + */ + protected ExVideoContainer(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); + findInterestingChildren(); + } + + /** + * Go through our child records, picking out the ones that are + * interesting, and saving those for use by the easy helper + * methods. + */ + private void findInterestingChildren() { + + // First child should be the ExMediaAtom + if(_children[0] instanceof ExMediaAtom) { + mediaAtom = (ExMediaAtom)_children[0]; + } else { + logger.log(POILogger.ERROR, "First child record wasn't a ExMediaAtom, was of type " + _children[0].getRecordType()); + } + if(_children[1] instanceof CString) { + pathAtom = (CString)_children[1]; + } else { + logger.log(POILogger.ERROR, "Second child record wasn't a CString, was of type " + _children[1].getRecordType()); + } + } + + /** + * Create a new ExVideoContainer, with blank fields + */ + public ExVideoContainer() { + // Setup our header block + _header = new byte[8]; + _header[0] = 0x0f; // We are a container record + LittleEndian.putShort(_header, 2, (short)getRecordType()); + + _children = new Record[2]; + _children[0] = mediaAtom = new ExMediaAtom(); + _children[1] = pathAtom = new CString(); + } + + /** + * We are of type 4103 + */ + public long getRecordType() { return RecordTypes.ExVideoContainer.typeID; } + + /** + * 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],getRecordType(),_children,out); + } + + /** + * Returns the ExMediaAtom of this link + */ + public ExMediaAtom getExMediaAtom() { return mediaAtom; } + + /** + * Returns the Path Atom (CString) of this link + */ + public CString getPathAtom() { return pathAtom; } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java index 8943f882d..0ee8cd883 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/InteractiveInfoAtom.java @@ -39,27 +39,39 @@ public class InteractiveInfoAtom extends RecordAtom /** * Action Table */ - public static final int ACTION_NONE = 0; - public static final int ACTION_MACRO = 1; - public static final int ACTION_RUNPROGRAM = 2; - public static final int ACTION_JUMP = 3; - public static final int ACTION_HYPERLINK = 4; - public static final int ACTION_OLE = 5; - public static final int ACTION_MEDIA = 6; - public static final int ACTION_CUSTOMSHOW = 7; + public static final byte ACTION_NONE = 0; + public static final byte ACTION_MACRO = 1; + public static final byte ACTION_RUNPROGRAM = 2; + public static final byte ACTION_JUMP = 3; + public static final byte ACTION_HYPERLINK = 4; + public static final byte ACTION_OLE = 5; + public static final byte ACTION_MEDIA = 6; + public static final byte ACTION_CUSTOMSHOW = 7; /** * Jump Table */ - public static final int JUMP_NONE = 0; - public static final int JUMP_NEXTSLIDE = 1; - public static final int JUMP_PREVIOUSSLIDE = 2; - public static final int JUMP_FIRSTSLIDE = 3; - public static final int JUMP_LASTSLIDE = 4; - public static final int JUMP_LASTSLIDEVIEWED = 5; - public static final int JUMP_ENDSHOW = 6; - + public static final byte JUMP_NONE = 0; + public static final byte JUMP_NEXTSLIDE = 1; + public static final byte JUMP_PREVIOUSSLIDE = 2; + public static final byte JUMP_FIRSTSLIDE = 3; + public static final byte JUMP_LASTSLIDE = 4; + public static final byte JUMP_LASTSLIDEVIEWED = 5; + public static final byte JUMP_ENDSHOW = 6; + /** + * Types of hyperlinks + */ + public static final byte LINK_NextSlide = 0x00; + public static final byte LINK_PreviousSlide = 0x01; + public static final byte LINK_FirstSlide = 0x02; + public static final byte LINK_LastSlide = 0x03; + public static final byte LINK_CustomShow = 0x06; + public static final byte LINK_SlideNumber = 0x07; + public static final byte LINK_Url = 0x08; + public static final byte LINK_OtherPresentation = 0x09; + public static final byte LINK_OtherFile = 0x0A; + public static final byte LINK_NULL = (byte)0xFF; /** * Record header. diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java new file mode 100755 index 000000000..4a319c2c4 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/OEShapeAtom.java @@ -0,0 +1,110 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; +import java.util.Date; + +import org.apache.poi.hslf.util.SystemTimeUtils; +import org.apache.poi.util.LittleEndian; + +/** + * Atom that contains information that describes shape client data. + * + * @author Yegor Kozlov + */ +public class OEShapeAtom extends RecordAtom +{ + + /** + * Record header. + */ + private byte[] _header; + + /** + * record data + */ + private byte[] _recdata; + + /** + * Constructs a brand new link related atom record. + */ + public OEShapeAtom() { + _recdata = new byte[4]; + + _header = new byte[8]; + LittleEndian.putShort(_header, 2, (short)getRecordType()); + LittleEndian.putInt(_header, 4, _recdata.length); + } + + /** + * Constructs the link related atom record from its + * source data. + * + * @param source the source data as a byte array. + * @param start the start offset into the byte array. + * @param len the length of the slice in the byte array. + */ + protected OEShapeAtom(byte[] source, int start, int len) { + // Get the header + _header = new byte[8]; + System.arraycopy(source,start,_header,0,8); + + // Grab the record data + _recdata = new byte[len-8]; + System.arraycopy(source,start+8,_recdata,0,len-8); + } + + /** + * Gets the record type. + * @return the record type. + */ + public long getRecordType() { return RecordTypes.OEShapeAtom.typeID; } + + /** + * Write the contents of the record back, so it can be written + * to disk + * + * @param out the output stream to write to. + * @throws java.io.IOException if an error occurs. + */ + public void writeOut(OutputStream out) throws IOException { + out.write(_header); + out.write(_recdata); + } + + /** + * shape flags. + * + * @return shape flags. + */ + public int getOptions(){ + return LittleEndian.getInt(_recdata, 0); + } + + /** + * shape flags. + * + * @param id shape flags. + */ + public void setOptions(int id){ + LittleEndian.putInt(_recdata, 0, id); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java index 9c867d5ab..758d21bda 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordTypes.java @@ -77,7 +77,7 @@ public class RecordTypes { public static final Type BookmarkSeedAtom = new Type(2025,null); public static final Type ColorSchemeAtom = new Type(2032,ColorSchemeAtom.class); public static final Type ExObjRefAtom = new Type(3009,null); - public static final Type OEShapeAtom = new Type(3009,null); + public static final Type OEShapeAtom = new Type(3009,OEShapeAtom.class); public static final Type OEPlaceholderAtom = new Type(3011,OEPlaceholderAtom.class); public static final Type GPopublicintAtom = new Type(3024,null); public static final Type GRatioAtom = new Type(3031,null); @@ -128,11 +128,11 @@ public class RecordTypes { public static final Type DateTimeMCAtom = new Type(4087,null); public static final Type GenericDateMCAtom = new Type(4088,null); public static final Type FooterMCAtom = new Type(4090,null); - public static final Type ExControlAtom = new Type(4091,null); - public static final Type ExMediaAtom = new Type(4100,null); - public static final Type ExVideo = new Type(4101,null); - public static final Type ExAviMovie = new Type(4102,null); - public static final Type ExMCIMovie = new Type(4103,null); + public static final Type ExControlAtom = new Type(4091,ExControlAtom.class); + public static final Type ExMediaAtom = new Type(4100,ExMediaAtom.class); + public static final Type ExVideoContainer = new Type(4101,ExVideoContainer.class); + public static final Type ExAviMovie = new Type(4102,ExAviMovie.class); + public static final Type ExMCIMovie = new Type(4103,ExMCIMovie.class); public static final Type ExMIDIAudio = new Type(4109,null); public static final Type ExCDAudio = new Type(4110,null); public static final Type ExWAVAudioEmbedded = new Type(4111,null); @@ -140,7 +140,8 @@ public class RecordTypes { public static final Type ExOleObjStg = new Type(4113,ExOleObjStg.class); public static final Type ExCDAudioAtom = new Type(4114,null); public static final Type ExWAVAudioEmbeddedAtom = new Type(4115,null); - public static final Type AnimationInfoAtom = new Type(4116,null); + public static final Type AnimationInfo = new Type(4116,AnimationInfo.class); + public static final Type AnimationInfoAtom = new Type(4081,AnimationInfoAtom.class); public static final Type RTFDateTimeMCAtom = new Type(4117,null); public static final Type ProgTags = new Type(5000,DummyPositionSensitiveRecordWithChildren.class); public static final Type ProgStringTag = new Type(5001,null); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java index 969e9036a..7b2304074 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java @@ -367,8 +367,14 @@ public class StyleTextPropAtom extends RecordAtom rawContents = baos.toByteArray(); } - - /** + + public void setRawContents(byte[] bytes) { + rawContents = bytes; + reserved = new byte[0]; + initialised = false; + } + + /** * Create a new Paragraph TextPropCollection, and add it to the list * @param charactersCovered The number of characters this TextPropCollection will cover * @return the new TextPropCollection, which will then be in the list diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java index 71ded8540..0be988d51 100755 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextRulerAtom.java @@ -53,7 +53,7 @@ public class TextRulerAtom extends RecordAtom { /** * Constructs a new empty ruler atom. */ - protected TextRulerAtom() { + public TextRulerAtom() { _header = new byte[8]; _data = new byte[0]; @@ -191,4 +191,19 @@ public class TextRulerAtom extends RecordAtom { public int[] getBulletOffsets(){ return bulletOffsets; } + + public static TextRulerAtom getParagraphInstance(){ + byte[] data = new byte[] { + 0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00, + 0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01 + }; + TextRulerAtom ruler = new TextRulerAtom(data, 0, data.length); + return ruler; + } + + public void setParagraphIndent(short tetxOffset, short bulletOffset){ + LittleEndian.putShort(_data, 4, tetxOffset); + LittleEndian.putShort(_data, 6, bulletOffset); + LittleEndian.putShort(_data, 8, bulletOffset); + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java index 6bc203b65..a4238b473 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/RichTextRun.java @@ -687,6 +687,7 @@ public class RichTextRun { */ public void setBulletFont(int idx) { setParaTextPropVal("bullet.font", idx); + setFlag(false, ParagraphFlagsTextProp.BULLET_HARDFONT_IDX, true); } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java index a546d0444..f480a30aa 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -24,11 +24,7 @@ import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.List; +import java.util.*; import org.apache.poi.ddf.EscherBSERecord; import org.apache.poi.ddf.EscherContainerRecord; @@ -38,21 +34,9 @@ import org.apache.poi.hslf.HSLFSlideShow; import org.apache.poi.hslf.exceptions.CorruptPowerPointFileException; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.model.*; -import org.apache.poi.hslf.record.Document; -import org.apache.poi.hslf.record.DocumentAtom; -import org.apache.poi.hslf.record.FontCollection; -import org.apache.poi.hslf.record.FontEntityAtom; -import org.apache.poi.hslf.record.HeadersFootersContainer; -import org.apache.poi.hslf.record.ParentAwareRecord; -import org.apache.poi.hslf.record.PersistPtrHolder; -import org.apache.poi.hslf.record.PositionDependentRecord; -import org.apache.poi.hslf.record.PositionDependentRecordContainer; -import org.apache.poi.hslf.record.Record; -import org.apache.poi.hslf.record.RecordContainer; -import org.apache.poi.hslf.record.RecordTypes; -import org.apache.poi.hslf.record.SlideListWithText; -import org.apache.poi.hslf.record.SlidePersistAtom; -import org.apache.poi.hslf.record.UserEditAtom; +import org.apache.poi.hslf.model.Notes; +import org.apache.poi.hslf.model.Slide; +import org.apache.poi.hslf.record.*; import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet; import org.apache.poi.util.ArrayUtil; import org.apache.poi.util.POILogFactory; @@ -590,9 +574,8 @@ public final class SlideShow { * Create a blank Slide. * * @return the created Slide - * @throws IOException */ - public Slide createSlide() throws IOException { + public Slide createSlide() { SlideListWithText slist = null; // We need to add the records to the SLWT that deals @@ -660,9 +643,13 @@ public final class SlideShow { for (int i = 0; i < _records.length; i++) { Record record = _records[i]; ByteArrayOutputStream out = new ByteArrayOutputStream(); - record.writeOut(out); + try { + record.writeOut(out); + } catch (IOException e){ + throw new HSLFException(e); + } - // Grab interesting records as they come past + // Grab interesting records as they come past if(_records[i].getRecordType() == RecordTypes.PersistPtrIncrementalBlock.typeID){ ptr = (PersistPtrHolder)_records[i]; } @@ -888,4 +875,72 @@ public final class SlideShow { } } + /** + * Add a movie in this presentation + * + * @param path the path or url to the movie + * @return 0-based index of the movie + */ + public int addMovie(String path, int type) { + ExObjList lst = (ExObjList)_documentRecord.findFirstOfType(RecordTypes.ExObjList.typeID); + if(lst == null){ + lst = new ExObjList(); + _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); + } + + ExObjListAtom objAtom = lst.getExObjListAtom(); + //increment the object ID seed + int objectId = (int)objAtom.getObjectIDSeed() + 1; + objAtom.setObjectIDSeed(objectId); + ExMCIMovie mci; + switch (type){ + case MovieShape.MOVIE_MPEG: + mci = new ExMCIMovie(); + break; + case MovieShape.MOVIE_AVI: + mci = new ExAviMovie(); + break; + default: + throw new IllegalArgumentException("Unsupported Movie: " + type); + } + + lst.appendChildRecord(mci); + ExVideoContainer exVideo = mci.getExVideo(); + exVideo.getExMediaAtom().setObjectId(objectId); + exVideo.getExMediaAtom().setMask(0xE80000); + exVideo.getPathAtom().setText(path); + return objectId; + } + + /** + * Add a control in this presentation + * + * @param name name of the control, e.g. "Shockwave Flash Object" + * @param progId OLE Programmatic Identifier, e.g. "ShockwaveFlash.ShockwaveFlash.9" + * @return 0-based index of the control + */ + public int addControl(String name, String progId) { + ExObjList lst = _documentRecord.getExObjList(); + if (lst == null) { + lst = new ExObjList(); + _documentRecord.addChildAfter(lst, _documentRecord.getDocumentAtom()); + } + ExObjListAtom objAtom = lst.getExObjListAtom(); + //increment the object ID seed + int objectId = (int) objAtom.getObjectIDSeed() + 1; + objAtom.setObjectIDSeed(objectId); + ExControl ctrl = new ExControl(); + ExOleObjAtom oleObj = ctrl.getExOleObjAtom(); + oleObj.setObjID(objectId); + oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE); + oleObj.setType(ExOleObjAtom.TYPE_CONTROL); + oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT); + + ctrl.setProgId(progId); + ctrl.setMenuName(name); + ctrl.setClipboardName(name); + lst.addChildAfter(ctrl, objAtom); + + return objectId; + } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java new file mode 100755 index 000000000..9e0bfaf77 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestMovieShape.java @@ -0,0 +1,66 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.hslf.model; + +import junit.framework.TestCase; + +import java.io.*; +import java.awt.*; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.HSLFSlideShow; + +/** + * Test MovieShape object. + * + * @author Yegor Kozlov + */ +public class TestMovieShape extends TestCase { + + protected String cwd = System.getProperty("HSLF.testdata.path"); + + public void testCreate() throws Exception { + SlideShow ppt = new SlideShow(); + + Slide slide = ppt.createSlide(); + + String path = cwd + "/test-movie.mpg"; + int movieIdx = ppt.addMovie(path, MovieShape.MOVIE_MPEG); + int thumbnailIdx = ppt.addPicture(new File(cwd, "tomcat.png"), Picture.PNG); + + MovieShape shape = new MovieShape(movieIdx, thumbnailIdx); + shape.setAnchor(new Rectangle2D.Float(300,225,120,90)); + slide.addShape(shape); + + assertEquals(path, shape.getPath()); + assertTrue(shape.isAutoPlay()); + shape.setAutoPlay(false); + assertFalse(shape.isAutoPlay()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ppt.write(out); + + ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray())); + slide = ppt.getSlides()[0]; + shape = (MovieShape)slide.getShapes()[0]; + assertEquals(path, shape.getPath()); + assertFalse(shape.isAutoPlay()); + + } + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java new file mode 100755 index 000000000..fff6482ec --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java @@ -0,0 +1,63 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ +package org.apache.poi.hslf.model; + +import junit.framework.TestCase; + +import java.io.*; +import java.awt.*; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.HSLFSlideShow; + +/** + * Test Table object. + * + * @author Yegor Kozlov + */ +public class TestTable extends TestCase { + + /** + * Test that ShapeFactory works properly and returns Table + */ + public void testShapeFactory() throws Exception { + SlideShow ppt = new SlideShow(); + + Slide slide = ppt.createSlide(); + + Table tbl = new Table(2, 5); + slide.addShape(tbl); + + assertTrue(slide.getShapes()[0] instanceof Table); + Table tbl2 = (Table)slide.getShapes()[0]; + assertEquals(tbl.getNumberOfColumns(), tbl2.getNumberOfColumns()); + assertEquals(tbl.getNumberOfRows(), tbl2.getNumberOfRows()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ppt.write(out); + out.close(); + + ppt = new SlideShow(new ByteArrayInputStream(out.toByteArray())); + slide = ppt.getSlides()[0]; + assertTrue(slide.getShapes()[0] instanceof Table); + Table tbl3 = (Table)slide.getShapes()[0]; + assertEquals(tbl.getNumberOfColumns(), tbl3.getNumberOfColumns()); + assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows()); + } + +} diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java new file mode 100755 index 000000000..3b8cf756d --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestAnimationInfoAtom.java @@ -0,0 +1,91 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +import org.apache.poi.util.HexDump; + +/** + * Tests that {@link HeadersFootersAtom} works properly + * + * @author Yegor Kozlov + */ +public class TestAnimationInfoAtom extends TestCase { + // From a real file + /* + + 00 00 00 07 04 05 00 00 00 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 00 + 00 00 00 + + */ + private byte[] data = new byte[] { + 0x01, 0x00, (byte)0xF1, 0x0F, 0x1C, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x04, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + public void testRead() throws Exception { + AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length); + assertEquals(RecordTypes.AnimationInfoAtom.typeID, record.getRecordType()); + assertTrue(record.getFlag(AnimationInfoAtom.Automatic)); + assertTrue(record.getFlag(AnimationInfoAtom.Play)); + assertTrue(record.getFlag(AnimationInfoAtom.Synchronous)); + assertFalse(record.getFlag(AnimationInfoAtom.Reverse)); + assertFalse(record.getFlag(AnimationInfoAtom.Sound)); + assertFalse(record.getFlag(AnimationInfoAtom.StopSound)); + assertFalse(record.getFlag(AnimationInfoAtom.Hide)); + assertFalse(record.getFlag(AnimationInfoAtom.AnimateBg)); + assertEquals(0x07000000, record.getDimColor()); + assertEquals(0, record.getSoundIdRef()); + assertEquals(0, record.getDelayTime()); + assertEquals(2, record.getOrderID()); + assertEquals(0, record.getSlideCount()); + } + + public void testWrite() throws Exception { + AnimationInfoAtom record = new AnimationInfoAtom(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + AnimationInfoAtom record = new AnimationInfoAtom(); + record.setDimColor(0x07000000); + record.setOrderID(2); + record.setFlag(AnimationInfoAtom.Automatic, true); + record.setFlag(AnimationInfoAtom.Play, true); + record.setFlag(AnimationInfoAtom.Synchronous, true); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java new file mode 100755 index 000000000..82255d973 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExControl.java @@ -0,0 +1,127 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +/** + * Tests that {@link org.apache.poi.hslf.record.ExControl} works properly + * + * @author Yegor Kozlov + */ +public class TestExControl extends TestCase { + + // From a real file (embedded SWF control) + /* + + + 00 01 00 00 + + + 01 00 00 00 02 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 96 13 00 + + + 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61 + 00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00 + + + 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00 6C 00 61 00 73 + 00 68 00 2E 00 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 46 00 + 6C 00 61 00 73 00 68 00 2E 00 39 00 + + + 53 00 68 00 6F 00 63 00 6B 00 77 00 61 00 76 00 65 00 20 00 46 00 6C 00 61 + 00 73 00 68 00 20 00 4F 00 62 00 6A 00 65 00 63 00 74 00 + + + */ + private byte[] data = new byte[] { + 0x0F, 0x00, (byte)0xEE, 0x0F, (byte)0xDA, 0x00, 0x00, 0x00, 0x00, 0x00, (byte)0xFB, 0x0F, 0x04, 0x00, 0x00, 0x00, + 0x00, 0x01, 0x00, 0x00, 0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, + 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00, + 0x10, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, + 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, + 0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00, (byte)0xBA, + 0x0F, 0x3E, 0x00, 0x00, 0x00, 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, + 0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x53, 0x00, 0x68, + 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x46, 0x00, 0x6C, 0x00, + 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x2E, 0x00, 0x39, 0x00, 0x30, 0x00, (byte)0xBA, 0x0F, 0x2C, 0x00, 0x00, 0x00, + 0x53, 0x00, 0x68, 0x00, 0x6F, 0x00, 0x63, 0x00, 0x6B, 0x00, 0x77, 0x00, 0x61, 0x00, 0x76, 0x00, 0x65, 0x00, 0x20, + 0x00, 0x46, 0x00, 0x6C, 0x00, 0x61, 0x00, 0x73, 0x00, 0x68, 0x00, 0x20, 0x00, 0x4F, 0x00, 0x62, 0x00, 0x6A, 0x00, + 0x65, 0x00, 0x63, 0x00, 0x74, 0x00 + }; + + public void testRead() throws Exception { + ExControl record = new ExControl(data, 0, data.length); + assertEquals(RecordTypes.ExControl.typeID, record.getRecordType()); + + assertNotNull(record.getExControlAtom()); + assertEquals(256, record.getExControlAtom().getSlideId()); + + ExOleObjAtom oleObj = record.getExOleObjAtom(); + assertNotNull(oleObj); + assertEquals(oleObj.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE); + assertEquals(oleObj.getType(), ExOleObjAtom.TYPE_CONTROL); + assertEquals(oleObj.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT); + + assertEquals("Shockwave Flash Object", record.getMenuName()); + assertEquals("ShockwaveFlash.ShockwaveFlash.9", record.getProgId()); + assertEquals("Shockwave Flash Object", record.getClipboardName()); + } + + public void testWrite() throws Exception { + ExControl record = new ExControl(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + ExControl record = new ExControl(); + ExControlAtom ctrl = record.getExControlAtom(); + ctrl.setSlideId(256); + + ExOleObjAtom oleObj = record.getExOleObjAtom(); + oleObj.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE); + oleObj.setType(ExOleObjAtom.TYPE_CONTROL); + oleObj.setObjID(1); + oleObj.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT); + oleObj.setObjStgDataRef(2); + oleObj.setOptions(1283584); + + record.setMenuName("Shockwave Flash Object"); + record.setProgId("ShockwaveFlash.ShockwaveFlash.9"); + record.setClipboardName("Shockwave Flash Object"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertEquals(data.length, b.length); + assertTrue(Arrays.equals(data, b)); + } + +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java new file mode 100755 index 000000000..6cda6a128 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExMediaAtom.java @@ -0,0 +1,94 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +/** + * Tests that {@link org.apache.poi.hslf.record.HeadersFootersAtom} works properly + * + * @author Yegor Kozlov + */ +public class TestExMediaAtom extends TestCase { + // From a real file + private byte[] data = new byte[] { + 0x00, 0x00, (byte)0x04, 0x10, 0x08, 0x00, 0x00, 00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + public void testRead() throws Exception { + ExMediaAtom record = new ExMediaAtom(data, 0, data.length); + assertEquals(RecordTypes.ExMediaAtom.typeID, record.getRecordType()); + + assertEquals(1, record.getObjectId()); + assertFalse(record.getFlag(ExMediaAtom.fLoop)); + assertFalse(record.getFlag(ExMediaAtom.fNarration)); + assertFalse(record.getFlag(ExMediaAtom.fRewind)); + } + + public void testWrite() throws Exception { + ExMediaAtom record = new ExMediaAtom(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + ExMediaAtom ref = new ExMediaAtom(data, 0, data.length); + System.out.println(ref.getMask()); + + ExMediaAtom record = new ExMediaAtom(); + record.setObjectId(1); + record.setFlag(HeadersFootersAtom.fHasDate, false); + record.setFlag(HeadersFootersAtom.fHasTodayDate, false); + record.setFlag(HeadersFootersAtom.fHasFooter, false); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testFlags() throws Exception { + ExMediaAtom record = new ExMediaAtom(); + + //in a new record all the bits are 0 + for(int i = 0; i < 3; i++) assertFalse(record.getFlag(1 << i)); + + record.setFlag(ExMediaAtom.fLoop, true); + assertTrue(record.getFlag(ExMediaAtom.fLoop)); + + record.setFlag(ExMediaAtom.fNarration, true); + assertTrue(record.getFlag(ExMediaAtom.fNarration)); + + record.setFlag(ExMediaAtom.fNarration, false); + assertFalse(record.getFlag(ExMediaAtom.fNarration)); + + record.setFlag(ExMediaAtom.fNarration, false); + assertFalse(record.getFlag(ExMediaAtom.fNarration)); + + } +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java new file mode 100755 index 000000000..ba539c271 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjAtom.java @@ -0,0 +1,78 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +/** + * Tests that {@link ExOleObjAtom} works properly + * + * @author Yegor Kozlov + */ +public class TestExOleObjAtom extends TestCase { + // From a real file (embedded SWF control) + private byte[] data = new byte[] { + 0x01, 0x00, (byte)0xC3, 0x0F, 0x18, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, (byte)0x96, 0x13, 0x00 }; + + public void testRead() throws Exception { + ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length); + assertEquals(RecordTypes.ExOleObjAtom.typeID, record.getRecordType()); + System.out.println(record); + + assertEquals(record.getDrawAspect(), ExOleObjAtom.DRAW_ASPECT_VISIBLE); + assertEquals(record.getType(), ExOleObjAtom.TYPE_CONTROL); + assertEquals(record.getObjID(), 1); + assertEquals(record.getSubType(), ExOleObjAtom.SUBTYPE_DEFAULT); + assertEquals(record.getObjStgDataRef(), 2); + assertEquals(record.getOptions(), 1283584); //ther meaning is unknown + } + + public void testWrite() throws Exception { + ExOleObjAtom record = new ExOleObjAtom(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + ExOleObjAtom record = new ExOleObjAtom(); + record.setDrawAspect(ExOleObjAtom.DRAW_ASPECT_VISIBLE); + record.setType(ExOleObjAtom.TYPE_CONTROL); + record.setObjID(1); + record.setSubType(ExOleObjAtom.SUBTYPE_DEFAULT); + record.setObjStgDataRef(2); + record.setOptions(1283584); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java new file mode 100755 index 000000000..4ce08c756 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExOleObjStg.java @@ -0,0 +1,144 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.io.FileOutputStream; +import java.io.InputStream; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.poi.poifs.filesystem.POIFSFileSystem; +import org.apache.poi.poifs.filesystem.DirectoryNode; +import org.apache.poi.poifs.filesystem.DocumentEntry; + +/** + * Tests that {@link ExOleObjStg} works properly + * + * @author Yegor Kozlov + */ +public class TestExOleObjStg extends TestCase { + + // From a real file (embedded SWF control) + /* + + 00 0E 00 00 78 9C BB 70 5E F0 C1 C2 8D 52 0F 19 D0 80 1D 03 33 C3 BF FF 9C + 0C 6C 48 62 8C 40 CC 04 E3 08 30 30 B0 40 C5 FE FD FF FF 1F 24 C4 0C C4 FF + 47 C1 90 02 41 0C F9 40 58 C2 A0 C0 E0 CA 90 07 A4 8B 18 2A D1 93 02 5E 20 + C6 C0 0A 8F 73 50 5A C8 BB 5D 73 29 77 DD 79 C1 69 3B 5C 5C 83 43 50 D5 06 + BC 48 2F 2B 66 38 C9 C8 0E 64 3B 30 42 C4 9C 81 B6 83 EC 4D 05 93 C5 24 D9 + 0D 02 42 0C 4C 8C C8 FE 21 56 9F 02 23 C9 56 E1 04 E4 D8 4F 4D 40 89 FD A0 + BC FB 17 4B BA F8 07 C5 A3 60 78 03 7A E6 FF 09 67 59 1B 41 F9 9F 95 61 34 + FF 53 13 50 62 3F 4C 1F AC 1C 18 CD F7 23 0B C0 DA 74 A0 B6 1B A8 3D 37 1A + F7 23 0B A4 87 A6 85 0A 00 1B 64 6F 38 21 98 03 DA C2 E7 60 90 01 92 69 0C + 39 0C 65 0C 05 40 32 11 58 2F A4 02 6B 07 3D 60 19 5D 0E 14 27 4E 05 1F 90 + 0C 67 C8 04 96 ED 29 C0 72 BE 1C C8 E3 06 E3 FF FF 39 18 B8 80 2C 0F A0 5C + 3A 43 06 58 2D A8 A7 E1 C3 10 02 97 87 B8 02 E6 1A 60 77 83 21 18 A8 12 64 + 8A 23 D0 B6 1C B8 59 C8 AA 90 F5 F0 62 94 75 DC C0 DE 0A 37 5C 1D 33 54 35 + 88 97 08 35 91 83 81 07 EC 27 10 BF 18 E8 9B E1 0F 00 BD 65 3D D4 + + */ + private byte[] data = new byte[] { + 0x10, 0x00, 0x11, 0x10, 0x5B, 0x01, 0x00, 0x00, 0x00, 0x0E, 0x00, 0x00, 0x78, (byte)0x9C, (byte)0xBB, 0x70, + 0x5E, (byte)0xF0, (byte)0xC1, (byte)0xC2, (byte)0x8D, 0x52, 0x0F, 0x19, (byte)0xD0, (byte)0x80, 0x1D, 0x03, + 0x33, (byte)0xC3, (byte)0xBF, (byte)0xFF, (byte)0x9C, 0x0C, 0x6C, 0x48, 0x62, (byte)0x8C, 0x40, (byte)0xCC, + 0x04, (byte)0xE3, 0x08, 0x30, 0x30, (byte)0xB0, 0x40, (byte)0xC5, (byte)0xFE, (byte)0xFD, (byte)0xFF, (byte)0xFF, + 0x1F, 0x24, (byte)0xC4, (byte)0x0C, (byte)0xC4, (byte)0xFF, 0x47, (byte)0xC1, (byte)0x90, 0x02, 0x41, 0x0C, + (byte)0xF9, 0x40, 0x58, (byte)0xC2, (byte)0xA0, (byte)0xC0, (byte)0xE0, (byte)0xCA, (byte)0x90, 0x07, (byte)0xA4, + (byte)0x8B, 0x18, 0x2A, (byte)0xD1, (byte)0x93, 0x02, 0x5E, 0x20, (byte)0xC6, (byte)0xC0, 0x0A, (byte)0x8F, + 0x73, 0x50, 0x5A, (byte)0xC8, (byte)0xBB, 0x5D, 0x73, 0x29, 0x77, (byte)0xDD, 0x79, (byte)0xC1, 0x69, 0x3B, + 0x5C, 0x5C, (byte)0x83, 0x43, 0x50, (byte)0xD5, 0x06, (byte)0xBC, 0x48, 0x2F, 0x2B, 0x66, 0x38, (byte)0xC9, + (byte)0xC8, 0x0E, 0x64, 0x3B, 0x30, 0x42, (byte)0xC4, (byte)0x9C, (byte)0x81, (byte)0xB6, (byte)0x83, (byte)0xEC, + 0x4D, 0x05, (byte)0x93, (byte)0xC5, 0x24, (byte)0xD9, 0x0D, 0x02, 0x42, 0x0C, 0x4C, (byte)0x8C, (byte)0xC8, + (byte)0xFE, 0x21, 0x56, (byte)0x9F, 0x02, 0x23, (byte)0xC9, 0x56, (byte)0xE1, 0x04, (byte)0xE4, (byte)0xD8, + 0x4F, 0x4D, 0x40, (byte)0x89, (byte)0xFD, (byte)0xA0, (byte)0xBC, (byte)0xFB, 0x17, 0x4B, (byte)0xBA, (byte)0xF8, + 0x07, (byte)0xC5, (byte)0xA3, 0x60, 0x78, 0x03, 0x7A, (byte)0xE6, (byte)0xFF, 0x09, 0x67, 0x59, 0x1B, 0x41, + (byte)0xF9, (byte)0x9F, (byte)0x95, 0x61, 0x34, (byte)0xFF, 0x53, 0x13, 0x50, 0x62, 0x3F, 0x4C, 0x1F, (byte)0xAC, + 0x1C, 0x18, (byte)0xCD, (byte)0xF7, 0x23, 0x0B, (byte)0xC0, (byte)0xDA, 0x74, (byte)0xA0, (byte)0xB6, 0x1B, + (byte)0xA8, 0x3D, 0x37, 0x1A, (byte)0xF7, 0x23, 0x0B, (byte)0xA4, (byte)0x87, (byte)0xA6, (byte)0x85, 0x0A, + 0x00, 0x1B, 0x64, 0x6F, 0x38, 0x21, (byte)0x98, 0x03, (byte)0xDA, (byte)0xC2, (byte)0xE7, 0x60, (byte)0x90, + 0x01, (byte)0x92, 0x69, 0x0C, 0x39, 0x0C, 0x65, 0x0C, 0x05, 0x40, 0x32, 0x11, 0x58, 0x2F, (byte)0xA4, 0x02, + 0x6B, 0x07, 0x3D, 0x60, 0x19, 0x5D, 0x0E, 0x14, 0x27, 0x4E, 0x05, 0x1F, (byte)0x90, 0x0C, 0x67, (byte)0xC8, + 0x04, (byte)0x96, (byte)0xED, 0x29, (byte)0xC0, 0x72, (byte)0xBE, 0x1C, (byte)0xC8, (byte)0xE3, 0x06, (byte)0xE3, + (byte)0xFF, (byte)0xFF, 0x39, 0x18, (byte)0xB8, (byte)0x80, 0x2C, 0x0F, (byte)0xA0, 0x5C, 0x3A, 0x43, 0x06, 0x58, + 0x2D, (byte)0xA8, (byte)0xA7, (byte)0xE1, (byte)0xC3, 0x10, 0x02, (byte)0x97, (byte)0x87, (byte)0xB8, 0x02, + (byte)0xE6, 0x1A, 0x60, 0x77, (byte)0x83, 0x21, 0x18, (byte)0xA8, 0x12, 0x64, (byte)0x8A, 0x23, (byte)0xD0, + (byte)0xB6, 0x1C, (byte)0xB8, 0x59, (byte)0xC8, (byte)0xAA, (byte)0x90, (byte)0xF5, (byte)0xF0, 0x62, (byte)0x94, + 0x75, (byte)0xDC, (byte)0xC0, (byte)0xDE, 0x0A, 0x37, 0x5C, 0x1D, 0x33, 0x54, 0x35, (byte)0x88, (byte)0x97, 0x08, + 0x35, (byte)0x91, (byte)0x83, (byte)0x81, 0x07, (byte)0xEC, 0x27, 0x10, (byte)0xBF, 0x18, (byte)0xE8, (byte)0x9B, + (byte)0xE1, 0x0F, 0x00, (byte)0xBD, 0x65, 0x3D, (byte)0xD4 + }; + + public void testRead() throws Exception { + ExOleObjStg record = new ExOleObjStg(data, 0, data.length); + assertEquals(RecordTypes.ExOleObjStg.typeID, record.getRecordType()); + + int len = record.getDataLength(); + byte[] oledata = readAll(record.getData()); + assertEquals(len, oledata.length); + + POIFSFileSystem fs = new POIFSFileSystem(record.getData()); + assertTrue("Constructed POIFS from ExOleObjStg data", true); + DocumentEntry doc = (DocumentEntry)fs.getRoot().getEntry("Contents"); + assertNotNull(doc); + assertTrue("Fetched the Contents stream containing OLE properties", true); + } + + public void testWrite() throws Exception { + ExOleObjStg record = new ExOleObjStg(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + ExOleObjStg src = new ExOleObjStg(data, 0, data.length); + byte[] oledata = readAll(src.getData()); + + ExOleObjStg tgt = new ExOleObjStg(); + tgt.setData(oledata); + + + assertEquals(src.getDataLength(), tgt.getDataLength()); + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + tgt.writeOut(out); + byte[] b = out.toByteArray(); + + assertEquals(data.length, b.length); + assertTrue(Arrays.equals(data, b)); + } + + private byte[] readAll(InputStream is) throws IOException { + int pos; + byte[] chunk = new byte[1024]; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + while((pos = is.read(chunk)) > 0){ + out.write(chunk, 0, pos); + } + return out.toByteArray(); + + } +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java new file mode 100755 index 000000000..224aedf61 --- /dev/null +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestExVideoContainer.java @@ -0,0 +1,90 @@ + +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + + + +package org.apache.poi.hslf.record; + + +import junit.framework.TestCase; +import java.io.ByteArrayOutputStream; +import java.util.Arrays; + +/** + * Tests that {@link HeadersFootersAtom} works properly + * + * @author Yegor Kozlov + */ +public class TestExVideoContainer extends TestCase { + + // From a real file + private byte[] data = new byte[]{ + 0x0F, 0x00, 0x05, 0x10, (byte) 0x9E, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x10, 0x08, 0x00, 0x00, 0x00, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, (byte)0xBA, 0x0F, (byte)0x86, 0x00, 0x00, 0x00, + 0x44, 0x00, 0x3A, 0x00, 0x5C, 0x00, 0x70, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x6A, 0x00, 0x65, 0x00, + 0x63, 0x00, 0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x53, 0x00, 0x63, 0x00, 0x68, 0x00, 0x75, 0x00, + 0x6C, 0x00, 0x65, 0x00, 0x72, 0x00, 0x41, 0x00, 0x47, 0x00, 0x5C, 0x00, 0x6D, 0x00, 0x63, 0x00, + 0x6F, 0x00, 0x6D, 0x00, 0x5F, 0x00, 0x76, 0x00, 0x5F, 0x00, 0x31, 0x00, 0x5F, 0x00, 0x30, 0x00, + 0x5F, 0x00, 0x34, 0x00, 0x5C, 0x00, 0x76, 0x00, 0x69, 0x00, 0x65, 0x00, 0x77, 0x00, 0x5C, 0x00, + 0x64, 0x00, 0x61, 0x00, 0x74, 0x00, 0x61, 0x00, 0x5C, 0x00, 0x74, 0x00, 0x65, 0x00, 0x73, 0x00, + 0x74, 0x00, 0x73, 0x00, 0x5C, 0x00, 0x69, 0x00, 0x6D, 0x00, 0x61, 0x00, 0x67, 0x00, 0x65, 0x00, + 0x73, 0x00, 0x5C, 0x00, 0x63, 0x00, 0x61, 0x00, 0x72, 0x00, 0x64, 0x00, 0x73, 0x00, 0x2E, 0x00, + 0x6D, 0x00, 0x70, 0x00, 0x67, 0x00}; + + + + + public void testRead() throws Exception { + ExVideoContainer record = new ExVideoContainer(data, 0, data.length); + assertEquals(RecordTypes.ExVideoContainer.typeID, record.getRecordType()); + + ExMediaAtom exMedia = record.getExMediaAtom(); + assertEquals(1, exMedia.getObjectId()); + assertNotNull(exMedia); + assertFalse(exMedia.getFlag(ExMediaAtom.fLoop)); + assertFalse(exMedia.getFlag(ExMediaAtom.fNarration)); + assertFalse(exMedia.getFlag(ExMediaAtom.fRewind)); + + CString path = record.getPathAtom(); + assertNotNull(exMedia); + assertEquals("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg", path.getText()); + } + + public void testWrite() throws Exception { + ExVideoContainer record = new ExVideoContainer(data, 0, data.length); + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } + + public void testNewRecord() throws Exception { + ExVideoContainer record = new ExVideoContainer(); + record.getExMediaAtom().setObjectId(1); + record.getPathAtom().setText("D:\\projects\\SchulerAG\\mcom_v_1_0_4\\view\\data\\tests\\images\\cards.mpg"); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + record.writeOut(baos); + byte[] b = baos.toByteArray(); + + assertTrue(Arrays.equals(data, b)); + } +} \ No newline at end of file diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java index 0b610cb64..3579ace59 100755 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestTextRulerAtom.java @@ -48,6 +48,10 @@ public class TestTextRulerAtom extends TestCase { 0x03, 0x69, 0x04, (byte)0xF6, 0x05, (byte)0xF6, 0x05 }; + private byte[] data_2 = new byte[] { + 0x00, 0x00, (byte)0xA6, 0x0F, 0x0A, 0x00, 0x00, 0x00, + 0x10, 0x03, 0x00, 0x00, (byte)0xF9, 0x00, 0x41, 0x01, 0x41, 0x01 + }; public void testReadRuler() throws Exception { TextRulerAtom ruler = new TextRulerAtom(data_1, 0, data_1.length); @@ -73,4 +77,16 @@ public class TestTextRulerAtom extends TestCase { byte[] result = out.toByteArray(); assertTrue(Arrays.equals(result, data_1)); } + + public void testRead2() throws Exception { + TextRulerAtom ruler = TextRulerAtom.getParagraphInstance(); + ruler.setParagraphIndent((short)249, (short)321); + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ruler.writeOut(out); + + byte[] result = out.toByteArray(); + assertTrue(Arrays.equals(result, data_2)); + + } + }