From 1bb312f416d9ad86452f2d167c417021b0a594c9 Mon Sep 17 00:00:00 2001 From: Nick Burch Date: Wed, 12 Apr 2006 18:48:53 +0000 Subject: [PATCH] Updates from Yegor: New shape and picture stuff (see bug 39256) git-svn-id: https://svn.apache.org/repos/asf/jakarta/poi/trunk@393576 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/hslf/model/AutoShape.java | 65 +++ .../src/org/apache/poi/hslf/model/Line.java | 3 +- .../apache/poi/hslf/model/PPGraphics2D.java | 30 +- .../org/apache/poi/hslf/model/Picture.java | 56 +- .../org/apache/poi/hslf/model/Rectangle.java | 16 +- .../src/org/apache/poi/hslf/model/Shape.java | 106 +++- .../apache/poi/hslf/model/ShapeFactory.java | 10 +- .../org/apache/poi/hslf/model/ShapeGroup.java | 10 + .../org/apache/poi/hslf/model/ShapeTypes.java | 10 +- .../src/org/apache/poi/hslf/model/Sheet.java | 25 +- .../apache/poi/hslf/model/SimpleShape.java | 15 +- .../org/apache/poi/hslf/model/TextBox.java | 490 ++++++++++++++++++ .../poi/hslf/record/EscherTextboxWrapper.java | 13 +- .../org/apache/poi/hslf/record/PPDrawing.java | 11 + .../apache/poi/hslf/record/TextBytesAtom.java | 12 + .../poi/hslf/record/TextHeaderAtom.java | 12 + .../poi/hslf/usermodel/PictureData.java | 24 +- .../apache/poi/hslf/usermodel/SlideShow.java | 26 +- .../org/apache/poi/hslf/model/TestShapes.java | 108 +++- .../poi/hslf/usermodel/TestPictures.java | 31 +- 20 files changed, 997 insertions(+), 76 deletions(-) create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java create mode 100644 src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java new file mode 100644 index 000000000..e546217bb --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/AutoShape.java @@ -0,0 +1,65 @@ +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; + +import java.awt.*; + +/** + * Represents a autoshape in a PowerPoint drawing + * + * @author Yegor Kozlov + */ +public class AutoShape extends SimpleShape { + + protected AutoShape(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + } + + public AutoShape(int type, Shape parent){ + super(null, parent); + _escherContainer = createSpContainer(type, parent instanceof ShapeGroup); + } + + public AutoShape(int type){ + this(type, null); + } + + protected EscherContainerRecord createSpContainer(int shapeType, boolean isChild){ + EscherContainerRecord spcont = super.createSpContainer(isChild); + + EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); + short type = (short)((shapeType << 4) | 0x2); + spRecord.setOptions(type); + + //set default properties for a line + EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); + + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__FILLCOLOR, 134217732)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__FILLBACKCOLOR, 134217728)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.FILL__NOFILLHITTEST, 1048592)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__COLOR, 134217729)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.LINESTYLE__NOLINEDRAWDASH, 524296)); + opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.SHADOWSTYLE__COLOR, 134217730)); + + opt.sortProperties(); + + return spcont; + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java index d6ea230cd..a94130ed3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java @@ -107,13 +107,12 @@ public class Line extends SimpleShape { EscherContainerRecord spcont = super.createSpContainer(isChild); EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); - short type = (ShapeTypes.Line << 4) + 2; + short type = (ShapeTypes.Line << 4) | 0x2; spRecord.setOptions(type); //set default properties for a line EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); - //opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 4)); opt.sortProperties(); return spcont; diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java index f84d968d2..80f97416c 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/PPGraphics2D.java @@ -139,6 +139,34 @@ public class PPGraphics2D extends Graphics2D { } public void drawString(String string, float x, float y){ + + TextBox txt = new TextBox(group); + txt.setMarginBottom(0); + txt.setMarginTop(0); + txt.setMarginLeft(0); + txt.setMarginRight(0); + txt.setText(string); + txt.setWordWrap(TextBox.WrapNone); + + if (font != null){ + txt.setFontSize(font.getSize()); + txt.setFontName(font.getName()); + //if(getColor() != null) txt.setFontColor(getColor()); + if (font.isBold()) txt.setBold(true); + if (font.isItalic()) txt.setItalic(true); + } + + txt.resizeToFitText(); + int height = (int)txt.getAnchor().getHeight(); + + /* + In powerpoint anchor of a shape is its top left corner. + Java graphics sets string coordinates by the baseline of the first character + so we need to shift down by the height of the textbox + */ + txt.moveTo((int)x, (int)(y - height)); + + group.addShape(txt); } public void fill(Shape shape){ @@ -212,7 +240,7 @@ public class PPGraphics2D extends Graphics2D { } public void drawOval(int x, int y, int width, int height) { - Ellipse ellipse = new Ellipse(); + AutoShape ellipse = new AutoShape(ShapeTypes.Ellipse); ellipse.setAnchor(new java.awt.Rectangle(x-width/2, y-height/2, width, height)); if (stroke instanceof BasicStroke){ BasicStroke bs = (BasicStroke)stroke; diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java index 5e74988ae..28e8e3f72 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Picture.java @@ -3,11 +3,14 @@ package org.apache.poi.hslf.model; import org.apache.poi.ddf.*; import org.apache.poi.hslf.usermodel.PictureData; import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.record.Document; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; import java.io.IOException; +import java.util.List; +import java.util.Arrays; /** @@ -122,16 +125,49 @@ public class Picture extends SimpleShape { } /** - * Set default size of the picture - * - * @param ppt presentation which holds the picture + * Resize this picture to the default size. */ - public void setDefaultSize(SlideShow ppt) throws IOException { - int idx = getPictureIndex(); - - PictureData pict = ppt.getPictures()[idx-1]; - BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData())); - - setAnchor(new java.awt.Rectangle(0, 0, img.getWidth()*6, img.getHeight()*6)); + public void setDefaultSize(){ + PictureData pict = getPictureData(); + try { + BufferedImage img = ImageIO.read(new ByteArrayInputStream(pict.getData())); + setAnchor(new java.awt.Rectangle(0, 0, img.getWidth(), img.getHeight())); + } catch (IOException e){ + throw new RuntimeException(e); + } } + + /** + * Returns the picture data for this picture. + * + * @return the picture data for this picture. + */ + public PictureData getPictureData(){ + SlideShow ppt = getSheet().getSlideShow(); + PictureData[] pict = ppt.getPictureData(); + Document doc = ppt.getDocumentRecord(); + EscherContainerRecord dggContainer = doc.getPPDrawingGroup().getDggContainer(); + EscherContainerRecord bstore = (EscherContainerRecord)Shape.getEscherChild(dggContainer, EscherContainerRecord.BSTORE_CONTAINER); + + List lst = bstore.getChildRecords(); + int idx = getPictureIndex()-1; + EscherBSERecord bse = (EscherBSERecord)lst.get(idx); + for ( int i = 0; i < pict.length; i++ ) { + if (Arrays.equals(bse.getUid(), pict[i].getUID())){ + return pict[i]; + } + } + return null; + } + + /** + * By default set the orininal image size + */ + protected void afterInsert(Sheet sh){ + java.awt.Rectangle anchor = getAnchor(); + if (anchor.equals(new java.awt.Rectangle())){ + setDefaultSize(); + } + } + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java index 2c5d69d22..1d69c47a4 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Rectangle.java @@ -21,39 +21,31 @@ import org.apache.poi.ddf.*; import java.awt.*; /** - * Represents a line in a PowerPoint drawing + * Represents a rectangle shae in a PowerPoint drawing * * @author Yegor Kozlov */ -public class Rectangle extends SimpleShape { +public class Rectangle extends TextBox { protected Rectangle(EscherContainerRecord escherRecord, Shape parent){ super(escherRecord, parent); } public Rectangle(Shape parent){ - super(null, parent); - _escherContainer = createSpContainer(parent instanceof ShapeGroup); + super(parent); } public Rectangle(){ - this(null); + super(); } protected EscherContainerRecord createSpContainer(boolean isChild){ EscherContainerRecord spcont = super.createSpContainer(isChild); - spcont.setOptions((short)15); EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); short type = (ShapeTypes.Rectangle << 4) + 2; spRecord.setOptions(type); - //set default properties for a rectangle - EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); - - opt.addEscherProperty(new EscherSimpleProperty(EscherProperties.GEOMETRY__SHAPEPATH, 4)); - opt.sortProperties(); - return spcont; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java index aa1b200e6..666568bfd 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Shape.java @@ -22,13 +22,47 @@ import java.awt.*; import java.util.Iterator; /** + *

* Represents a Shape which is the elemental object that composes a drawing. + * This class is a wrapper around EscherSpContainer which holds all information + * about a shape in PowerPoint document. + *

+ *

+ * When you add a shape, you usually specify the dimensions of the shape and the position + * of the upper�left corner of the bounding box for the shape relative to the upper�left + * corner of the page, worksheet, or slide. Distances in the drawing layer are measured + * in points (72 points = 1 inch). + *

+ *

* * @author Yegor Kozlov */ public abstract class Shape { + /** + * In Escher absolute distances are specified in + * English Metric Units (EMUs), occasionally referred to as A units; + * there are 360000 EMUs per centimeter, 914400 EMUs per inch, 12700 EMUs per point. + */ + public static final int EMU_PER_INCH = 914400; public static final int EMU_PER_POINT = 12700; + public static final int EMU_PER_CENTIMETER = 360000; + + /** + * Master DPI (576 pixels per inch). + * Used by the reference coordinate system in PowerPoint. + */ + public static final int MASTER_DPI = 576; + + /** + * Pixels DPI (96 pixels per inch) + */ + public static final int PIXEL_DPI = 96; + + /** + * Points DPI (72 pixels per inch) + */ + public static final int POINT_DPI = 72; /** * Either EscherSpContainer or EscheSpgrContainer record @@ -42,6 +76,11 @@ public abstract class Shape { */ protected Shape _parent; + /** + * The Sheet this shape belongs to + */ + protected Sheet _sheet; + /** * Create a Shape object. This constructor is used when an existing Shape is read from from a PowerPoint document. * @@ -86,25 +125,25 @@ public abstract class Shape { if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); - anchor.x = rec.getDx1(); - anchor.y = rec.getDy1(); - anchor.width = rec.getDx2() - anchor.x; - anchor.height = rec.getDy2() - anchor.y; + anchor.x = rec.getDx1()*POINT_DPI/MASTER_DPI; + anchor.y = rec.getDy1()*POINT_DPI/MASTER_DPI; + anchor.width = (rec.getDx2() - anchor.x)*POINT_DPI/MASTER_DPI; + anchor.height = (rec.getDy2() - anchor.y)*POINT_DPI/MASTER_DPI; } else { EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); anchor = new java.awt.Rectangle(); - anchor.y = rec.getFlag(); - anchor.x = rec.getCol1(); - anchor.width = rec.getDx1() - anchor.x; - anchor.height = rec.getRow1() - anchor.y; + anchor.y = rec.getFlag()*POINT_DPI/MASTER_DPI; + anchor.x = rec.getCol1()*POINT_DPI/MASTER_DPI; + anchor.width = (rec.getDx1() - rec.getCol1())*POINT_DPI/MASTER_DPI; + anchor.height = (rec.getRow1() - rec.getFlag())*POINT_DPI/MASTER_DPI; } return anchor; } /** * Sets the anchor (the bounding box rectangle) of this shape. - * All coordinates should be expressed in Master units (576 dpi). + * All coordinates should be expressed in poitns (72 dpi). * * @param anchor new anchor */ @@ -113,17 +152,17 @@ public abstract class Shape { int flags = spRecord.getFlags(); if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = (EscherChildAnchorRecord)getEscherChild(_escherContainer, EscherChildAnchorRecord.RECORD_ID); - rec.setDx1(anchor.x); - rec.setDy1(anchor.y); - rec.setDx2(anchor.width + anchor.x); - rec.setDy2(anchor.height + anchor.y); + rec.setDx1(anchor.x*MASTER_DPI/POINT_DPI); + rec.setDy1(anchor.y*MASTER_DPI/POINT_DPI); + rec.setDx2((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI); + rec.setDy2((anchor.height + anchor.y)*MASTER_DPI/POINT_DPI); } else { EscherClientAnchorRecord rec = (EscherClientAnchorRecord)getEscherChild(_escherContainer, EscherClientAnchorRecord.RECORD_ID); - rec.setFlag((short)anchor.y); - rec.setCol1((short)anchor.x); - rec.setDx1((short)(anchor.width + anchor.x)); - rec.setRow1((short)(anchor.height + anchor.y)); + rec.setFlag((short)(anchor.y*MASTER_DPI/POINT_DPI)); + rec.setCol1((short)(anchor.x*MASTER_DPI/POINT_DPI)); + rec.setDx1((short)((anchor.width + anchor.x)*MASTER_DPI/POINT_DPI)); + rec.setRow1((short)((anchor.height + anchor.y)*MASTER_DPI/POINT_DPI)); } } @@ -171,11 +210,11 @@ public abstract class Shape { } /** - * Set an escher property in the opt record. + * Set an escher property for this shape. * * @param opt The opt record to set the properties to. * @param propId The id of the property. One of the constants defined in EscherOptRecord. - * @param value value of the property + * @param value value of the property. If value = -1 then the property is removed. */ public static void setEscherProperty(EscherOptRecord opt, short propId, int value){ java.util.List props = opt.getEscherProperties(); @@ -198,4 +237,33 @@ public abstract class Shape { public EscherContainerRecord getSpContainer(){ return _escherContainer; } + + /** + * Event which fires when a shape is inserted in the sheet. + * In some cases we need to propagate changes to upper level containers. + *
+ * Default implementation does nothing. + * + * @param sh - owning shape + */ + protected void afterInsert(Sheet sh){ + + } + + /** + * @return the SlideShow this shape belongs to + */ + public Sheet getSheet(){ + return _sheet; + } + + /** + * Assign the SlideShow this shape belongs to + * + * @param sheet owner of this shape + */ + public void setSheet(Sheet sheet){ + _sheet = sheet; + } + } 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 c886638c3..5c7f1c89a 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeFactory.java @@ -25,6 +25,9 @@ import org.apache.poi.ddf.EscherContainerRecord; */ public class ShapeFactory { + /** + * Create a new shape from the data provided. + */ public static Shape createShape(EscherContainerRecord spContainer, Shape parent){ if (spContainer.getRecordId() == EscherContainerRecord.SPGR_CONTAINER){ return new ShapeGroup(spContainer, parent); @@ -36,6 +39,8 @@ public class ShapeFactory { int type = spRecord.getOptions() >> 4; switch (type){ case ShapeTypes.TextBox: + shape = new TextBox(spContainer, parent); + break; case ShapeTypes.Rectangle: shape = new Rectangle(spContainer, parent); break; @@ -45,14 +50,11 @@ public class ShapeFactory { case ShapeTypes.Line: shape = new Line(spContainer, parent); break; - case ShapeTypes.Ellipse: - shape = new Ellipse(spContainer, parent); - break; case ShapeTypes.NotPrimitive: shape = new ShapeGroup(spContainer, parent); break; default: - shape = new SimpleShape(spContainer, parent); + shape = new AutoShape(spContainer, parent); break; } return shape; diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java index 0d2d61e95..a3f6584b2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeGroup.java @@ -27,11 +27,21 @@ import java.util.List; */ public class ShapeGroup extends Shape{ + /** + * Create a new ShapeGroup. This constructor is used when a new shape is created. + * + */ public ShapeGroup(){ this(null, null); _escherContainer = createSpContainer(false); } + /** + * Create a ShapeGroup object and initilize it from the supplied Record container. + * + * @param escherRecord EscherSpContainer container which holds information about this shape + * @param parent the parent of the shape + */ protected ShapeGroup(EscherContainerRecord escherRecord, Shape parent){ super(escherRecord, parent); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java index fd571c181..30966d586 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/ShapeTypes.java @@ -82,9 +82,9 @@ public class ShapeTypes { public static final int Chevron = 55; public static final int Pentagon = 56; public static final int NoSmoking = 57; - public static final int Seal8 = 58; - public static final int Seal16 = 59; - public static final int Seal32 = 60; + public static final int Star8 = 58; + public static final int Star16 = 59; + public static final int Star32 = 60; public static final int WedgeRectCallout = 61; public static final int WedgeRRectCallout = 62; public static final int WedgeEllipseCallout = 63; @@ -116,7 +116,7 @@ public class ShapeTypes { public static final int LeftUpArrow = 89; public static final int BentUpArrow = 90; public static final int BentArrow = 91; - public static final int Seal24 = 92; + public static final int Star24 = 92; public static final int StripedRightArrow = 93; public static final int NotchedRightArrow = 94; public static final int BlockArc = 95; @@ -211,7 +211,7 @@ public class ShapeTypes { public static final int Moon = 184; public static final int BracketPair = 185; public static final int BracePair = 186; - public static final int Seal4 = 187; + public static final int Star4 = 187; public static final int DoubleWave = 188; public static final int ActionButtonBlank = 189; public static final int ActionButtonHome = 190; diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java index e1c95d747..35353e9c8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Sheet.java @@ -23,6 +23,7 @@ import org.apache.poi.ddf.EscherContainerRecord; import org.apache.poi.ddf.EscherDgRecord; import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.usermodel.SlideShow; import java.util.ArrayList; import java.util.Iterator; @@ -38,6 +39,11 @@ import java.util.Vector; public abstract class Sheet { + /** + * The SlideShow we belong to + */ + private SlideShow _slideShow; + /** * Returns an array of all the TextRuns in the sheet. */ @@ -59,7 +65,19 @@ public abstract class Sheet * Fetch the PPDrawing from the underlying record */ protected abstract PPDrawing getPPDrawing(); + + + /** + * Fetch the SlideShow we're attached to + */ + public SlideShow getSlideShow() { return _slideShow; } + + /** + * Set the SlideShow we're attached to + */ + public void setSlideShow(SlideShow ss) { _slideShow = ss; } + /** * For a given PPDrawing, grab all the TextRuns */ @@ -149,7 +167,9 @@ public abstract class Sheet ArrayList shapes = new ArrayList(); for (int i=1;iEscherSpContainer container which holds information about this shape + * @param parent the parent of the shape + */ protected SimpleShape(EscherContainerRecord escherRecord, Shape parent){ super(escherRecord, parent); } @@ -102,12 +108,12 @@ public class SimpleShape extends Shape { } /** - * @return color of the line + * @return color of the line. If color is not set returns java.awt.Color.black */ public Color getLineColor(){ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); EscherRGBProperty prop = (EscherRGBProperty)getEscherProperty(opt, EscherProperties.LINESTYLE__COLOR); - Color color = null; + Color color = Color.black; if (prop != null){ Color swp = new Color(prop.getRgbColor()); color = new Color(swp.getBlue(), swp.getGreen(), swp.getRed()); @@ -136,6 +142,11 @@ public class SimpleShape extends Shape { return prop == null ? Line.LineSolid : prop.getPropertyValue(); } + /** + * The color used to fill this shape. + * + * @param color the background color + */ public void setFillColor(Color color){ EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java b/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java new file mode 100644 index 000000000..5584ab1be --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/model/TextBox.java @@ -0,0 +1,490 @@ + +/* ==================================================================== + Copyright 2002-2004 Apache Software Foundation + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hslf.model; + +import org.apache.poi.ddf.*; +import org.apache.poi.hslf.record.*; +import org.apache.poi.hslf.usermodel.RichTextRun; + +import java.awt.*; +import java.awt.font.FontRenderContext; +import java.awt.font.TextLayout; +import java.io.IOException; + +/** + * Represents a TextFrame shape in PowerPoint. + *

+ * Contains the text in a text frame as well as the properties and methods + * that control alignment and anchoring of the text. + *

+ * + * @author Yegor Kozlov + */ +public class TextBox extends SimpleShape { + + /** + * How to anchor the text + */ + public static final int AnchorTop = 0; + public static final int AnchorMiddle = 1; + public static final int AnchorBottom = 2; + public static final int AnchorTopCentered = 3; + public static final int AnchorMiddleCentered = 4; + public static final int AnchorBottomCentered = 5; + public static final int AnchorTopBaseline = 6; + public static final int AnchorBottomBaseline = 7; + public static final int AnchorTopCenteredBaseline = 8; + public static final int AnchorBottomCenteredBaseline = 9; + + /** + * How to wrap the text + */ + public static final int WrapSquare = 0; + public static final int WrapByPoints = 1; + public static final int WrapNone = 2; + public static final int WrapTopBottom = 3; + public static final int WrapThrough = 4; + + /** + * How to align the text + */ + public static final int AlignLeft = 0; + public static final int AlignCenter = 1; + public static final int AlignRight = 2; + public static final int AlignJustify = 3; + + /** + * Default font size + */ + public static final int DefaultFontSize = 24; + + /** + * Low-level object which holds actual text and format data + */ + protected TextRun _txtrun; + + /** + * Escher container which holds text attributes such as + * TextHeaderAtom, TextBytesAtom ot TextCharsAtom, StyleTextPropAtom etc. + */ + protected EscherTextboxWrapper _txtbox; + + /** + * Create a TextBox object and initialize it from the supplied Record container. + * + * @param escherRecord EscherSpContainer container which holds information about this shape + * @param parent the parent of the shape + */ + protected TextBox(EscherContainerRecord escherRecord, Shape parent){ + super(escherRecord, parent); + + EscherTextboxRecord textbox = (EscherTextboxRecord)Shape.getEscherChild(_escherContainer, EscherTextboxRecord.RECORD_ID); + _txtbox = new EscherTextboxWrapper(textbox); + + TextHeaderAtom tha = null; + TextBytesAtom tba = null; + StyleTextPropAtom sta = null; + Record[] child = _txtbox.getChildRecords(); + for (int i = 0; i < child.length; i++) { + if (child[i] instanceof TextHeaderAtom) tha = (TextHeaderAtom)child[i]; + else if (child[i] instanceof TextBytesAtom) tba = (TextBytesAtom)child[i]; + else if (child[i] instanceof StyleTextPropAtom) sta = (StyleTextPropAtom)child[i]; + } + + _txtrun = new TextRun(tha,tba,sta); + } + + /** + * Create a new TextBox. This constructor is used when a new shape is created. + * + * @param parent the parent of this Shape. For example, if this text box is a cell + * in a table then the parent is Table. + */ + public TextBox(Shape parent){ + super(null, parent); + _escherContainer = createSpContainer(parent instanceof ShapeGroup); + } + + /** + * Create a new TextBox. This constructor is used when a new shape is created. + * + */ + public TextBox(){ + this(null); + } + + /** + * Create a new textBox and initialize internal structures + * + * @return the created EscherContainerRecord which holds shape data + */ + protected EscherContainerRecord createSpContainer(boolean isChild){ + EscherContainerRecord spcont = super.createSpContainer(isChild); + + EscherSpRecord spRecord = spcont.getChildById(EscherSpRecord.RECORD_ID); + short type = (ShapeTypes.TextBox << 4) | 0x2; + spRecord.setOptions(type); + + //set default properties for a textbox + EscherOptRecord opt = (EscherOptRecord)getEscherChild(spcont, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0); + + setEscherProperty(opt, EscherProperties.FILL__FILLCOLOR, 134217732); + setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, 134217728); + setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 1048576); + setEscherProperty(opt, EscherProperties.LINESTYLE__COLOR, 134217729); + setEscherProperty(opt, EscherProperties.LINESTYLE__NOLINEDRAWDASH, 524288); + setEscherProperty(opt, EscherProperties.SHADOWSTYLE__COLOR, 134217730); + + opt.sortProperties(); + + //create EscherTextboxWrapper + _txtbox = new EscherTextboxWrapper(); + + TextHeaderAtom tha = new TextHeaderAtom(); + _txtbox.appendChildRecord(tha); + + TextBytesAtom tba = new TextBytesAtom(); + _txtbox.appendChildRecord(tba); + + StyleTextPropAtom sta = new StyleTextPropAtom(0); + _txtbox.appendChildRecord(sta); + + _txtrun = new TextRun(tha,tba,sta); + _txtrun.setText(""); + spcont.addChildRecord(_txtbox.getEscherRecord()); + + return spcont; + } + + /** + * Returns the text contained in this text frame. + * + * @return the text string for this textbox. + */ + public String getText(){ + return _txtrun.getText(); + } + + /** + * Sets the text contained in this text frame. + * + * @param text the text string used by this object. + */ + public void setText(String text){ + _txtrun.setText(text); + } + + /** + * When a textbox is added to a sheet we need to tell upper-level + * PPDrawing about it. + * + * @param sh the sheet we are adding to + */ + protected void afterInsert(Sheet sh){ + PPDrawing ppdrawing = sh.getPPDrawing(); + ppdrawing.addTextboxWrapper(_txtbox); + // Ensure the escher layer knows about the added records + try { + _txtbox.writeOut(null); + } catch (IOException e){ + throw new RuntimeException(e); + } + } + + /** + * Returns the bounds of this TextFrame. + * Note, this is very primitive estimation, the precision is poor. + * + * @return the bounds of this TextFrame. + */ + protected Dimension getTextSize(){ + FontRenderContext frc = new FontRenderContext(null, true, true); + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + int size = rt.getFontSize(); + if (size == -1) size = TextBox.DefaultFontSize; + int style = 0; + if (rt.isBold()) style |= Font.BOLD; + if (rt.isItalic()) style |= Font.ITALIC; + String fntname = rt.getFontName(); + if (fntname == null) //get the default font from Document.Environment.FontCollection + fntname = getSheet().getSlideShow().getDocumentRecord().getEnvironment().getFontCollection().getFontWithId(0); + Font font = new Font(fntname, style, size); + + TextLayout layout = new TextLayout(getText(), font, frc); + int width = Math.round(layout.getAdvance()); + width += getMarginLeft() + getMarginRight(); + int height = Math.round(layout.getAscent()); + height += getMarginTop() + getMarginBottom(); + return new Dimension(width, height); + } + + /** + * Adjust the size of the TextBox so it encompasses the text inside it. + */ + public void resizeToFitText(){ + Dimension size = getTextSize(); + java.awt.Rectangle anchor = getAnchor(); + anchor.setSize(size); + setAnchor(anchor); + } + + /** + * Returns the type of vertical alignment for the text. + * One of the Anchor* constants defined in this class. + * + * @return the type of alignment + */ + public int getVerticalAlignment(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT); + return prop == null ? AlignCenter : prop.getPropertyValue(); + } + + /** + * Sets the type of vertical alignment for the text. + * One of the Anchor* constants defined in this class. + * + * @param align - the type of alignment + */ + public void setVerticalAlignment(int align){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__ANCHORTEXT, align); + } + /** + * Returns the distance (in points) between the bottom of the text frame + * and the bottom of the inscribed rectangle of the shape that contains the text. + * Default value is 1/20 inch. + * + * @return the botom margin + */ + public int getMarginBottom(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM); + int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue(); + return val/EMU_PER_POINT; + } + + /** + * Sets the botom margin. + * @see #getMarginBottom() + * + * @param margin the bottom margin + */ + public void setMarginBottom(int margin){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM, margin*EMU_PER_POINT); + } + + /** + * Returns the distance (in EMUs) between the left edge of the text frame + * and the left edge of the inscribed rectangle of the shape that contains + * the text. + * Default value is 1/10 inch. + * + * @return the left margin + */ + public int getMarginLeft(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTBOTTOM); + int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue(); + return val/EMU_PER_POINT; + } + + /** + * Sets the left margin. + * @see #getMarginLeft() + * + * @param margin the left margin + */ + public void setMarginLeft(int margin){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTLEFT, margin*EMU_PER_POINT); + } + + /** + * Returns the distance (in EMUs) between the right edge of the + * text frame and the right edge of the inscribed rectangle of the shape + * that contains the text. + * Default value is 1/10 inch. + * + * @return the right margin + */ + public int getMarginRight(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT); + int val = prop == null ? EMU_PER_INCH/10 : prop.getPropertyValue(); + return val/EMU_PER_POINT; + } + + /** + * Sets the right margin. + * @see #getMarginRight() + * + * @param margin the right margin + */ + public void setMarginRight(int margin){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTRIGHT, margin*EMU_PER_POINT); + } + + /** + * Returns the distance (in EMUs) between the top of the text frame + * and the top of the inscribed rectangle of the shape that contains the text. + * Default value is 1/20 inch. + * + * @return the top margin + */ + public int getMarginTop(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTTOP); + int val = prop == null ? EMU_PER_INCH/20 : prop.getPropertyValue(); + return val/EMU_PER_POINT; + } + + /** + * Sets the top margin. + * @see #getMarginTop() + * + * @param margin the top margin + */ + public void setMarginTop(int margin){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTTOP, margin*EMU_PER_POINT); + } + + + /** + * Returns the value indicating word wrap. + * One of the Wrap* constants defined in this class. + * + * @return the value indicating word wrap + */ + public int getWordWrap(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT); + return prop == null ? WrapSquare : prop.getPropertyValue(); + } + + /** + * Specifies how the text should be wrapped + * + * @param wrap the value indicating how the text should be wrapped + */ + public void setWordWrap(int wrap){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__WRAPTEXT, wrap); + } + + /** + * @return id for the text. + */ + public int getTextId(){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + EscherSimpleProperty prop = (EscherSimpleProperty)getEscherProperty(opt, EscherProperties.TEXT__TEXTID); + return prop == null ? 0 : prop.getPropertyValue(); + } + + /** + * Sets text ID + * + * @param id of the text + */ + public void setTextId(int id){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + setEscherProperty(opt, EscherProperties.TEXT__TEXTID, id); + } + + /** + * The color used to fill this shape. + * + * @param color the background color + */ + public void setBackgroundColor(Color color){ + EscherOptRecord opt = (EscherOptRecord)getEscherChild(_escherContainer, EscherOptRecord.RECORD_ID); + int rgb = new Color(color.getBlue(), color.getGreen(), color.getRed(), 0).getRGB(); + setEscherProperty(opt, EscherProperties.FILL__FILLBACKCOLOR, rgb); + } + + /** + * @return array of RichTextRun objects which control text formatting in this text box + */ + public RichTextRun[] getRichTextRuns(){ + return _txtrun.getRichTextRuns(); + } + + /** + * Sets the Font object for this text frame + * + * @param size the size of the font + */ + public void setFontSize(int size){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + rt.setFontSize(size); + } + + /** + * + * @return the size of the font applied to this text shape + */ + public int getFontSize(){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + return rt.getFontSize(); + } + + /** + * Set whether to use bold or not + * + * @param bold true if the text should be bold, false otherwise + */ + public void setBold(boolean bold){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + rt.setBold(bold); + } + + /** + * Set whether to use italic or not + * + * @param italic true if the text should be italic, false otherwise + */ + public void setItalic(boolean italic){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + rt.setItalic(italic); + } + + /** + * Set whether to use underline or not + * + * @param underline true if the text should be underlined, false otherwise + */ + public void setUnderline(boolean underline){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + rt.setUnderlined(underline); + } + + /** + * Sets the font of this text shape + * + * @param name the name of the font to be applied to this text shape + */ + public void setFontName(String name){ + RichTextRun rt = _txtrun.getRichTextRuns()[0]; + rt.setFontName(name); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java index c07291f21..67acf2391 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java @@ -47,7 +47,7 @@ public class EscherTextboxWrapper extends RecordContainer /** * Creates the wrapper for the given DDF Escher Record and children */ - protected EscherTextboxWrapper(EscherTextboxRecord textbox) { + public EscherTextboxWrapper(EscherTextboxRecord textbox) { _escherRecord = textbox; _type = (long)_escherRecord.getRecordId(); @@ -55,7 +55,18 @@ public class EscherTextboxWrapper extends RecordContainer byte[] data = _escherRecord.getData(); _children = Record.findChildRecords(data,0,data.length); } + + /** + * Creates a new, empty wrapper for DDF Escher Records and their children + */ + public EscherTextboxWrapper() { + _escherRecord = new EscherTextboxRecord(); + _escherRecord.setRecordId(EscherTextboxRecord.RECORD_ID); + _escherRecord.setOptions((short)15); + _children = new Record[0]; + } + /** * Return the type of the escher record (normally in the 0xFnnn range) diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java index 4cbcca00b..1967fc211 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java @@ -262,4 +262,15 @@ public class PPDrawing extends RecordAtom dgContainer }; } + + /** + * Add a new EscherTextboxWrapper to this PPDrawing. + */ + public void addTextboxWrapper(EscherTextboxWrapper txtbox){ + EscherTextboxWrapper[] tw = new EscherTextboxWrapper[textboxWrappers.length + 1]; + System.arraycopy(textboxWrappers, 0, tw, 0, textboxWrappers.length); + + tw[textboxWrappers.length] = txtbox; + textboxWrappers = tw; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java index a0d421317..eb11cf673 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java @@ -71,6 +71,18 @@ public class TextBytesAtom extends RecordAtom _text = new byte[len-8]; System.arraycopy(source,start+8,_text,0,len-8); } + + /** + * Create an empty TextBytes Atom + */ + public TextBytesAtom() { + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 0); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 0); + + _text = new byte[]{}; + } /** * We are of type 4008 diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java index 7dddc529e..1e84a24ff 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java @@ -75,6 +75,18 @@ public class TextHeaderAtom extends RecordAtom implements ParentAwareRecord // Grab the type textType = (int)LittleEndian.getInt(source,start+8); } + + /** + * Create a new TextHeader Atom, for an unknown type of text + */ + public TextHeaderAtom() { + _header = new byte[8]; + LittleEndian.putUShort(_header, 0, 0); + LittleEndian.putUShort(_header, 2, (int)_type); + LittleEndian.putInt(_header, 4, 4); + + textType = OTHER_TYPE; + } /** * We are of type 3999 diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java index 0d2eba10c..2f37b72df 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/PictureData.java @@ -25,6 +25,7 @@ import java.security.NoSuchAlgorithmException; /** * A class that represents the image data contained in the Presentation. + * * * @author Yegor Kozlov */ @@ -35,6 +36,9 @@ public class PictureData { */ public static final int HEADER_SIZE = 25; + protected static final int JPEG_HEADER = -266516832; + protected static final int PNG_HEADER = -266441216; + /** * Binary data of the picture */ @@ -118,11 +122,27 @@ public class PictureData { */ public void setType(int format){ switch (format){ - case Picture.JPEG: LittleEndian.putInt(header, 0, -266516832); break; - case Picture.PNG: LittleEndian.putInt(header, 0, -266441216); break; + case Picture.JPEG: LittleEndian.putInt(header, 0, PictureData.JPEG_HEADER); break; + case Picture.PNG: LittleEndian.putInt(header, 0, PictureData.PNG_HEADER); break; } } + /** + * Returns type of this picture. + * Must be one of the static constans defined in the Picture class. + * + * @return type of this picture. + */ + public int getType(){ + int format = 0; + int val = LittleEndian.getInt(header, 0); + switch (val){ + case PictureData.JPEG_HEADER: format = Picture.JPEG; break; + case PictureData.PNG_HEADER: format = Picture.PNG; break; + } + return format; + } + /** * Returns the header of the Picture * 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 5485c8043..cef58ffe0 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/SlideShow.java @@ -401,6 +401,7 @@ public class SlideShow _notes = new Notes[notesV.size()]; for(int i=0; i<_notes.length; i++) { _notes[i] = (Notes)notesV.get(i); + _notes[i].setSlideShow(this); // Now supply ourselves to all the rich text runs // of this note's TextRuns @@ -418,6 +419,7 @@ public class SlideShow _slides = new Slide[slidesV.size()]; for(int i=0; i<_slides.length; i++) { _slides[i] = (Slide)slidesV.get(i); + _slides[i].setSlideShow(this); // Now supply ourselves to all the rich text runs // of this slide's TextRuns @@ -472,9 +474,9 @@ public class SlideShow //public MetaSheet[] getMetaSheets() { return _msheets; } /** - * Returns all the pictures attached to the SlideShow + * Returns the data of all the pictures attached to the SlideShow */ - public PictureData[] getPictures() throws IOException { + public PictureData[] getPictureData() { return _hslfSlideShow.getPictures(); } @@ -483,7 +485,20 @@ public class SlideShow */ public Dimension getPageSize(){ DocumentAtom docatom = _documentRecord.getDocumentAtom(); - return new Dimension((int)docatom.getSlideSizeX(), (int)docatom.getSlideSizeY()); + int pgx = (int)docatom.getSlideSizeX()*Shape.POINT_DPI/Shape.MASTER_DPI; + int pgy = (int)docatom.getSlideSizeY()*Shape.POINT_DPI/Shape.MASTER_DPI; + return new Dimension(pgx, pgy); + } + + /** + * Change the current page size + * + * @param pgsize page size (in points) + */ + public void setPageSize(Dimension pgsize){ + DocumentAtom docatom = _documentRecord.getDocumentAtom(); + docatom.setSlideSizeX(pgsize.width*Shape.MASTER_DPI/Shape.POINT_DPI); + docatom.setSlideSizeY(pgsize.height*Shape.MASTER_DPI/Shape.POINT_DPI); } /** @@ -491,9 +506,9 @@ public class SlideShow */ protected FontCollection getFontCollection() { return _fonts; } /** - * Helper method for usermodel: Get the document record + * Helper method for usermodel and model: Get the document record */ - protected Document getDocumentRecord() { return _documentRecord; } + public Document getDocumentRecord() { return _documentRecord; } /* =============================================================== @@ -607,6 +622,7 @@ public class SlideShow usr.setLastViewType((short)UserEditAtom.LAST_VIEW_SLIDE_VIEW); // All done and added + slide.setSlideShow(this); return slide; } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java index 400a66c6e..1ceaa9e00 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestShapes.java @@ -17,12 +17,14 @@ package org.apache.poi.hslf.model; import junit.framework.TestCase; import org.apache.poi.hslf.usermodel.SlideShow; +import org.apache.poi.hslf.usermodel.RichTextRun; import org.apache.poi.hslf.HSLFSlideShow; import java.awt.*; import java.awt.Rectangle; import java.io.ByteArrayOutputStream; import java.io.ByteArrayInputStream; +import java.io.FileOutputStream; /** * Test drawing shapes via Graphics2D @@ -36,21 +38,23 @@ public class TestShapes extends TestCase { String dirname = System.getProperty("HSLF.testdata.path"); String filename = dirname + "/empty.ppt"; ppt = new SlideShow(new HSLFSlideShow(filename)); - getClass().getResourceAsStream(""); } public void testGraphics() throws Exception { Slide slide = ppt.createSlide(); Line line = new Line(); - line.setAnchor(new Rectangle(1296, 2544, 1344, 528)); + java.awt.Rectangle lineAnchor = new java.awt.Rectangle(100, 200, 50, 60); + line.setAnchor(lineAnchor); + System.out.println(line.getAnchor()); line.setLineWidth(3); line.setLineStyle(Line.LineDashSys); line.setLineColor(Color.red); slide.addShape(line); - Ellipse ellipse = new Ellipse(); - ellipse.setAnchor(new Rectangle(4000, 1000, 1000, 1000)); + AutoShape ellipse = new AutoShape(ShapeTypes.Ellipse); + java.awt.Rectangle ellipseAnchor = new Rectangle(320, 154, 55, 111); + ellipse.setAnchor(ellipseAnchor); ellipse.setLineWidth(2); ellipse.setLineStyle(Line.LineSolid); ellipse.setLineColor(Color.green); @@ -64,17 +68,103 @@ public class TestShapes extends TestCase { //read ppt from byte array ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()))); - assertEquals(ppt.getSlides().length, 1); + assertEquals(1, ppt.getSlides().length); slide = ppt.getSlides()[0]; Shape[] shape = slide.getShapes(); - assertEquals(shape.length, 2); + assertEquals(2, shape.length); assertTrue(shape[0] instanceof Line); //group shape - assertEquals(shape[0].getAnchor(), new Rectangle(1296, 2544, 1344, 528)); //group shape + assertEquals(lineAnchor, shape[0].getAnchor()); //group shape - assertTrue(shape[1] instanceof Ellipse); //group shape - assertEquals(shape[1].getAnchor(), new Rectangle(4000, 1000, 1000, 1000)); //group shape + assertTrue(shape[1] instanceof AutoShape); //group shape + assertEquals(ellipseAnchor, shape[1].getAnchor()); //group shape + } + + /** + * Verify that we can read TextBox shapes + * @throws Exception + */ + public void testTextBoxRead() throws Exception { + String dirname = System.getProperty("HSLF.testdata.path"); + String filename = dirname + "/with_textbox.ppt"; + ppt = new SlideShow(new HSLFSlideShow(filename)); + Slide sl = ppt.getSlides()[0]; + Shape[] sh = sl.getShapes(); + for (int i = 0; i < sh.length; i++) { + assertTrue(sh[i] instanceof TextBox); + TextBox txtbox = (TextBox)sh[i]; + String text = txtbox.getText(); + assertNotNull(text); + + assertEquals(txtbox.getRichTextRuns().length, 1); + RichTextRun rt = txtbox.getRichTextRuns()[0]; + + if (text.equals("Hello, World!!!")){ + assertEquals(32, rt.getFontSize()); + assertTrue(rt.isBold()); + assertTrue(rt.isItalic()); + } else if (text.equals("I am just a poor boy")){ + assertEquals(44, rt.getFontSize()); + assertTrue(rt.isBold()); + } else if (text.equals("This is Times New Roman")){ + assertEquals(16, rt.getFontSize()); + assertTrue(rt.isBold()); + assertTrue(rt.isItalic()); + assertTrue(rt.isUnderlined()); + } else if (text.equals("Plain Text")){ + assertEquals(18, rt.getFontSize()); + } + } + } + + /** + * Verify that we can add TextBox shapes to a slide + * @throws Exception + */ + public void testTextBoxWrite() throws Exception { + ppt = new SlideShow(); + Slide sl = ppt.createSlide(); + + TextBox txtbox = new TextBox(); + txtbox.setText("Hello, World!"); + txtbox.setFontSize(42); + txtbox.setBold(true); + txtbox.setItalic(true); + + sl.addShape(txtbox); + + txtbox = new TextBox(); + txtbox.setText("Plain text in default font"); + sl.addShape(txtbox); + + assertEquals(sl.getShapes().length, 2); + + //serialize and read again + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ppt.write(out); + out.close(); + + ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()))); + sl = ppt.getSlides()[0]; + assertEquals(sl.getShapes().length, 2); + + Shape[] sh = sl.getShapes(); + for (int i = 0; i < sh.length; i++) { + assertTrue(sh[i] instanceof TextBox); + txtbox = (TextBox)sh[i]; + String text = txtbox.getText(); + assertNotNull(text); + + assertEquals(txtbox.getRichTextRuns().length, 1); + RichTextRun rt = txtbox.getRichTextRuns()[0]; + + if (text.equals("Hello, World!")){ + assertEquals(42, rt.getFontSize()); + assertTrue(rt.isBold()); + assertTrue(rt.isItalic()); + } + } } } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java index b4503eb01..3a3133a95 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java @@ -50,10 +50,37 @@ public class TestPictures extends TestCase{ BufferedImage img = ImageIO.read(new ByteArrayInputStream(data)); assertNotNull(img); + assertEquals(Picture.PNG, pict[i].getType()); } ppt.close(); } + public void testReadPicturesForSlide() throws Exception { + + SlideShow ppt = new SlideShow(new HSLFSlideShow(filename)); + + Slide[] slide = ppt.getSlides(); + for (int i = 0; i < slide.length; i++) { + Slide sl = slide[i]; + Shape[] sh = sl.getShapes(); + for (int j = 0; j < sh.length; j++) { + Shape shape = sh[j]; + if (shape instanceof Picture){ + Picture picture = (Picture)shape; + + PictureData pictdata = picture.getPictureData(); + assertEquals(Picture.PNG, pictdata.getType()); + + //raw data. + byte[] data = pictdata.getData(); + BufferedImage img = ImageIO.read(new ByteArrayInputStream(data)); + assertNotNull(img); + } + } + + } + } + public void testSerializePictures() throws Exception { HSLFSlideShow ppt = new HSLFSlideShow(filename); PictureData[] pict = ppt.getPictures(); @@ -78,12 +105,10 @@ public class TestPictures extends TestCase{ idx = ppt.addPicture(new File(dirname + "/clock.jpg"), Picture.JPEG); slide = ppt.createSlide(); pict = new Picture(idx); - pict.setDefaultSize(ppt); slide.addShape(pict); idx = ppt.addPicture(new File(dirname + "/painting.png"), Picture.PNG); pict = new Picture(idx); - pict.setDefaultSize(ppt); slide.addShape(pict); ByteArrayOutputStream out = new ByteArrayOutputStream(); @@ -91,7 +116,7 @@ public class TestPictures extends TestCase{ out.close(); ppt = new SlideShow(new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()))); - assertTrue(ppt.getPictures().length == 2 ); + assertTrue(ppt.getPictureData().length == 2 ); } }