From: Andreas Beeker Date: Wed, 10 Jun 2015 22:23:47 +0000 (+0000) Subject: Fixed TextRun handling and various junit tests X-Git-Tag: REL_3_13_FINAL~207^2~10 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=b00e544e9b28a8c0a1c1ad5eddc473a6fa0285cf;p=poi.git Fixed TextRun handling and various junit tests git-svn-id: https://svn.apache.org/repos/asf/poi/branches/common_sl@1684773 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java b/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java index 494c7f9c32..c27e8e4e8e 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java +++ b/src/examples/src/org/apache/poi/hslf/examples/ApacheconEU08.java @@ -146,10 +146,10 @@ public final class ApacheconEU08 { {"Note"}, {"This presentation was created programmatically using POI HSLF"} }; - Table table1 = new Table(2, 1); + HSLFTable table1 = new HSLFTable(2, 1); for (int i = 0; i < txt1.length; i++) { for (int j = 0; j < txt1[i].length; j++) { - TableCell cell = table1.getCell(i, j); + HSLFTableCell cell = table1.getCell(i, j); cell.setText(txt1[i][j]); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); rt.setFontSize(10); diff --git a/src/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java b/src/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java index 19fe496769..cd900902d8 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java +++ b/src/examples/src/org/apache/poi/hslf/examples/Graphics2DDemo.java @@ -50,7 +50,7 @@ public final class Graphics2DDemo { //define position of the drawing in the slide Rectangle bounds = new java.awt.Rectangle(200, 100, 350, 300); group.setAnchor(bounds); - group.setCoordinates(new java.awt.Rectangle(0, 0, 100, 100)); + group.setInteriorAnchor(new java.awt.Rectangle(0, 0, 100, 100)); slide.addShape(group); Graphics2D graphics = new PPGraphics2D(group); @@ -68,7 +68,7 @@ public final class Graphics2DDemo { } graphics.setColor(Color.black); graphics.setFont(new Font("Arial", Font.BOLD, 14)); - graphics.draw(group.getCoordinates()); + graphics.draw(group.getInteriorAnchor()); graphics.drawString("Performance", x + 30, y + 10); FileOutputStream out = new FileOutputStream("hslf-graphics.ppt"); diff --git a/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java b/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java index abc40750cc..b931ba1b57 100644 --- a/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java +++ b/src/examples/src/org/apache/poi/hslf/examples/TableDemo.java @@ -49,10 +49,10 @@ public final class TableDemo { HSLFSlide slide = ppt.createSlide(); //six rows, two columns - Table table1 = new Table(6, 2); + HSLFTable table1 = new HSLFTable(6, 2); for (int i = 0; i < txt1.length; i++) { for (int j = 0; j < txt1[i].length; j++) { - TableCell cell = table1.getCell(i, j); + HSLFTableCell cell = table1.getCell(i, j); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); rt.setFontFamily("Arial"); rt.setFontSize(10); @@ -88,10 +88,10 @@ public final class TableDemo { }; //two rows, one column - Table table2 = new Table(2, 1); + HSLFTable table2 = new HSLFTable(2, 1); for (int i = 0; i < txt2.length; i++) { for (int j = 0; j < txt2[i].length; j++) { - TableCell cell = table2.getCell(i, j); + HSLFTableCell cell = table2.getCell(i, j); HSLFTextRun rt = cell.getTextParagraphs().get(0).getTextRuns().get(0); rt.setFontSize(10); rt.setFontFamily("Arial"); diff --git a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java index b6193cea4e..bbb84bae8c 100644 --- a/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java +++ b/src/java/org/apache/poi/ddf/EscherChildAnchorRecord.java @@ -40,13 +40,26 @@ public class EscherChildAnchorRecord private int field_4_dy2; public int fillFields(byte[] data, int offset, EscherRecordFactory recordFactory) { - /*int bytesRemaining =*/ readHeader( data, offset ); + int bytesRemaining = readHeader( data, offset ); int pos = offset + 8; int size = 0; - field_1_dx1 = LittleEndian.getInt( data, pos + size );size+=4; - field_2_dy1 = LittleEndian.getInt( data, pos + size );size+=4; - field_3_dx2 = LittleEndian.getInt( data, pos + size );size+=4; - field_4_dy2 = LittleEndian.getInt( data, pos + size );size+=4; + switch (bytesRemaining) { + case 16: // RectStruct + field_1_dx1 = LittleEndian.getInt( data, pos + size );size+=4; + field_2_dy1 = LittleEndian.getInt( data, pos + size );size+=4; + field_3_dx2 = LittleEndian.getInt( data, pos + size );size+=4; + field_4_dy2 = LittleEndian.getInt( data, pos + size );size+=4; + break; + case 8: // SmallRectStruct + field_1_dx1 = LittleEndian.getShort( data, pos + size );size+=2; + field_2_dy1 = LittleEndian.getShort( data, pos + size );size+=2; + field_3_dx2 = LittleEndian.getShort( data, pos + size );size+=2; + field_4_dy2 = LittleEndian.getShort( data, pos + size );size+=2; + break; + default: + throw new RuntimeException("Invalid EscherChildAnchorRecord - neither 8 nor 16 bytes."); + } + return 8 + size; } @@ -58,8 +71,8 @@ public class EscherChildAnchorRecord LittleEndian.putInt( data, pos, getRecordSize()-8 ); pos += 4; LittleEndian.putInt( data, pos, field_1_dx1 ); pos += 4; LittleEndian.putInt( data, pos, field_2_dy1 ); pos += 4; - LittleEndian.putInt( data, pos, field_3_dx2 ); pos += 4; - LittleEndian.putInt( data, pos, field_4_dy2 ); pos += 4; + LittleEndian.putInt( data, pos, field_3_dx2 ); pos += 4; + LittleEndian.putInt( data, pos, field_4_dy2 ); pos += 4; listener.afterRecordSerialize( pos, getRecordId(), pos - offset, this ); return pos - offset; diff --git a/src/java/org/apache/poi/ddf/EscherProperties.java b/src/java/org/apache/poi/ddf/EscherProperties.java index 7ec219006b..2bee6b92e5 100644 --- a/src/java/org/apache/poi/ddf/EscherProperties.java +++ b/src/java/org/apache/poi/ddf/EscherProperties.java @@ -507,7 +507,7 @@ public final class EscherProperties { addProp(m, SHADOWSTYLE__ORIGINX, "shadowstyle.originx"); addProp(m, SHADOWSTYLE__ORIGINY, "shadowstyle.originy"); addProp(m, SHADOWSTYLE__SHADOW, "shadowstyle.shadow"); - addProp(m, SHADOWSTYLE__SHADOWOBSURED, "shadowstyle.shadowobsured"); + addProp(m, SHADOWSTYLE__SHADOWOBSURED, "shadowstyle.shadowobscured"); addProp(m, PERSPECTIVE__TYPE, "perspective.type"); addProp(m, PERSPECTIVE__OFFSETX, "perspective.offsetx"); addProp(m, PERSPECTIVE__OFFSETY, "perspective.offsety"); diff --git a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java index c21c227968..dbf28531fe 100644 --- a/src/java/org/apache/poi/ddf/EscherTextboxRecord.java +++ b/src/java/org/apache/poi/ddf/EscherTextboxRecord.java @@ -17,6 +17,7 @@ package org.apache.poi.ddf; +import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.RecordFormatException; @@ -32,7 +33,7 @@ import org.apache.poi.util.RecordFormatException; */ public class EscherTextboxRecord extends EscherRecord { - public static final short RECORD_ID = (short)0xF00D; + public static final short RECORD_ID = (short)RecordTypes.EscherClientTextbox; public static final String RECORD_DESCRIPTION = "msofbtClientTextbox"; private static final byte[] NO_BYTES = new byte[0]; diff --git a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java index beadac4646..8ada601697 100644 --- a/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java +++ b/src/java/org/apache/poi/hssf/usermodel/DummyGraphics2d.java @@ -28,6 +28,7 @@ import java.awt.image.ImageObserver; import java.awt.image.RenderedImage; import java.awt.image.renderable.RenderableImage; import java.text.AttributedCharacterIterator; +import java.util.Arrays; import java.util.Map; public class DummyGraphics2d @@ -262,7 +263,7 @@ public class DummyGraphics2d public void setPaint( Paint paint ) { - System.out.println( "setPain(Paint):" ); + System.out.println( "setPaint(Paint):" ); System.out.println( "paint = " + paint ); g2D.setPaint( paint ); } @@ -285,7 +286,19 @@ public class DummyGraphics2d public void setStroke(Stroke s) { System.out.println( "setStroke(Stoke):" ); - System.out.println( "s = " + s ); + if (s instanceof BasicStroke) { + BasicStroke bs = (BasicStroke)s; + StringBuilder str = new StringBuilder("s = BasicStroke("); + str.append("dash[]: "+Arrays.toString(bs.getDashArray())+", "); + str.append("dashPhase: "+bs.getDashPhase()+", "); + str.append("endCap: "+bs.getEndCap()+", "); + str.append("lineJoin: "+bs.getLineJoin()+", "); + str.append("width: "+bs.getLineWidth()+", "); + str.append("miterLimit: "+bs.getMiterLimit()+")"); + System.out.println(str.toString()); + } else { + System.out.println( "s = " + s ); + } g2D.setStroke( s ); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java index 685ae46da5..309f39fd9f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFGroupShape.java @@ -25,8 +25,7 @@ import java.util.List; import java.util.regex.Pattern; import org.apache.poi.openxml4j.opc.*; -import org.apache.poi.sl.usermodel.PlaceableShape; -import org.apache.poi.sl.usermodel.ShapeGroup; +import org.apache.poi.sl.usermodel.GroupShape; import org.apache.poi.util.*; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.*; @@ -38,7 +37,7 @@ import org.openxmlformats.schemas.presentationml.x2006.main.*; * @author Yegor Kozlov */ @Beta -public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, ShapeGroup { +public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, GroupShape { private static POILogger _logger = POILogFactory.getLogger(XSLFGroupShape.class); private final List _shapes; @@ -118,7 +117,7 @@ public class XSLFGroupShape extends XSLFShape implements XSLFShapeContainer, Sha * used for calculations of grouping, scaling, and rotation * behavior of shapes placed within a group. */ - public void setInteriorAnchor(Rectangle2D anchor){ + public void setInteriorAnchor(Rectangle2D anchor) { CTGroupTransform2D xfrm = getSafeXfrm(); CTPoint2D off = xfrm.isSetChOff() ? xfrm.getChOff() : xfrm.addNewChOff(); long x = Units.toEMU(anchor.getX()); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java index b815e00d42..3d532bb953 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java @@ -169,6 +169,7 @@ public class XSLFPictureShape extends XSLFSimpleShape implements PictureShape { return id; } + @Override public Insets getClipping(){ CTPicture ct = (CTPicture)getXmlObject(); CTRelativeRect r = ct.getBlipFill().getSrcRect(); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index 43ec701301..e1fff264ca 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -481,6 +481,7 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements XSLFShapeC * * @param graphics */ + @Override public void draw(Graphics2D graphics){ DrawFactory drawFact = DrawFactory.getInstance(graphics); Drawable draw = drawFact.getDrawable(this); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java index b6ee1bc8e2..8b5e596702 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -21,7 +21,6 @@ import java.io.IOException; import org.apache.poi.POIXMLDocumentPart; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.openxml4j.opc.PackageRelationship; -import org.apache.poi.sl.usermodel.Notes; import org.apache.poi.sl.usermodel.Slide; import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java b/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java deleted file mode 100644 index cfeffb8008..0000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java +++ /dev/null @@ -1,95 +0,0 @@ -/* ==================================================================== - 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.blip; - -import org.apache.poi.hslf.usermodel.HSLFPictureData; -import org.apache.poi.hslf.usermodel.HSLFPictureShape; -import org.apache.poi.util.POILogger; -import org.apache.poi.util.POILogFactory; - - - - - -/* ==================================================================== - 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. -==================================================================== */ -import javax.imageio.ImageIO; - -import java.awt.*; -import java.awt.geom.AffineTransform; -import java.awt.image.BufferedImage; -import java.io.ByteArrayInputStream; - -/** - * Creates BufferedImage using javax.imageio.ImageIO and draws it in the specified graphics. - * - * @author Yegor Kozlov. - */ -public final class BitmapPainter implements ImagePainter { - protected POILogger logger = POILogFactory.getLogger(this.getClass()); - - public void paint(Graphics2D graphics, HSLFPictureData pict, HSLFPictureShape parent) { - BufferedImage img; - try { - img = ImageIO.read(new ByteArrayInputStream(pict.getData())); - } catch (Exception e) { - logger.log(POILogger.WARN, "ImageIO failed to create image. image.type: " + pict.getType()); - return; - } - - boolean isClipped = true; - Insets clip = parent.getBlipClip(); - if (clip == null) { - isClipped = false; - clip = new Insets(0,0,0,0); - } - - int iw = img.getWidth(); - int ih = img.getHeight(); - - Rectangle anchor = parent.getLogicalAnchor2D().getBounds(); - - double cw = (100000-clip.left-clip.right) / 100000.0; - double ch = (100000-clip.top-clip.bottom) / 100000.0; - double sx = anchor.getWidth()/(iw*cw); - double sy = anchor.getHeight()/(ih*ch); - double tx = anchor.getX()-(iw*sx*clip.left/100000.0); - double ty = anchor.getY()-(ih*sy*clip.top/100000.0); - AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ; - - Shape clipOld = graphics.getClip(); - if (isClipped) graphics.clip(anchor.getBounds2D()); - graphics.drawRenderedImage(img, at); - graphics.setClip(clipOld); - } - -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/blip/ImagePainter.java b/src/scratchpad/src/org/apache/poi/hslf/blip/ImagePainter.java deleted file mode 100644 index 76359dc304..0000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/blip/ImagePainter.java +++ /dev/null @@ -1,72 +0,0 @@ -/* ==================================================================== - 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.blip; - -import org.apache.poi.hslf.usermodel.HSLFPictureData; -import org.apache.poi.hslf.usermodel.HSLFPictureShape; - -import java.awt.*; - -/** - * A common interface for objects that can render ppt picture data. - *

- * Subclasses can redefine it and use third-party libraries for actual rendering, - * for example, Bitmaps can be rendered using javax.imageio.* , WMF can be rendered using Apache Batik, - * PICT can be rendered using Apple QuickTime API for Java, etc. - *

- * - * A typical usage is as follows: - * - * public WMFPaiter implements ImagePainter{ - * public void paint(Graphics2D graphics, PictureData pict, Picture parent){ - * DataInputStream is = new DataInputStream(new ByteArrayInputStream(pict.getData())); - * org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore wmfStore = - * new org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore(); - * try { - * wmfStore.read(is); - * } catch (IOException e){ - * return; - * } - * - * Rectangle anchor = parent.getAnchor(); - * float scale = (float)anchor.width/wmfStore.getWidthPixels(); - * - * org.apache.batik.transcoder.wmf.tosvg.WMFPainter painter = - * new org.apache.batik.transcoder.wmf.tosvg.WMFPainter(wmfStore, 0, 0, scale); - * graphics.translate(anchor.x, anchor.y); - * painter.paint(graphics); - * } - * } - * PictureData.setImagePainter(Picture.WMF, new WMFPaiter()); - * ... - * - * Subsequent calls of Slide.draw(Graphics gr) will use WMFPaiter for WMF images. - * - * @author Yegor Kozlov. - */ -public interface ImagePainter { - - /** - * Paints the specified picture data - * - * @param graphics the graphics to paintb into - * @param pict the data to paint - * @param parent the shapes that owns the picture data - */ - public void paint(Graphics2D graphics, HSLFPictureData pict, HSLFPictureShape parent); -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java index 5dceebf9c5..1167cc3558 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java +++ b/src/scratchpad/src/org/apache/poi/hslf/extractor/PowerPointExtractor.java @@ -247,8 +247,8 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { // Table text for (HSLFShape shape : slide.getShapes()){ - if (shape instanceof Table){ - extractTableText(ret, (Table)shape); + if (shape instanceof HSLFTable){ + extractTableText(ret, (HSLFTable)shape); } } // Slide footer, if set @@ -305,10 +305,10 @@ public final class PowerPointExtractor extends POIOLE2TextExtractor { return ret.toString(); } - private void extractTableText(StringBuffer ret, Table table) { + private void extractTableText(StringBuffer ret, HSLFTable table) { for (int row = 0; row < table.getNumberOfRows(); row++){ for (int col = 0; col < table.getNumberOfColumns(); col++){ - TableCell cell = table.getCell(row, col); + HSLFTableCell cell = table.getCell(row, col); //defensive null checks; don't know if they're necessary if (cell != null){ String txt = cell.getText(); 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 b28c88ec68..c777351728 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Line.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/Line.java @@ -22,8 +22,8 @@ import org.apache.poi.hslf.usermodel.*; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.ShapeType; -import java.awt.geom.Rectangle2D; -import java.awt.geom.Line2D; +import java.awt.geom.*; +import java.util.ArrayList; /** * Represents a line in a PowerPoint drawing @@ -69,4 +69,63 @@ public final class Line extends HSLFSimpleShape { Rectangle2D anchor = getLogicalAnchor2D(); return new Line2D.Double(anchor.getX(), anchor.getY(), anchor.getX() + anchor.getWidth(), anchor.getY() + anchor.getHeight()); } + + /** + * + * @return 'absolute' anchor of this shape relative to the parent sheet + * + * @deprecated TODO: remove the whole class, should work with preset geometries instead + */ + public Rectangle2D getLogicalAnchor2D(){ + Rectangle2D anchor = getAnchor2D(); + + //if it is a groupped shape see if we need to transform the coordinates + if (getParent() != null){ + ArrayList lst = new ArrayList(); + for (ShapeContainer parent=this.getParent(); + parent instanceof HSLFGroupShape; + parent = ((HSLFGroupShape)parent).getParent()) { + lst.add(0, (HSLFGroupShape)parent); + } + + AffineTransform tx = new AffineTransform(); + for(HSLFGroupShape prnt : lst) { + Rectangle2D exterior = prnt.getAnchor2D(); + Rectangle2D interior = prnt.getInteriorAnchor(); + + double scaleX = exterior.getWidth() / interior.getWidth(); + double scaleY = exterior.getHeight() / interior.getHeight(); + + tx.translate(exterior.getX(), exterior.getY()); + tx.scale(scaleX, scaleY); + tx.translate(-interior.getX(), -interior.getY()); + + } + anchor = tx.createTransformedShape(anchor).getBounds2D(); + } + + double angle = getRotation(); + if(angle != 0.){ + double centerX = anchor.getX() + anchor.getWidth()/2; + double centerY = anchor.getY() + anchor.getHeight()/2; + + AffineTransform trans = new AffineTransform(); + trans.translate(centerX, centerY); + trans.rotate(Math.toRadians(angle)); + trans.translate(-centerX, -centerY); + + Rectangle2D rect = trans.createTransformedShape(anchor).getBounds2D(); + if((anchor.getWidth() < anchor.getHeight() && rect.getWidth() > rect.getHeight()) || + (anchor.getWidth() > anchor.getHeight() && rect.getWidth() < rect.getHeight()) ){ + trans = new AffineTransform(); + trans.translate(centerX, centerY); + trans.rotate(Math.PI/2); + trans.translate(-centerX, -centerY); + anchor = trans.createTransformedShape(anchor).getBounds2D(); + } + } + return anchor; + } + + } diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/Table.java b/src/scratchpad/src/org/apache/poi/hslf/model/Table.java deleted file mode 100644 index 8f3b245c15..0000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Table.java +++ /dev/null @@ -1,345 +0,0 @@ -/* ==================================================================== - 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 org.apache.poi.ddf.*; -import org.apache.poi.hslf.usermodel.*; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.util.LittleEndian; - -import java.util.*; -import java.util.List; -import java.awt.*; - -/** - * Represents a table in a PowerPoint presentation - * - * @author Yegor Kozlov - */ -public final class Table extends HSLFGroupShape { - - protected static final int BORDER_TOP = 1; - protected static final int BORDER_RIGHT = 2; - protected static final int BORDER_BOTTOM = 3; - protected static final int BORDER_LEFT = 4; - - protected static final int BORDERS_ALL = 5; - protected static final int BORDERS_OUTSIDE = 6; - protected static final int BORDERS_INSIDE = 7; - protected static final int BORDERS_NONE = 8; - - - protected TableCell[][] cells; - - /** - * Create a new Table of the given number of rows and columns - * - * @param numrows the number of rows - * @param numcols the number of columns - */ - public Table(int numrows, int numcols) { - super(); - - if(numrows < 1) throw new IllegalArgumentException("The number of rows must be greater than 1"); - if(numcols < 1) throw new IllegalArgumentException("The number of columns must be greater than 1"); - - int x=0, y=0, tblWidth=0, tblHeight=0; - cells = new TableCell[numrows][numcols]; - for (int i = 0; i < cells.length; i++) { - x = 0; - for (int j = 0; j < cells[i].length; j++) { - cells[i][j] = new TableCell(this); - Rectangle anchor = new Rectangle(x, y, TableCell.DEFAULT_WIDTH, TableCell.DEFAULT_HEIGHT); - cells[i][j].setAnchor(anchor); - x += TableCell.DEFAULT_WIDTH; - } - y += TableCell.DEFAULT_HEIGHT; - } - tblWidth = x; - tblHeight = y; - setAnchor(new Rectangle(0, 0, tblWidth, tblHeight)); - - EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0); - EscherOptRecord opt = new EscherOptRecord(); - opt.setRecordId((short)0xF122); - opt.addEscherProperty(new EscherSimpleProperty((short)0x39F, 1)); - EscherArrayProperty p = new EscherArrayProperty((short)(0x4000 | 0x3A0), false, null); - p.setSizeOfElements(0x0004); - p.setNumberOfElementsInArray(numrows); - p.setNumberOfElementsInMemory(numrows); - opt.addEscherProperty(p); - List lst = spCont.getChildRecords(); - lst.add(lst.size()-1, opt); - spCont.setChildRecords(lst); - } - - /** - * Create a Table 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 - */ - public Table(EscherContainerRecord escherRecord, ShapeContainer parent) { - super(escherRecord, parent); - } - - /** - * Gets a cell - * - * @param row the row index (0-based) - * @param col the column index (0-based) - * @return the cell - */ - public TableCell getCell(int row, int col) { - return cells[row][col]; - } - - public int getNumberOfColumns() { - return cells[0].length; - } - public int getNumberOfRows() { - return cells.length; - } - - protected void afterInsert(HSLFSheet sh){ - super.afterInsert(sh); - - EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0); - List lst = spCont.getChildRecords(); - EscherOptRecord opt = (EscherOptRecord)lst.get(lst.size()-2); - EscherArrayProperty p = opt.lookup(0x3A0); - for (int i = 0; i < cells.length; i++) { - TableCell cell = cells[i][0]; - int rowHeight = cell.getAnchor().height*MASTER_DPI/POINT_DPI; - byte[] val = new byte[4]; - LittleEndian.putInt(val, 0, rowHeight); - p.setElement(i, val); - for (int j = 0; j < cells[i].length; j++) { - TableCell c = cells[i][j]; - addShape(c); - - Line bt = c.getBorderTop(); - if(bt != null) addShape(bt); - - Line br = c.getBorderRight(); - if(br != null) addShape(br); - - Line bb = c.getBorderBottom(); - if(bb != null) addShape(bb); - - Line bl = c.getBorderLeft(); - if(bl != null) addShape(bl); - - } - } - - } - - protected void initTable(){ - List shapeList = getShapeList(); - Collections.sort(shapeList, new Comparator(){ - public int compare( HSLFShape o1, HSLFShape o2 ) { - Rectangle anchor1 = o1.getAnchor(); - Rectangle anchor2 = o2.getAnchor(); - int delta = anchor1.y - anchor2.y; - if(delta == 0) delta = anchor1.x - anchor2.x; - return delta; - } - }); - int y0 = -1; - int maxrowlen = 0; - List> lst = new ArrayList>(); - List row = null; - for (HSLFShape sh : shapeList) { - if(sh instanceof HSLFTextShape){ - Rectangle anchor = sh.getAnchor(); - if(anchor.y != y0){ - y0 = anchor.y; - row = new ArrayList(); - lst.add(row); - } - row.add(sh); - maxrowlen = Math.max(maxrowlen, row.size()); - } - } - cells = new TableCell[lst.size()][maxrowlen]; - for (int i = 0; i < lst.size(); i++) { - row = lst.get(i); - for (int j = 0; j < row.size(); j++) { - HSLFTextShape tx = (HSLFTextShape)row.get(j); - cells[i][j] = new TableCell(tx.getSpContainer(), getParent()); - cells[i][j].setSheet(tx.getSheet()); - } - } - } - - /** - * Assign the SlideShow this shape belongs to - * - * @param sheet owner of this shape - */ - public void setSheet(HSLFSheet sheet){ - super.setSheet(sheet); - if(cells == null) initTable(); - } - - /** - * Sets the row height. - * - * @param row the row index (0-based) - * @param height the height to set (in pixels) - */ - public void setRowHeight(int row, int height){ - int currentHeight = cells[row][0].getAnchor().height; - int dy = height - currentHeight; - - for (int i = row; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - Rectangle anchor = cells[i][j].getAnchor(); - if(i == row) anchor.height = height; - else anchor.y += dy; - cells[i][j].setAnchor(anchor); - } - } - Rectangle tblanchor = getAnchor(); - tblanchor.height += dy; - setAnchor(tblanchor); - - } - - /** - * Sets the column width. - * - * @param col the column index (0-based) - * @param width the width to set (in pixels) - */ - public void setColumnWidth(int col, int width){ - int currentWidth = cells[0][col].getAnchor().width; - int dx = width - currentWidth; - for (int i = 0; i < cells.length; i++) { - Rectangle anchor = cells[i][col].getAnchor(); - anchor.width = width; - cells[i][col].setAnchor(anchor); - - if(col < cells[i].length - 1) for (int j = col+1; j < cells[i].length; j++) { - anchor = cells[i][j].getAnchor(); - anchor.x += dx; - cells[i][j].setAnchor(anchor); - } - } - Rectangle tblanchor = getAnchor(); - tblanchor.width += dx; - setAnchor(tblanchor); - } - - /** - * Format the table and apply the specified Line to all cell boundaries, - * both outside and inside - * - * @param line the border line - */ - public void setAllBorders(Line line){ - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - TableCell cell = cells[i][j]; - cell.setBorderTop(cloneBorder(line)); - cell.setBorderLeft(cloneBorder(line)); - if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); - if(i == cells.length - 1) cell.setBorderBottom(cloneBorder(line)); - } - } - } - - /** - * Format the outside border using the specified Line object - * - * @param line the border line - */ - public void setOutsideBorders(Line line){ - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - TableCell cell = cells[i][j]; - - if(j == 0) cell.setBorderLeft(cloneBorder(line)); - if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); - else { - cell.setBorderLeft(null); - cell.setBorderLeft(null); - } - - if(i == 0) cell.setBorderTop(cloneBorder(line)); - else if(i == cells.length - 1) cell.setBorderBottom(cloneBorder(line)); - else { - cell.setBorderTop(null); - cell.setBorderBottom(null); - } - } - } - } - - /** - * Format the inside border using the specified Line object - * - * @param line the border line - */ - public void setInsideBorders(Line line){ - for (int i = 0; i < cells.length; i++) { - for (int j = 0; j < cells[i].length; j++) { - TableCell cell = cells[i][j]; - - if(j != cells[i].length - 1) - cell.setBorderRight(cloneBorder(line)); - else { - cell.setBorderLeft(null); - cell.setBorderLeft(null); - } - if(i != cells.length - 1) cell.setBorderBottom(cloneBorder(line)); - else { - cell.setBorderTop(null); - cell.setBorderBottom(null); - } - } - } - } - - private Line cloneBorder(Line line){ - Line border = createBorder(); - border.setLineWidth(line.getLineWidth()); - border.setLineDashing(line.getLineDashing()); - border.setLineColor(line.getLineColor()); - border.setLineCompound(line.getLineCompound()); - return border; - } - - /** - * Create a border to format this table - * - * @return the created border - */ - public Line createBorder(){ - Line line = new Line(this); - - EscherOptRecord opt = getEscherOptRecord(); - setEscherProperty(opt, EscherProperties.GEOMETRY__SHAPEPATH, -1); - setEscherProperty(opt, EscherProperties.GEOMETRY__FILLOK, -1); - setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000); - setEscherProperty(opt, EscherProperties.THREED__LIGHTFACE, 0x80000); - - return line; - } -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java b/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java deleted file mode 100644 index cfdee76633..0000000000 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java +++ /dev/null @@ -1,158 +0,0 @@ -/* ==================================================================== - 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 java.awt.Rectangle; - -import org.apache.poi.ddf.EscherContainerRecord; -import org.apache.poi.ddf.EscherOptRecord; -import org.apache.poi.ddf.EscherProperties; -import org.apache.poi.hslf.usermodel.HSLFShape; -import org.apache.poi.hslf.usermodel.HSLFTextBox; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.sl.usermodel.ShapeType; - -/** - * Represents a cell in a ppt table - * - * @author Yegor Kozlov - */ -public final class TableCell extends HSLFTextBox { - protected static final int DEFAULT_WIDTH = 100; - protected static final int DEFAULT_HEIGHT = 40; - - private Line borderLeft; - private Line borderRight; - private Line borderTop; - private Line borderBottom; - - /** - * Create a TableCell object and initialize it from the supplied Record container. - * - * @param escherRecord {@link EscherSpContainer} container which holds information about this shape - * @param parent the parent of the shape - */ - protected TableCell(EscherContainerRecord escherRecord, ShapeContainer parent){ - super(escherRecord, parent); - } - - /** - * Create a new TableCell. 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 TableCell(ShapeContainer parent){ - super(parent); - - setShapeType(ShapeType.RECT); - //_txtrun.setRunType(TextHeaderAtom.HALF_BODY_TYPE); - //_txtrun.getRichTextRuns()[0].setFlag(false, 0, false); - } - - protected EscherContainerRecord createSpContainer(boolean isChild){ - _escherContainer = super.createSpContainer(isChild); - EscherOptRecord opt = getEscherOptRecord(); - setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0); - setEscherProperty(opt, EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20000); - setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150001); - setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000); - setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x40000); - - return _escherContainer; - } - - protected void anchorBorder(int type, Line line){ - Rectangle cellAnchor = getAnchor(); - Rectangle lineAnchor = new Rectangle(); - switch(type){ - case Table.BORDER_TOP: - lineAnchor.x = cellAnchor.x; - lineAnchor.y = cellAnchor.y; - lineAnchor.width = cellAnchor.width; - lineAnchor.height = 0; - break; - case Table.BORDER_RIGHT: - lineAnchor.x = cellAnchor.x + cellAnchor.width; - lineAnchor.y = cellAnchor.y; - lineAnchor.width = 0; - lineAnchor.height = cellAnchor.height; - break; - case Table.BORDER_BOTTOM: - lineAnchor.x = cellAnchor.x; - lineAnchor.y = cellAnchor.y + cellAnchor.height; - lineAnchor.width = cellAnchor.width; - lineAnchor.height = 0; - break; - case Table.BORDER_LEFT: - lineAnchor.x = cellAnchor.x; - lineAnchor.y = cellAnchor.y; - lineAnchor.width = 0; - lineAnchor.height = cellAnchor.height; - break; - default: - throw new IllegalArgumentException("Unknown border type: " + type); - } - line.setAnchor(lineAnchor); - } - - public Line getBorderLeft() { - return borderLeft; - } - - public void setBorderLeft(Line line) { - if(line != null) anchorBorder(Table.BORDER_LEFT, line); - this.borderLeft = line; - } - - public Line getBorderRight() { - return borderRight; - } - - public void setBorderRight(Line line) { - if(line != null) anchorBorder(Table.BORDER_RIGHT, line); - this.borderRight = line; - } - - public Line getBorderTop() { - return borderTop; - } - - public void setBorderTop(Line line) { - if(line != null) anchorBorder(Table.BORDER_TOP, line); - this.borderTop = line; - } - - public Line getBorderBottom() { - return borderBottom; - } - - public void setBorderBottom(Line line) { - if(line != null) anchorBorder(Table.BORDER_BOTTOM, line); - this.borderBottom = line; - } - - public void setAnchor(Rectangle anchor){ - super.setAnchor(anchor); - - if(borderTop != null) anchorBorder(Table.BORDER_TOP, borderTop); - if(borderRight != null) anchorBorder(Table.BORDER_RIGHT, borderRight); - if(borderBottom != null) anchorBorder(Table.BORDER_BOTTOM, borderBottom); - if(borderLeft != null) anchorBorder(Table.BORDER_LEFT, borderLeft); - } -} diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index 0e46e6a7f5..17d0c1d2c5 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java @@ -285,6 +285,7 @@ public class TextPropCollection { * Clones the given text properties */ public void copy(TextPropCollection other) { + if (this == other) return; this.charactersCovered = other.charactersCovered; this.indentLevel = other.indentLevel; this.maskSpecial = other.maskSpecial; 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 715eb81799..10744e49d7 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/EscherTextboxWrapper.java @@ -87,9 +87,7 @@ public final class EscherTextboxWrapper extends RecordContainer { // Grab the children's data ByteArrayOutputStream baos = new ByteArrayOutputStream(); - for(int i=0; i<_children.length; i++) { - _children[i].writeOut(baos); - } + for (Record r : _children) r.writeOut(baos); byte[] data = baos.toByteArray(); // Save in the escher layer 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 09f21d1baf..09854b5ab9 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/PPDrawing.java @@ -97,7 +97,7 @@ public final class PPDrawing extends RecordAtom { findEscherChildren(erf, contents, 8, len-8, escherChildren); this.childRecords = escherChildren.toArray(new EscherRecord[escherChildren.size()]); - if (1 == this.childRecords.length && (short)0xf002 == this.childRecords[0].getRecordId() && this.childRecords[0] instanceof EscherContainerRecord) { + if (1 == this.childRecords.length && (short)RecordTypes.EscherDgContainer == this.childRecords[0].getRecordId() && this.childRecords[0] instanceof EscherContainerRecord) { this.textboxWrappers = findInDgContainer((EscherContainerRecord) this.childRecords[0]); } else { // Find and EscherTextboxRecord's, and wrap them up @@ -106,37 +106,30 @@ public final class PPDrawing extends RecordAtom { this.textboxWrappers = textboxes.toArray(new EscherTextboxWrapper[textboxes.size()]); } } - private EscherTextboxWrapper[] findInDgContainer(final EscherContainerRecord escherContainerF002) { + private EscherTextboxWrapper[] findInDgContainer(final EscherContainerRecord dgContainer) { final List found = new LinkedList(); - final EscherContainerRecord SpgrContainer = findFirstEscherContainerRecordOfType((short)0xf003, escherContainerF002); - final EscherContainerRecord[] escherContainersF004 = findAllEscherContainerRecordOfType((short)0xf004, SpgrContainer); - for (EscherContainerRecord spContainer : escherContainersF004) { + final EscherContainerRecord spgrContainer = findFirstEscherContainerRecordOfType((short)RecordTypes.EscherSpgrContainer, dgContainer); + final EscherContainerRecord[] spContainers = findAllEscherContainerRecordOfType((short)RecordTypes.EscherSpContainer, spgrContainer); + for (EscherContainerRecord spContainer : spContainers) { StyleTextProp9Atom nineAtom = findInSpContainer(spContainer); - EscherSpRecord sp = null; - final EscherRecord escherContainerF00A = findFirstEscherRecordOfType((short)0xf00a, spContainer); - if (null != escherContainerF00A) { - if (escherContainerF00A instanceof EscherSpRecord) { - sp = (EscherSpRecord) escherContainerF00A; - } - } - final EscherRecord escherContainerF00D = findFirstEscherRecordOfType((short)0xf00d, spContainer); - if (null == escherContainerF00D) { continue; } - if (escherContainerF00D instanceof EscherTextboxRecord) { - EscherTextboxRecord tbr = (EscherTextboxRecord) escherContainerF00D; - EscherTextboxWrapper w = new EscherTextboxWrapper(tbr); - w.setStyleTextProp9Atom(nineAtom); - if (null != sp) { - w.setShapeId(sp.getShapeId()); - } - found.add(w); + EscherSpRecord sp = (EscherSpRecord)findFirstEscherRecordOfType((short)RecordTypes.EscherSp, spContainer); + EscherTextboxRecord clientTextbox = (EscherTextboxRecord)findFirstEscherRecordOfType((short)RecordTypes.EscherClientTextbox, spContainer); + if (null == clientTextbox) { continue; } + + EscherTextboxWrapper w = new EscherTextboxWrapper(clientTextbox); + w.setStyleTextProp9Atom(nineAtom); + if (null != sp) { + w.setShapeId(sp.getShapeId()); } + found.add(w); } return found.toArray(new EscherTextboxWrapper[found.size()]); } + private StyleTextProp9Atom findInSpContainer(final EscherContainerRecord spContainer) { - final EscherContainerRecord escherContainerF011 = findFirstEscherContainerRecordOfType((short)0xf011, spContainer); - if (null == escherContainerF011) { return null; } - final EscherContainerRecord escherContainer1388 = findFirstEscherContainerRecordOfType((short)0x1388, escherContainerF011); + EscherContainerRecord clientData = findFirstEscherContainerRecordOfType((short)RecordTypes.EscherClientData, spContainer); + if (null == clientData) { return null; } + final EscherContainerRecord escherContainer1388 = findFirstEscherContainerRecordOfType((short)0x1388, clientData); if (null == escherContainer1388) { return null; } final EscherContainerRecord escherContainer138A = findFirstEscherContainerRecordOfType((short)0x138A, escherContainer1388); if (null == escherContainer138A) { return null; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java index be732d44ca..91a590afe8 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/RecordContainer.java @@ -294,7 +294,7 @@ public abstract class RecordContainer extends Record // Write out our header, less the size mout.write(new byte[] {headerA,headerB}); byte[] typeB = new byte[2]; - LittleEndian.putShort(typeB,(short)type); + LittleEndian.putShort(typeB, 0, (short)type); mout.write(typeB); mout.write(new byte[4]); @@ -320,7 +320,7 @@ public abstract class RecordContainer extends Record // Write out our header, less the size baos.write(new byte[] {headerA,headerB}); byte[] typeB = new byte[2]; - LittleEndian.putShort(typeB,(short)type); + LittleEndian.putShort(typeB,0,(short)type); baos.write(typeB); baos.write(new byte[] {0,0,0,0}); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java index 315455d90c..195b59c08f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideListWithText.java @@ -93,8 +93,6 @@ public final class SlideListWithText extends RecordContainer { } int clen = endPos - i - 1; - boolean emptySet = false; - if(clen == 0) { emptySet = true; } // Create a SlideAtomsSets, not caring if they're empty //if(emptySet) { continue; } @@ -149,7 +147,7 @@ public final class SlideListWithText extends RecordContainer { } public void setInstance(int inst){ - LittleEndian.putShort(_header, (short)((inst << 4) | 0xF)); + LittleEndian.putShort(_header, 0, (short)((inst << 4) | 0xF)); } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java index 9b74711fc6..09d97a3b74 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFFill.java @@ -128,9 +128,10 @@ public final class HSLFFill { }; } case FILL_PICTURE: { + final HSLFPictureData pd = getPictureData(); + if (pd == null) break; + return new TexturePaint() { - final HSLFPictureData pd = getPictureData(); - public InputStream getImageData() { return new ByteArrayInputStream(pd.getData()); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java index 5e47776d6e..5133ff1229 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFGroupShape.java @@ -21,8 +21,7 @@ import java.awt.geom.Rectangle2D; import java.util.*; import org.apache.poi.ddf.*; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.sl.usermodel.ShapeType; +import org.apache.poi.sl.usermodel.*; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.POILogger; @@ -31,7 +30,7 @@ import org.apache.poi.util.POILogger; * * @author Yegor Kozlov */ -public class HSLFGroupShape extends HSLFShape implements ShapeContainer { +public class HSLFGroupShape extends HSLFShape implements GroupShape { /** * Create a new ShapeGroup. This constructor is used when a new shape is created. @@ -87,13 +86,8 @@ public class HSLFGroupShape extends HSLFShape implements ShapeContainer { protected static POILogger logger = POILogFactory.getLogger(HSLFNotes.class); - private List> _runs; + private List> _paragraphs = new ArrayList>(); /** * Constructs a Notes Sheet from the given Notes record. @@ -49,13 +49,16 @@ public final class HSLFNotes extends HSLFSheet implements Notes l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { + if (!_paragraphs.contains(l)) _paragraphs.add(l); + } + + if (_paragraphs.isEmpty()) { logger.log(POILogger.WARN, "No text records found for notes sheet"); } // Set the sheet on each TextRun - for (List ltp : _runs) { + for (List ltp : _paragraphs) { for (HSLFTextParagraph tp : ltp) { tp.supplySheet(this); } @@ -67,7 +70,7 @@ public final class HSLFNotes extends HSLFSheet implements Notes> getTextParagraphs() { - return _runs; + return _paragraphs; } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureData.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureData.java index b848a2cb9b..230a6401d2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureData.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureData.java @@ -17,31 +17,22 @@ package org.apache.poi.hslf.usermodel; -import java.awt.Graphics2D; import java.io.IOException; import java.io.OutputStream; import java.security.MessageDigest; -import org.apache.poi.hslf.blip.BitmapPainter; -import org.apache.poi.hslf.blip.DIB; -import org.apache.poi.hslf.blip.EMF; -import org.apache.poi.hslf.blip.ImagePainter; -import org.apache.poi.hslf.blip.JPEG; -import org.apache.poi.hslf.blip.PICT; -import org.apache.poi.hslf.blip.PNG; -import org.apache.poi.hslf.blip.WMF; +import org.apache.poi.hslf.blip.*; import org.apache.poi.poifs.crypt.CryptoFunctions; import org.apache.poi.poifs.crypt.HashAlgorithm; -import org.apache.poi.util.LittleEndian; -import org.apache.poi.util.POILogFactory; -import org.apache.poi.util.POILogger; +import org.apache.poi.sl.usermodel.PictureData; +import org.apache.poi.util.*; /** * A class that represents image data contained in a slide show. * * @author Yegor Kozlov */ -public abstract class HSLFPictureData { +public abstract class HSLFPictureData implements PictureData { protected POILogger logger = POILogFactory.getLogger(this.getClass()); @@ -91,13 +82,6 @@ public abstract class HSLFPictureData { */ protected abstract int getSignature(); - protected static final ImagePainter[] painters = new ImagePainter[8]; - static { - HSLFPictureData.setImagePainter(HSLFPictureShape.PNG, new BitmapPainter()); - HSLFPictureData.setImagePainter(HSLFPictureShape.JPEG, new BitmapPainter()); - HSLFPictureData.setImagePainter(HSLFPictureShape.DIB, new BitmapPainter()); - } - /** * Returns the raw binary data of this Picture excluding the first 8 bytes * which hold image signature and size of the image data. @@ -233,31 +217,4 @@ public abstract class HSLFPictureData { public int getSize(){ return getData().length; } - - public void draw(Graphics2D graphics, HSLFPictureShape parent){ - ImagePainter painter = painters[getType()]; - if(painter != null) painter.paint(graphics, this, parent); - else logger.log(POILogger.WARN, "Rendering is not supported: " + getClass().getName()); - } - - /** - * Register ImagePainter for the specified image type - * - * @param type image type, must be one of the static constants defined in the Picture class. - * @param painter - */ - public static void setImagePainter(int type, ImagePainter painter){ - painters[type] = painter; - } - - /** - * Return ImagePainter for the specified image type - * - * @param type blip type, must be one of the static constants defined in the Picture class. - * @return ImagePainter for the specified image type - */ - public static ImagePainter getImagePainter(int type){ - return painters[type]; - } - } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureShape.java index c97c281680..03a9861ae6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFPictureShape.java @@ -28,8 +28,7 @@ import javax.imageio.ImageIO; import org.apache.poi.ddf.*; import org.apache.poi.hslf.blip.Bitmap; import org.apache.poi.hslf.record.Document; -import org.apache.poi.sl.usermodel.ShapeContainer; -import org.apache.poi.sl.usermodel.ShapeType; +import org.apache.poi.sl.usermodel.*; import org.apache.poi.util.*; @@ -38,7 +37,7 @@ import org.apache.poi.util.*; * * @author Yegor Kozlov */ -public class HSLFPictureShape extends HSLFSimpleShape { +public class HSLFPictureShape extends HSLFSimpleShape implements PictureShape { /** * Windows Enhanced Metafile (EMF) @@ -165,11 +164,7 @@ public class HSLFPictureShape extends HSLFSimpleShape { } } - /** - * Returns the picture data for this picture. - * - * @return the picture data for this picture. - */ + @Override public HSLFPictureData getPictureData(){ HSLFSlideShow ppt = getSheet().getSlideShow(); HSLFPictureData[] pict = ppt.getPictureData(); @@ -247,13 +242,11 @@ public class HSLFPictureShape extends HSLFSimpleShape { } /** - * Returns the clipping values as percent ratio relatively to the image size. * The anchor specified by {@link #getLogicalAnchor2D()} is the displayed size, * i.e. the size of the already clipped image - * - * @return the clipping as insets converted/scaled to 100000 (=100%) */ - public Insets getBlipClip() { + @Override + public Insets getClipping() { EscherOptRecord opt = getEscherOptRecord(); double top = getFractProp(opt, EscherProperties.BLIP__CROPFROMTOP); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java index a6be80f92f..a68f3a74e9 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShape.java @@ -165,14 +165,12 @@ public abstract class HSLFShape implements Shape { public Rectangle2D getAnchor2D(){ EscherSpRecord spRecord = getEscherChild(EscherSpRecord.RECORD_ID); int flags = spRecord.getFlags(); - Rectangle2D anchor=null; + Rectangle2D anchor; if ((flags & EscherSpRecord.FLAG_CHILD) != 0){ EscherChildAnchorRecord rec = getEscherChild(EscherChildAnchorRecord.RECORD_ID); - anchor = new java.awt.Rectangle(); if(rec == null){ logger.log(POILogger.WARN, "EscherSpRecord.FLAG_CHILD is set but EscherChildAnchorRecord was not found"); EscherClientAnchorRecord clrec = getEscherChild(EscherClientAnchorRecord.RECORD_ID); - anchor = new java.awt.Rectangle(); anchor = new Rectangle2D.Float( (float)clrec.getCol1()*POINT_DPI/MASTER_DPI, (float)clrec.getFlag()*POINT_DPI/MASTER_DPI, @@ -187,10 +185,8 @@ public abstract class HSLFShape implements Shape { (float)(rec.getDy2()-rec.getDy1())*POINT_DPI/MASTER_DPI ); } - } - else { + } else { EscherClientAnchorRecord rec = getEscherChild(EscherClientAnchorRecord.RECORD_ID); - anchor = new java.awt.Rectangle(); anchor = new Rectangle2D.Float( (float)rec.getCol1()*POINT_DPI/MASTER_DPI, (float)rec.getFlag()*POINT_DPI/MASTER_DPI, @@ -201,10 +197,6 @@ public abstract class HSLFShape implements Shape { return anchor; } - public Rectangle2D getLogicalAnchor2D(){ - return getAnchor2D(); - } - /** * Sets the anchor (the bounding box rectangle) of this shape. * All coordinates should be expressed in points (72 dpi). @@ -262,8 +254,9 @@ public abstract class HSLFShape implements Shape { * @return escher property or null if not found. */ public static T getEscherProperty(EscherOptRecord opt, int propId){ - return opt.lookup(propId); - } + if (opt == null) return null; + return opt.lookup(propId); + } /** * Set an escher property for this shape. @@ -475,15 +468,6 @@ public abstract class HSLFShape implements Shape { logger.log(POILogger.INFO, "Rendering " + getShapeName()); } - /** - * Return shape outline as a java.awt.Shape object - * - * @return the shape outline - */ - public java.awt.Shape getOutline(){ - return getLogicalAnchor2D(); - } - public EscherOptRecord getEscherOptRecord() { EscherOptRecord opt = getEscherChild(EscherOptRecord.RECORD_ID); if (opt == null) { @@ -516,8 +500,7 @@ public abstract class HSLFShape implements Shape { public double getRotation(){ int rot = getEscherProperty(EscherProperties.TRANSFORM__ROTATION); - double angle = Units.fixedPointToDouble(rot) % 360.0; - return angle; + return Units.fixedPointToDouble(rot); } public void setRotation(double theta){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java index 0f9dd2815e..9f715fb389 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFShapeFactory.java @@ -69,7 +69,7 @@ public final class HSLFShapeFactory { List props = f.createProperties( opt.serialize(), 8, opt.getInstance() ); EscherSimpleProperty p = (EscherSimpleProperty)props.get(0); if(p.getPropertyNumber() == 0x39F && p.getPropertyValue() == 1){ - group = new Table(spContainer, parent); + group = new HSLFTable(spContainer, parent); } else { group = new HSLFGroupShape(spContainer, parent); } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java index 547e095efb..0247a75936 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java @@ -22,6 +22,8 @@ import java.util.*; import org.apache.poi.ddf.*; import org.apache.poi.hslf.record.*; +import org.apache.poi.sl.draw.DrawFactory; +import org.apache.poi.sl.draw.Drawable; import org.apache.poi.sl.usermodel.Sheet; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; @@ -288,8 +290,11 @@ public abstract class HSLFSheet implements Sheet { return _background; } - public void draw(Graphics2D graphics){ - + @Override + public void draw(Graphics2D graphics) { + DrawFactory drawFact = DrawFactory.getInstance(graphics); + Drawable draw = drawFact.getDrawable(this); + draw.draw(graphics); } /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java index 6c053d49f4..5ddf311e52 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSimpleShape.java @@ -18,16 +18,15 @@ package org.apache.poi.hslf.usermodel; import java.awt.Color; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; import java.io.ByteArrayOutputStream; -import java.util.ArrayList; import org.apache.poi.ddf.*; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.record.*; +import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.draw.geom.*; import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound; import org.apache.poi.sl.usermodel.StrokeStyle.LineDash; import org.apache.poi.util.LittleEndian; @@ -196,7 +195,7 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape { public StrokeStyle getStrokeStyle(){ return new StrokeStyle() { public PaintStyle getPaint() { - return null; + return DrawPaint.createSolidPaint(HSLFSimpleShape.this.getLineColor()); } public LineCap getLineCap() { @@ -204,15 +203,15 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape { } public LineDash getLineDash() { - return null; + return HSLFSimpleShape.this.getLineDashing(); } public LineCompound getLineCompound() { - return null; + return HSLFSimpleShape.this.getLineCompound(); } public double getLineWidth() { - return 0; + return HSLFSimpleShape.this.getLineWidth(); } }; @@ -234,61 +233,6 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape { getFill().setForegroundColor(color); } - /** - * - * @return 'absolute' anchor of this shape relative to the parent sheet - */ - public Rectangle2D getLogicalAnchor2D(){ - Rectangle2D anchor = getAnchor2D(); - - //if it is a groupped shape see if we need to transform the coordinates - if (getParent() != null){ - ArrayList lst = new ArrayList(); - for (ShapeContainer parent=this.getParent(); - parent instanceof HSLFGroupShape; - parent = ((HSLFGroupShape)parent).getParent()) { - lst.add(0, (HSLFGroupShape)parent); - } - - AffineTransform tx = new AffineTransform(); - for(HSLFGroupShape prnt : lst) { - Rectangle2D exterior = prnt.getAnchor2D(); - Rectangle2D interior = prnt.getCoordinates(); - - double scaleX = exterior.getWidth() / interior.getWidth(); - double scaleY = exterior.getHeight() / interior.getHeight(); - - tx.translate(exterior.getX(), exterior.getY()); - tx.scale(scaleX, scaleY); - tx.translate(-interior.getX(), -interior.getY()); - - } - anchor = tx.createTransformedShape(anchor).getBounds2D(); - } - - double angle = getRotation(); - if(angle != 0.){ - double centerX = anchor.getX() + anchor.getWidth()/2; - double centerY = anchor.getY() + anchor.getHeight()/2; - - AffineTransform trans = new AffineTransform(); - trans.translate(centerX, centerY); - trans.rotate(Math.toRadians(angle)); - trans.translate(-centerX, -centerY); - - Rectangle2D rect = trans.createTransformedShape(anchor).getBounds2D(); - if((anchor.getWidth() < anchor.getHeight() && rect.getWidth() > rect.getHeight()) || - (anchor.getWidth() > anchor.getHeight() && rect.getWidth() < rect.getHeight()) ){ - trans = new AffineTransform(); - trans.translate(centerX, centerY); - trans.rotate(Math.PI/2); - trans.translate(-centerX, -centerY); - anchor = trans.createTransformedShape(anchor).getBounds2D(); - } - } - return anchor; - } - /** * Find a record in the underlying EscherClientDataRecord * @@ -424,11 +368,6 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape { return (adjval == -1) ? null : new Guide(name, "val "+adjval); } - public LineDecoration getLineDecoration() { - // TODO Auto-generated method stub - return null; - } - public CustomGeometry getGeometry() { ShapeType st = getShapeType(); String name = st.getOoxmlName(); @@ -442,11 +381,89 @@ public abstract class HSLFSimpleShape extends HSLFShape implements SimpleShape { return geom; } - public Shadow getShadow() { - // TODO Auto-generated method stub - return null; - } + public double getShadowAngle() { + EscherOptRecord opt = getEscherOptRecord(); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETX); + int offX = (prop == null) ? 0 : prop.getPropertyValue(); + prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETY); + int offY = (prop == null) ? 0 : prop.getPropertyValue(); + return Math.toDegrees(Math.atan2(offY, offX)); + } + public double getShadowDistance() { + EscherOptRecord opt = getEscherOptRecord(); + EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETX); + int offX = (prop == null) ? 0 : prop.getPropertyValue(); + prop = getEscherProperty(opt, EscherProperties.SHADOWSTYLE__OFFSETY); + int offY = (prop == null) ? 0 : prop.getPropertyValue(); + return Units.toPoints((long)Math.hypot(offX, offY)); + } + + /** + * @return color of the line. If color is not set returns java.awt.Color.black + */ + public Color getShadowColor(){ + Color clr = getColor(EscherProperties.SHADOWSTYLE__COLOR, EscherProperties.SHADOWSTYLE__OPACITY, -1); + return clr == null ? Color.black : clr; + } + public Shadow getShadow() { + EscherOptRecord opt = getEscherOptRecord(); + EscherProperty shadowType = opt.lookup(EscherProperties.SHADOWSTYLE__TYPE); + if (shadowType == null) return null; + + return new Shadow(){ + public SimpleShape getShadowParent() { + return HSLFSimpleShape.this; + } + + public double getDistance() { + return getShadowDistance(); + } + + public double getAngle() { + return getShadowAngle(); + } + + public double getBlur() { + // TODO Auto-generated method stub + return 0; + } + + public SolidPaint getFillStyle() { + return DrawPaint.createSolidPaint(getShadowColor()); + } + + }; + } + + public LineDecoration getLineDecoration() { + return new LineDecoration() { + + public DecorationShape getHeadShape() { + return DecorationShape.NONE; + } + + public DecorationSize getHeadWidth() { + return DecorationSize.MEDIUM; + } + + public DecorationSize getHeadLength() { + return DecorationSize.MEDIUM; + } + + public DecorationShape getTailShape() { + return DecorationShape.NONE; + } + + public DecorationSize getTailWidth() { + return DecorationSize.MEDIUM; + } + + public DecorationSize getTailLength() { + return DecorationSize.MEDIUM; + } + }; + } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java index 048655fa64..e238751038 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlide.java @@ -72,7 +72,9 @@ public final class HSLFSlide extends HSLFSheet implements Slide l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { + if (!_paragraphs.contains(l)) _paragraphs.add(l); + } for(List ltp : _paragraphs) { for (HSLFTextParagraph tp : ltp) { diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java index 1c6518e5e0..a6dfedf073 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSlideMaster.java @@ -32,7 +32,7 @@ import org.apache.poi.hslf.record.*; * @author Yegor Kozlov */ public final class HSLFSlideMaster extends HSLFMasterSheet { - private final List> _runs = new ArrayList>(); + private final List> _paragraphs = new ArrayList>(); /** * all TxMasterStyleAtoms available in this master @@ -46,8 +46,11 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { public HSLFSlideMaster(MainMaster record, int sheetNo) { super(record, sheetNo); - _runs.addAll(HSLFTextParagraph.findTextParagraphs(getPPDrawing())); - for (List p : _runs) { + for (List l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { + if (!_paragraphs.contains(l)) _paragraphs.add(l); + } + + for (List p : _paragraphs) { for (HSLFTextParagraph htp : p) { htp.supplySheet(this); } @@ -58,7 +61,7 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { * Returns an array of all the TextRuns found */ public List> getTextParagraphs() { - return _runs; + return _paragraphs; } /** @@ -138,7 +141,7 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { protected void onAddTextShape(HSLFTextShape shape) { List runs = shape.getTextParagraphs(); - _runs.add(runs); + _paragraphs.add(runs); } public TxMasterStyleAtom[] getTxMasterStyleAtoms(){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java new file mode 100644 index 0000000000..b028758e6f --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java @@ -0,0 +1,346 @@ +/* ==================================================================== + 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.usermodel; + +import org.apache.poi.ddf.*; +import org.apache.poi.hslf.model.Line; +import org.apache.poi.hslf.usermodel.*; +import org.apache.poi.sl.usermodel.ShapeContainer; +import org.apache.poi.util.LittleEndian; + +import java.util.*; +import java.util.List; +import java.awt.*; + +/** + * Represents a table in a PowerPoint presentation + * + * @author Yegor Kozlov + */ +public final class HSLFTable extends HSLFGroupShape { + + protected static final int BORDER_TOP = 1; + protected static final int BORDER_RIGHT = 2; + protected static final int BORDER_BOTTOM = 3; + protected static final int BORDER_LEFT = 4; + + protected static final int BORDERS_ALL = 5; + protected static final int BORDERS_OUTSIDE = 6; + protected static final int BORDERS_INSIDE = 7; + protected static final int BORDERS_NONE = 8; + + + protected HSLFTableCell[][] cells; + + /** + * Create a new Table of the given number of rows and columns + * + * @param numrows the number of rows + * @param numcols the number of columns + */ + public HSLFTable(int numrows, int numcols) { + super(); + + if(numrows < 1) throw new IllegalArgumentException("The number of rows must be greater than 1"); + if(numcols < 1) throw new IllegalArgumentException("The number of columns must be greater than 1"); + + int x=0, y=0, tblWidth=0, tblHeight=0; + cells = new HSLFTableCell[numrows][numcols]; + for (int i = 0; i < cells.length; i++) { + x = 0; + for (int j = 0; j < cells[i].length; j++) { + cells[i][j] = new HSLFTableCell(this); + Rectangle anchor = new Rectangle(x, y, HSLFTableCell.DEFAULT_WIDTH, HSLFTableCell.DEFAULT_HEIGHT); + cells[i][j].setAnchor(anchor); + x += HSLFTableCell.DEFAULT_WIDTH; + } + y += HSLFTableCell.DEFAULT_HEIGHT; + } + tblWidth = x; + tblHeight = y; + setAnchor(new Rectangle(0, 0, tblWidth, tblHeight)); + + EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0); + EscherOptRecord opt = new EscherOptRecord(); + opt.setRecordId((short)0xF122); + opt.addEscherProperty(new EscherSimpleProperty((short)0x39F, 1)); + EscherArrayProperty p = new EscherArrayProperty((short)(0x4000 | 0x3A0), false, null); + p.setSizeOfElements(0x0004); + p.setNumberOfElementsInArray(numrows); + p.setNumberOfElementsInMemory(numrows); + opt.addEscherProperty(p); + List lst = spCont.getChildRecords(); + lst.add(lst.size()-1, opt); + spCont.setChildRecords(lst); + } + + /** + * Create a Table 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 + */ + public HSLFTable(EscherContainerRecord escherRecord, ShapeContainer parent) { + super(escherRecord, parent); + } + + /** + * Gets a cell + * + * @param row the row index (0-based) + * @param col the column index (0-based) + * @return the cell + */ + public HSLFTableCell getCell(int row, int col) { + return cells[row][col]; + } + + public int getNumberOfColumns() { + return cells[0].length; + } + public int getNumberOfRows() { + return cells.length; + } + + protected void afterInsert(HSLFSheet sh){ + super.afterInsert(sh); + + EscherContainerRecord spCont = (EscherContainerRecord) getSpContainer().getChild(0); + List lst = spCont.getChildRecords(); + EscherOptRecord opt = (EscherOptRecord)lst.get(lst.size()-2); + EscherArrayProperty p = opt.lookup(0x3A0); + for (int i = 0; i < cells.length; i++) { + HSLFTableCell cell = cells[i][0]; + int rowHeight = cell.getAnchor().height*MASTER_DPI/POINT_DPI; + byte[] val = new byte[4]; + LittleEndian.putInt(val, 0, rowHeight); + p.setElement(i, val); + for (int j = 0; j < cells[i].length; j++) { + HSLFTableCell c = cells[i][j]; + addShape(c); + + Line bt = c.getBorderTop(); + if(bt != null) addShape(bt); + + Line br = c.getBorderRight(); + if(br != null) addShape(br); + + Line bb = c.getBorderBottom(); + if(bb != null) addShape(bb); + + Line bl = c.getBorderLeft(); + if(bl != null) addShape(bl); + + } + } + + } + + protected void initTable(){ + List shapeList = getShapeList(); + Collections.sort(shapeList, new Comparator(){ + public int compare( HSLFShape o1, HSLFShape o2 ) { + Rectangle anchor1 = o1.getAnchor(); + Rectangle anchor2 = o2.getAnchor(); + int delta = anchor1.y - anchor2.y; + if(delta == 0) delta = anchor1.x - anchor2.x; + return delta; + } + }); + int y0 = -1; + int maxrowlen = 0; + List> lst = new ArrayList>(); + List row = null; + for (HSLFShape sh : shapeList) { + if(sh instanceof HSLFTextShape){ + Rectangle anchor = sh.getAnchor(); + if(anchor.y != y0){ + y0 = anchor.y; + row = new ArrayList(); + lst.add(row); + } + row.add(sh); + maxrowlen = Math.max(maxrowlen, row.size()); + } + } + cells = new HSLFTableCell[lst.size()][maxrowlen]; + for (int i = 0; i < lst.size(); i++) { + row = lst.get(i); + for (int j = 0; j < row.size(); j++) { + HSLFTextShape tx = (HSLFTextShape)row.get(j); + cells[i][j] = new HSLFTableCell(tx.getSpContainer(), getParent()); + cells[i][j].setSheet(tx.getSheet()); + } + } + } + + /** + * Assign the SlideShow this shape belongs to + * + * @param sheet owner of this shape + */ + public void setSheet(HSLFSheet sheet){ + super.setSheet(sheet); + if(cells == null) initTable(); + } + + /** + * Sets the row height. + * + * @param row the row index (0-based) + * @param height the height to set (in pixels) + */ + public void setRowHeight(int row, int height){ + int currentHeight = cells[row][0].getAnchor().height; + int dy = height - currentHeight; + + for (int i = row; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + Rectangle anchor = cells[i][j].getAnchor(); + if(i == row) anchor.height = height; + else anchor.y += dy; + cells[i][j].setAnchor(anchor); + } + } + Rectangle tblanchor = getAnchor(); + tblanchor.height += dy; + setAnchor(tblanchor); + + } + + /** + * Sets the column width. + * + * @param col the column index (0-based) + * @param width the width to set (in pixels) + */ + public void setColumnWidth(int col, int width){ + int currentWidth = cells[0][col].getAnchor().width; + int dx = width - currentWidth; + for (int i = 0; i < cells.length; i++) { + Rectangle anchor = cells[i][col].getAnchor(); + anchor.width = width; + cells[i][col].setAnchor(anchor); + + if(col < cells[i].length - 1) for (int j = col+1; j < cells[i].length; j++) { + anchor = cells[i][j].getAnchor(); + anchor.x += dx; + cells[i][j].setAnchor(anchor); + } + } + Rectangle tblanchor = getAnchor(); + tblanchor.width += dx; + setAnchor(tblanchor); + } + + /** + * Format the table and apply the specified Line to all cell boundaries, + * both outside and inside + * + * @param line the border line + */ + public void setAllBorders(Line line){ + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + HSLFTableCell cell = cells[i][j]; + cell.setBorderTop(cloneBorder(line)); + cell.setBorderLeft(cloneBorder(line)); + if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); + if(i == cells.length - 1) cell.setBorderBottom(cloneBorder(line)); + } + } + } + + /** + * Format the outside border using the specified Line object + * + * @param line the border line + */ + public void setOutsideBorders(Line line){ + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + HSLFTableCell cell = cells[i][j]; + + if(j == 0) cell.setBorderLeft(cloneBorder(line)); + if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); + else { + cell.setBorderLeft(null); + cell.setBorderLeft(null); + } + + if(i == 0) cell.setBorderTop(cloneBorder(line)); + else if(i == cells.length - 1) cell.setBorderBottom(cloneBorder(line)); + else { + cell.setBorderTop(null); + cell.setBorderBottom(null); + } + } + } + } + + /** + * Format the inside border using the specified Line object + * + * @param line the border line + */ + public void setInsideBorders(Line line){ + for (int i = 0; i < cells.length; i++) { + for (int j = 0; j < cells[i].length; j++) { + HSLFTableCell cell = cells[i][j]; + + if(j != cells[i].length - 1) + cell.setBorderRight(cloneBorder(line)); + else { + cell.setBorderLeft(null); + cell.setBorderLeft(null); + } + if(i != cells.length - 1) cell.setBorderBottom(cloneBorder(line)); + else { + cell.setBorderTop(null); + cell.setBorderBottom(null); + } + } + } + } + + private Line cloneBorder(Line line){ + Line border = createBorder(); + border.setLineWidth(line.getLineWidth()); + border.setLineDashing(line.getLineDashing()); + border.setLineColor(line.getLineColor()); + border.setLineCompound(line.getLineCompound()); + return border; + } + + /** + * Create a border to format this table + * + * @return the created border + */ + public Line createBorder(){ + Line line = new Line(this); + + EscherOptRecord opt = getEscherOptRecord(); + setEscherProperty(opt, EscherProperties.GEOMETRY__SHAPEPATH, -1); + setEscherProperty(opt, EscherProperties.GEOMETRY__FILLOK, -1); + setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000); + setEscherProperty(opt, EscherProperties.THREED__LIGHTFACE, 0x80000); + + return line; + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java new file mode 100644 index 0000000000..b1e7c3e4bb --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java @@ -0,0 +1,157 @@ +/* ==================================================================== + 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.usermodel; + +import java.awt.Rectangle; + +import org.apache.poi.ddf.EscherContainerRecord; +import org.apache.poi.ddf.EscherOptRecord; +import org.apache.poi.ddf.EscherProperties; +import org.apache.poi.hslf.model.Line; +import org.apache.poi.sl.usermodel.ShapeContainer; +import org.apache.poi.sl.usermodel.ShapeType; + +/** + * Represents a cell in a ppt table + * + * @author Yegor Kozlov + */ +public final class HSLFTableCell extends HSLFTextBox { + protected static final int DEFAULT_WIDTH = 100; + protected static final int DEFAULT_HEIGHT = 40; + + private Line borderLeft; + private Line borderRight; + private Line borderTop; + private Line borderBottom; + + /** + * Create a TableCell object and initialize it from the supplied Record container. + * + * @param escherRecord {@link EscherSpContainer} container which holds information about this shape + * @param parent the parent of the shape + */ + protected HSLFTableCell(EscherContainerRecord escherRecord, ShapeContainer parent){ + super(escherRecord, parent); + } + + /** + * Create a new TableCell. 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 HSLFTableCell(ShapeContainer parent){ + super(parent); + + setShapeType(ShapeType.RECT); + //_txtrun.setRunType(TextHeaderAtom.HALF_BODY_TYPE); + //_txtrun.getRichTextRuns()[0].setFlag(false, 0, false); + } + + protected EscherContainerRecord createSpContainer(boolean isChild){ + _escherContainer = super.createSpContainer(isChild); + EscherOptRecord opt = getEscherOptRecord(); + setEscherProperty(opt, EscherProperties.TEXT__TEXTID, 0); + setEscherProperty(opt, EscherProperties.TEXT__SIZE_TEXT_TO_FIT_SHAPE, 0x20000); + setEscherProperty(opt, EscherProperties.FILL__NOFILLHITTEST, 0x150001); + setEscherProperty(opt, EscherProperties.SHADOWSTYLE__SHADOWOBSURED, 0x20000); + setEscherProperty(opt, EscherProperties.PROTECTION__LOCKAGAINSTGROUPING, 0x40000); + + return _escherContainer; + } + + protected void anchorBorder(int type, Line line){ + Rectangle cellAnchor = getAnchor(); + Rectangle lineAnchor = new Rectangle(); + switch(type){ + case HSLFTable.BORDER_TOP: + lineAnchor.x = cellAnchor.x; + lineAnchor.y = cellAnchor.y; + lineAnchor.width = cellAnchor.width; + lineAnchor.height = 0; + break; + case HSLFTable.BORDER_RIGHT: + lineAnchor.x = cellAnchor.x + cellAnchor.width; + lineAnchor.y = cellAnchor.y; + lineAnchor.width = 0; + lineAnchor.height = cellAnchor.height; + break; + case HSLFTable.BORDER_BOTTOM: + lineAnchor.x = cellAnchor.x; + lineAnchor.y = cellAnchor.y + cellAnchor.height; + lineAnchor.width = cellAnchor.width; + lineAnchor.height = 0; + break; + case HSLFTable.BORDER_LEFT: + lineAnchor.x = cellAnchor.x; + lineAnchor.y = cellAnchor.y; + lineAnchor.width = 0; + lineAnchor.height = cellAnchor.height; + break; + default: + throw new IllegalArgumentException("Unknown border type: " + type); + } + line.setAnchor(lineAnchor); + } + + public Line getBorderLeft() { + return borderLeft; + } + + public void setBorderLeft(Line line) { + if(line != null) anchorBorder(HSLFTable.BORDER_LEFT, line); + this.borderLeft = line; + } + + public Line getBorderRight() { + return borderRight; + } + + public void setBorderRight(Line line) { + if(line != null) anchorBorder(HSLFTable.BORDER_RIGHT, line); + this.borderRight = line; + } + + public Line getBorderTop() { + return borderTop; + } + + public void setBorderTop(Line line) { + if(line != null) anchorBorder(HSLFTable.BORDER_TOP, line); + this.borderTop = line; + } + + public Line getBorderBottom() { + return borderBottom; + } + + public void setBorderBottom(Line line) { + if(line != null) anchorBorder(HSLFTable.BORDER_BOTTOM, line); + this.borderBottom = line; + } + + public void setAnchor(Rectangle anchor){ + super.setAnchor(anchor); + + if(borderTop != null) anchorBorder(HSLFTable.BORDER_TOP, borderTop); + if(borderRight != null) anchorBorder(HSLFTable.BORDER_RIGHT, borderRight); + if(borderBottom != null) anchorBorder(HSLFTable.BORDER_BOTTOM, borderBottom); + if(borderLeft != null) anchorBorder(HSLFTable.BORDER_LEFT, borderLeft); + } +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java index ca6ddf6e80..f80bd3fc92 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -17,7 +17,10 @@ package org.apache.poi.hslf.usermodel; +import static org.apache.poi.hslf.record.RecordTypes.OutlineTextRefAtom; + import java.awt.Color; +import java.io.IOException; import java.util.*; import org.apache.poi.hslf.model.PPFont; @@ -52,8 +55,7 @@ public final class HSLFTextParagraph implements TextParagraph { private final TextHeaderAtom _headerAtom; private TextBytesAtom _byteAtom; private TextCharsAtom _charAtom; - private StyleTextPropAtom _styleAtom; - private TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); + private final TextPropCollection _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); protected TextRulerAtom _ruler; protected List _runs = new ArrayList(); @@ -61,11 +63,6 @@ public final class HSLFTextParagraph implements TextParagraph { private HSLFSheet _sheet; private int shapeId; - /** - * all text run records that follow TextHeaderAtom. - * (there can be misc InteractiveInfo, TxInteractiveInfo and other records) - */ - private Record[] _records; // private StyleTextPropAtom styleTextPropAtom; private StyleTextProp9Atom styleTextProp9Atom; @@ -76,32 +73,30 @@ public final class HSLFTextParagraph implements TextParagraph { * @param tha the TextHeaderAtom that defines what's what * @param tba the TextBytesAtom containing the text or null if {@link TextCharsAtom} is provided * @param tca the TextCharsAtom containing the text or null if {@link TextBytesAtom} is provided - * @param sta the StyleTextPropAtom which defines the character stylings */ /* package */ HSLFTextParagraph( TextHeaderAtom tha, TextBytesAtom tba, - TextCharsAtom tca, - StyleTextPropAtom sta + TextCharsAtom tca ) { + if (tha == null) { + throw new IllegalArgumentException("TextHeaderAtom must be set."); + } _headerAtom = tha; - _styleAtom = sta; _byteAtom = tba; _charAtom = tca; } /* package */ HSLFTextParagraph(HSLFTextParagraph other) { - _headerAtom = other._headerAtom; - _styleAtom = other._styleAtom; - _byteAtom = other._byteAtom; - _charAtom = other._charAtom; - _paragraphStyle = other._paragraphStyle; - _parentShape = other._parentShape; - _sheet = other._sheet; - _ruler = other._ruler; // ???? - shapeId = other.shapeId; - _records = other._records; - } + _headerAtom = other._headerAtom; + _byteAtom = other._byteAtom; + _charAtom = other._charAtom; + _parentShape = other._parentShape; + _sheet = other._sheet; + _ruler = other._ruler; + shapeId = other.shapeId; + _paragraphStyle.copy(other._paragraphStyle); + } public void addTextRun(HSLFTextRun run) { _runs.add(run); @@ -120,7 +115,7 @@ public final class HSLFTextParagraph implements TextParagraph { } public void setParagraphStyle(TextPropCollection paragraphStyle) { - _paragraphStyle = paragraphStyle; + _paragraphStyle.copy(paragraphStyle); } /** @@ -196,20 +191,52 @@ public final class HSLFTextParagraph implements TextParagraph { _ruler = getTextRuler(); if (_ruler == null) { _ruler = TextRulerAtom.getParagraphInstance(); - _headerAtom.getParentRecord().appendChildRecord(_ruler); + Record childAfter = _byteAtom; + if (childAfter == null) childAfter = _charAtom; + if (childAfter == null) childAfter = _headerAtom; + _headerAtom.getParentRecord().addChildAfter(_ruler, childAfter); } return _ruler; } /** - * Returns records that make up this text run + * Returns records that make up the list of text paragraphs + * (there can be misc InteractiveInfo, TxInteractiveInfo and other records) * * @return text run records */ public Record[] getRecords(){ - return _records; + Record r[] = _headerAtom.getParentRecord().getChildRecords(); + return getRecords(r, new int[]{0}, _headerAtom); } + private static Record[] getRecords(Record[] records, int[] startIdx, TextHeaderAtom headerAtom) { + if (records == null) { + throw new NullPointerException("records need to be set."); + } + + for (; startIdx[0] < records.length; startIdx[0]++) { + Record r = records[startIdx[0]]; + if (r instanceof TextHeaderAtom && (headerAtom == null || r == headerAtom)) break; + } + + if (startIdx[0] >= records.length) { + logger.log(POILogger.INFO, "header atom wasn't found - container might contain only an OutlineTextRefAtom"); + return new Record[0]; + } + + int length; + for (length = 1; startIdx[0]+length < records.length; length++) { + if (records[startIdx[0]+length] instanceof TextHeaderAtom) break; + } + + Record result[] = new Record[length]; + System.arraycopy(records, startIdx[0], result, 0, length); + startIdx[0] += length; + + return result; + } + /** Numbered List info */ public void setStyleTextProp9Atom(final StyleTextProp9Atom styleTextProp9Atom) { this.styleTextProp9Atom = styleTextProp9Atom; @@ -220,11 +247,6 @@ public final class HSLFTextParagraph implements TextParagraph { return this.styleTextProp9Atom; } - /** Characters covered */ - public StyleTextPropAtom getStyleTextPropAtom() { - return this._styleAtom; - } - /** * Fetch the value of the given Paragraph related TextProp. * Returns -1 if that TextProp isn't present. @@ -232,14 +254,9 @@ public final class HSLFTextParagraph implements TextParagraph { * Master Sheet will apply. */ private int getParaTextPropVal(String propName) { - TextProp prop = null; - boolean hardAttribute = false; - if (_paragraphStyle != null){ - prop = _paragraphStyle.findByName(propName); - - BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); - hardAttribute = maskProp != null && maskProp.getValue() == 0; - } + TextProp prop = _paragraphStyle.findByName(propName); + BitMaskTextProp maskProp = (BitMaskTextProp)_paragraphStyle.findByName(ParagraphFlagsTextProp.NAME); + boolean hardAttribute = (maskProp != null && maskProp.getValue() == 0); if (prop == null && !hardAttribute){ HSLFSheet sheet = getSheet(); int txtype = getRunType(); @@ -258,11 +275,6 @@ public final class HSLFTextParagraph implements TextParagraph { */ public void setParaTextPropVal(String propName, int val) { // Ensure we have the StyleTextProp atom we're going to need - if(_paragraphStyle == null) { - _styleAtom = findStyleAtomPresent(_headerAtom, -1); - _paragraphStyle = _styleAtom.getParagraphStyles().get(0); - } - assert(_paragraphStyle!=null); TextProp tp = fetchOrAddTextProp(_paragraphStyle, propName); tp.setValue(val); @@ -615,10 +627,7 @@ public final class HSLFTextParagraph implements TextParagraph { protected void setFlag(int index, boolean value) { // Ensure we have the StyleTextProp atom we're going to need - if(_paragraphStyle == null) { - _paragraphStyle = new TextPropCollection(1, TextPropType.paragraph); - } - + assert(_paragraphStyle!=null); BitMaskTextProp prop = (BitMaskTextProp) fetchOrAddTextProp(_paragraphStyle, ParagraphFlagsTextProp.NAME); prop.setSubValue(value,index); } @@ -653,12 +662,13 @@ public final class HSLFTextParagraph implements TextParagraph { boolean afterHeader = false; StyleTextPropAtom style = null; for (Record record : header.getParentRecord().getChildRecords()) { - if (afterHeader && record.getRecordType() == RecordTypes.TextHeaderAtom.typeID) { + long rt = record.getRecordType(); + if (afterHeader && rt == RecordTypes.TextHeaderAtom.typeID) { // already on the next header, quit searching break; } afterHeader |= (header == record); - if (afterHeader && record.getRecordType() == RecordTypes.StyleTextPropAtom.typeID) { + if (afterHeader && rt == RecordTypes.StyleTextPropAtom.typeID) { // found it style = (StyleTextPropAtom)record; } @@ -789,12 +799,20 @@ public final class HSLFTextParagraph implements TextParagraph { * If TextSpecInfoAtom is present, we must update the text size in it, * otherwise the ppt will be corrupted */ - for (Record r : paragraphs.get(0)._records) { + for (Record r : paragraphs.get(0).getRecords()) { if (r instanceof TextSpecInfoAtom) { ((TextSpecInfoAtom)r).setParentSize(rawText.length()+1); break; } } + + if (_txtbox instanceof EscherTextboxWrapper) { + try { + ((EscherTextboxWrapper)_txtbox).writeOut(null); + } catch (IOException e) { + throw new RuntimeException("failed dummy write", e); + } + } } /** @@ -817,7 +835,7 @@ public final class HSLFTextParagraph implements TextParagraph { if (!isFirst) { TextPropCollection tpc = htp.getParagraphStyle(); HSLFTextParagraph prevHtp = htp; - htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom, htp._styleAtom); + htp = new HSLFTextParagraph(htp._headerAtom, htp._byteAtom, htp._charAtom); htp.getParagraphStyle().copy(tpc); htp.setParentShape(prevHtp.getParentShape()); htp.setShapeId(prevHtp.getShapeId()); @@ -930,24 +948,14 @@ public final class HSLFTextParagraph implements TextParagraph { /** * For a given PPDrawing, grab all the TextRuns */ - public static List> findTextParagraphs(PPDrawing ppdrawing) { + public static List> findTextParagraphs(PPDrawing ppdrawing, HSLFSheet sheet) { List> runsV = new ArrayList>(); for (EscherTextboxWrapper wrapper : ppdrawing.getTextboxWrappers()) { - runsV.addAll(findTextParagraphs(wrapper)); + runsV.add(findTextParagraphs(wrapper, sheet)); } return runsV; } - /** - * Scans through the supplied record array, looking for - * a TextHeaderAtom followed by one of a TextBytesAtom or - * a TextCharsAtom. Builds up TextRuns from these - * - * @param records the records to build from - * @param found vector to add any found to - */ - protected static List> findTextParagraphs(final Record[] records) { - return findTextParagraphs(records, null); - } + /** * Scans through the supplied record array, looking for * a TextHeaderAtom followed by one of a TextBytesAtom or @@ -955,14 +963,73 @@ public final class HSLFTextParagraph implements TextParagraph { * * @param wrapper an EscherTextboxWrapper */ - protected static List> findTextParagraphs(EscherTextboxWrapper wrapper) { + protected static List findTextParagraphs(EscherTextboxWrapper wrapper, HSLFSheet sheet) { // propagate parents to parent-aware records RecordContainer.handleParentAwareRecords(wrapper); int shapeId = wrapper.getShapeId(); - List> rv = findTextParagraphs(wrapper.getChildRecords(), wrapper.getStyleTextProp9Atom()); - for (List htpList : rv) { - for (HSLFTextParagraph htp : htpList) { + List rv = null; + + OutlineTextRefAtom ota = (OutlineTextRefAtom)wrapper.findFirstOfType(OutlineTextRefAtom.typeID); + if (ota != null) { + // if we are based on an outline, there are no further records to be parsed from the wrapper + if (sheet == null) { + throw new RuntimeException("Outline atom reference can't be solved without a sheet record"); + } + + List> sheetRuns = sheet.getTextParagraphs(); + assert(sheetRuns != null); + + int idx = ota.getTextIndex(); + for (List r : sheetRuns) { + if (r.isEmpty()) continue; + int ridx = r.get(0).getIndex(); + if (ridx > idx) break; + if (ridx == idx) { + if (rv == null) { + rv = r; + } else { + // create a new container + // TODO: ... is this case really happening? + rv = new ArrayList(rv); + rv.addAll(r); + } + } + } + if(rv == null || rv.isEmpty()) { + logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); + } + } else { + if (sheet != null) { + // check sheet runs first, so we get exactly the same paragraph list + List> sheetRuns = sheet.getTextParagraphs(); + assert(sheetRuns != null); + + for (List paras : sheetRuns) { + if (!paras.isEmpty() && paras.get(0)._headerAtom.getParentRecord() == wrapper) { + rv = paras; + break; + } + } + } + + if (rv == null) { + // if we haven't found the wrapper in the sheet runs, create a new paragraph list from its record + List> rvl = findTextParagraphs(wrapper.getChildRecords()); + switch (rvl.size()) { + case 0: break; // nothing found + case 1: rv = rvl.get(0); break; // normal case + default: + throw new RuntimeException("TextBox contains more than one list of paragraphs."); + } + } + } + + if (rv != null) { + StyleTextProp9Atom styleTextProp9Atom = wrapper.getStyleTextProp9Atom(); + + for (HSLFTextParagraph htp : rv) { htp.setShapeId(shapeId); + htp.setStyleTextProp9Atom(styleTextProp9Atom); } } return rv; @@ -974,77 +1041,59 @@ public final class HSLFTextParagraph implements TextParagraph { * a TextCharsAtom. Builds up TextRuns from these * * @param records the records to build from - * @param styleTextProp9Atom an optional StyleTextProp9Atom with numbered lists info */ - protected static List> findTextParagraphs(Record[] records, StyleTextProp9Atom styleTextProp9Atom) { + protected static List> findTextParagraphs(Record[] records) { List> paragraphCollection = new ArrayList>(); - if (records == null) { - throw new NullPointerException("records need to be filled."); - } - - int recordIdx; - for (recordIdx = 0; recordIdx < records.length; recordIdx++) { - if (records[recordIdx] instanceof TextHeaderAtom) break; - } - - if (recordIdx == records.length) { - logger.log(POILogger.INFO, "No text records found."); - return paragraphCollection; - } - - for (int slwtIndex = 0; recordIdx < records.length; slwtIndex++) { - List paragraphs = new ArrayList(); - paragraphCollection.add(paragraphs); - - TextHeaderAtom header = (TextHeaderAtom)records[recordIdx++]; + int[] recordIdx = {0}; + + for (int slwtIndex = 0; recordIdx[0] < records.length; slwtIndex++) { + TextHeaderAtom header = null; TextBytesAtom tbytes = null; TextCharsAtom tchars = null; TextRulerAtom ruler = null; MasterTextPropAtom indents = null; - List otherRecordList = new ArrayList(); - - for (; recordIdx < records.length; recordIdx++) { - Record r = records[recordIdx]; + for (Record r : getRecords(records, recordIdx, null)) { long rt = r.getRecordType(); - if (RecordTypes.TextHeaderAtom.typeID == rt) break; - else if (RecordTypes.TextBytesAtom.typeID == rt) tbytes = (TextBytesAtom)r; - else if (RecordTypes.TextCharsAtom.typeID == rt) tchars = (TextCharsAtom)r; - // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below - else if (RecordTypes.TextRulerAtom.typeID == rt) ruler = (TextRulerAtom)r; - else if (RecordTypes.MasterTextPropAtom.typeID == rt) { + if (RecordTypes.TextHeaderAtom.typeID == rt) { + header = (TextHeaderAtom)r; + } else if (RecordTypes.TextBytesAtom.typeID == rt) { + tbytes = (TextBytesAtom)r; + } else if (RecordTypes.TextCharsAtom.typeID == rt) { + tchars = (TextCharsAtom)r; + } else if (RecordTypes.TextRulerAtom.typeID == rt) { + ruler = (TextRulerAtom)r; + } else if (RecordTypes.MasterTextPropAtom.typeID == rt) { indents = (MasterTextPropAtom)r; - otherRecordList.add(indents); - } else { - otherRecordList.add(r); } + // don't search for RecordTypes.StyleTextPropAtom.typeID here ... see findStyleAtomPresent below } - assert(header != null); + if (header == null) break; + if (header.getParentRecord() instanceof SlideListWithText) { // runs found in PPDrawing are not linked with SlideListWithTexts header.setIndex(slwtIndex); } - Record otherRecords[] = otherRecordList.toArray(new Record[otherRecordList.size()]); - if (tbytes == null && tchars == null) { tbytes = new TextBytesAtom(); - // header.getParentRecord().addChildAfter(tbytes, header); + // don't add record yet - set it in storeText logger.log(POILogger.INFO, "bytes nor chars atom doesn't exist. Creating dummy record for later saving."); } String rawText = (tchars != null) ? tchars.getText() : tbytes.getText(); StyleTextPropAtom styles = findStyleAtomPresent(header, rawText.length()); + List paragraphs = new ArrayList(); + paragraphCollection.add(paragraphs); + // split, but keep delimiter for (String para : rawText.split("(?<=\r)")) { - HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars, styles); + HSLFTextParagraph tpara = new HSLFTextParagraph(header, tbytes, tchars); paragraphs.add(tpara); - tpara.setStyleTextProp9Atom(styleTextProp9Atom); tpara._ruler = ruler; - tpara._records = otherRecords; tpara.getParagraphStyle().updateTextSize(para.length()); HSLFTextRun trun = new HSLFTextRun(tpara); @@ -1059,6 +1108,10 @@ public final class HSLFTextParagraph implements TextParagraph { } } + if (paragraphCollection.isEmpty()) { + logger.log(POILogger.DEBUG, "No text records found."); + } + return paragraphCollection; } @@ -1166,9 +1219,8 @@ public final class HSLFTextParagraph implements TextParagraph { TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); wrapper.appendChildRecord(sta); - HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, sta); + HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null); htp.setParagraphStyle(paraStyle); - htp._records = new Record[0]; HSLFTextRun htr = new HSLFTextRun(htp); htr.setCharacterStyle(charStyle); diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java index 0fb443b604..1e1dca888f 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextRun.java @@ -92,7 +92,7 @@ public final class HSLFTextRun implements TextRun { * Change the text */ public void setText(String text) { - _runText = text; + _runText = HSLFTextParagraph.toInternalString(text); } // --------------- Internal helpers on rich text properties ------- diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java index e05ae79a60..60e9eb84e2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java @@ -159,10 +159,29 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape } protected EscherTextboxWrapper getEscherTextboxWrapper(){ - if(_txtbox == null){ - EscherTextboxRecord textRecord = getEscherChild(EscherTextboxRecord.RECORD_ID); - if(textRecord != null) _txtbox = new EscherTextboxWrapper(textRecord); + if(_txtbox != null) return _txtbox; + + EscherTextboxRecord textRecord = getEscherChild(EscherTextboxRecord.RECORD_ID); + if (textRecord == null) return null; + + HSLFSheet sheet = getSheet(); + if (sheet != null) { + PPDrawing drawing = sheet.getPPDrawing(); + if (drawing != null) { + EscherTextboxWrapper wrappers[] = drawing.getTextboxWrappers(); + if (wrappers != null) { + for (EscherTextboxWrapper w : wrappers) { + // check for object identity + if (textRecord == w.getEscherRecord()) { + _txtbox = w; + return _txtbox; + } + } + } + } } + + _txtbox = new EscherTextboxWrapper(textRecord); return _txtbox; } @@ -507,13 +526,17 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape _paragraphs.addAll(HSLFTextParagraph.createEmptyParagraph()); _txtbox = _paragraphs.get(0).getTextboxWrapper(); } else { - initParagraphsFromSheetRecords(); - if (_paragraphs.isEmpty()) { - List> llhtp = HSLFTextParagraph.findTextParagraphs(_txtbox); - if (!llhtp.isEmpty()) { - _paragraphs.addAll(llhtp.get(0)); - } + _paragraphs = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); + if (_paragraphs == null || _paragraphs.isEmpty()) { + throw new RuntimeException("TextRecord didn't contained any text lines"); } +// initParagraphsFromSheetRecords(); +// if (_paragraphs.isEmpty()) { +// List> llhtp = HSLFTextParagraph.findTextParagraphs(_txtbox); +// if (!llhtp.isEmpty()) { +// _paragraphs.addAll(llhtp.get(0)); +// } +// } } for (HSLFTextParagraph p : _paragraphs) { @@ -536,57 +559,47 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape } } - protected void initParagraphsFromSheetRecords(){ - EscherTextboxWrapper txtbox = getEscherTextboxWrapper(); - HSLFSheet sheet = getSheet(); - - if(sheet == null || txtbox == null) return; - - OutlineTextRefAtom ota = null; - - Record[] child = txtbox.getChildRecords(); - for (int i = 0; i < child.length; i++) { - if (child[i] instanceof OutlineTextRefAtom) { - ota = (OutlineTextRefAtom)child[i]; - break; - } - } - - List> sheetRuns = _sheet.getTextParagraphs(); - _paragraphs.clear(); - if (sheetRuns != null) { - if (ota != null) { - int idx = ota.getTextIndex(); - for (List r : sheetRuns) { - if (r.isEmpty()) continue; - int ridx = r.get(0).getIndex(); - if (ridx > idx) break; - if (ridx == idx) _paragraphs.addAll(r); - } - if(_paragraphs.isEmpty()) { - logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); - } - } else { - int shapeId = getShapeId(); - for (List r : sheetRuns) { - if (r.isEmpty()) continue; - if (r.get(0).getShapeId() == shapeId) _paragraphs.addAll(r); - } - } - } - - // ensure the same references child records of TextRun - // TODO: check the purpose of this ... -// if(_txtrun != null) { -// for (int i = 0; i < child.length; i++) { -// for (Record r : _txtrun.getRecords()) { -// if (child[i].getRecordType() == r.getRecordType()) { -// child[i] = r; -// } -// } +// protected void initParagraphsFromSheetRecords(){ +// EscherTextboxWrapper txtbox = getEscherTextboxWrapper(); +// HSLFSheet sheet = getSheet(); +// +// if (sheet == null || txtbox == null) return; +// List> sheetRuns = _sheet.getTextParagraphs(); +// if (sheetRuns == null) return; +// +// _paragraphs.clear(); +// OutlineTextRefAtom ota = (OutlineTextRefAtom)txtbox.findFirstOfType(OutlineTextRefAtom.typeID); +// +// if (ota != null) { +// int idx = ota.getTextIndex(); +// for (List r : sheetRuns) { +// if (r.isEmpty()) continue; +// int ridx = r.get(0).getIndex(); +// if (ridx > idx) break; +// if (ridx == idx) _paragraphs.addAll(r); +// } +// if(_paragraphs.isEmpty()) { +// logger.log(POILogger.WARN, "text run not found for OutlineTextRefAtom.TextIndex=" + idx); +// } +// } else { +// int shapeId = getShapeId(); +// for (List r : sheetRuns) { +// if (r.isEmpty()) continue; +// if (r.get(0).getShapeId() == shapeId) _paragraphs.addAll(r); // } // } - } +// +// // ensure the same references child records of TextRun - see #48916 +//// if(_txtrun != null) { +//// for (int i = 0; i < child.length; i++) { +//// for (Record r : _txtrun.getRecords()) { +//// if (child[i].getRecordType() == r.getRecordType()) { +//// child[i] = r; +//// } +//// } +//// } +//// } +// } /* // 0xB acts like cariage return in page titles and like blank in the others @@ -740,7 +753,8 @@ public abstract class HSLFTextShape extends HSLFSimpleShape implements TextShape * Also updates the styles to the correct text length. */ protected void storeText() { - HSLFTextParagraph.storeText(_paragraphs); + List paras = getTextParagraphs(); + HSLFTextParagraph.storeText(paras); } // Accesser methods follow diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java index fc6131d952..b1e38d977b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java @@ -29,7 +29,7 @@ import org.apache.poi.hslf.record.SlideAtom; * @author Yegor Kozlov */ public final class HSLFTitleMaster extends HSLFMasterSheet { - private final List> _runs = new ArrayList>(); + private final List> _paragraphs = new ArrayList>(); /** * Constructs a TitleMaster @@ -38,14 +38,16 @@ public final class HSLFTitleMaster extends HSLFMasterSheet { public HSLFTitleMaster(org.apache.poi.hslf.record.Slide record, int sheetNo) { super(record, sheetNo); - _runs.addAll(HSLFTextParagraph.findTextParagraphs(getPPDrawing())); + for (List l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { + if (!_paragraphs.contains(l)) _paragraphs.add(l); + } } /** * Returns an array of all the TextRuns found */ public List> getTextParagraphs() { - return _runs; + return _paragraphs; } /** diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawFactory.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawFactory.java index df4d635a69..6eb30eb707 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawFactory.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawFactory.java @@ -69,8 +69,8 @@ public class DrawFactory { return getDrawable((FreeformShape>)shape); } else if (shape instanceof TextShape) { return getDrawable((TextShape>)shape); - } else if (shape instanceof ShapeGroup) { - return getDrawable((ShapeGroup)shape); + } else if (shape instanceof GroupShape) { + return getDrawable((GroupShape)shape); } else if (shape instanceof PictureShape) { return getDrawable((PictureShape)shape); } else if (shape instanceof Background) { @@ -110,8 +110,8 @@ public class DrawFactory { return new DrawTextShape(shape); } - public > DrawShapeGroup getDrawable(T shape) { - return new DrawShapeGroup(shape); + public > DrawGroupShape getDrawable(T shape) { + return new DrawGroupShape(shape); } public DrawPictureShape getDrawable(T shape) { diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java new file mode 100644 index 0000000000..31f2496b32 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java @@ -0,0 +1,53 @@ +package org.apache.poi.sl.draw; + +import java.awt.Graphics2D; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +import org.apache.poi.sl.usermodel.*; + + +public class DrawGroupShape> extends DrawShape implements Drawable { + + public DrawGroupShape(T shape) { + super(shape); + } + + public void draw(Graphics2D graphics) { + + // the coordinate system of this group of shape + Rectangle2D interior = shape.getInteriorAnchor(); + // anchor of this group relative to the parent shape + Rectangle2D exterior = shape.getAnchor(); + + AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); + AffineTransform tx0 = new AffineTransform(tx); + + double scaleX = interior.getWidth() == 0. ? 1.0 : exterior.getWidth() / interior.getWidth(); + double scaleY = interior.getHeight() == 0. ? 1.0 : exterior.getHeight() / interior.getHeight(); + + tx.translate(exterior.getX(), exterior.getY()); + tx.scale(scaleX, scaleY); + tx.translate(-interior.getX(), -interior.getY()); + + DrawFactory drawFact = DrawFactory.getInstance(graphics); + AffineTransform at2 = graphics.getTransform(); + + for (Shape child : shape) { + // remember the initial transform and restore it after we are done with the drawing + AffineTransform at = graphics.getTransform(); + graphics.setRenderingHint(Drawable.GSAVE, true); + + Drawable draw = drawFact.getDrawable(child); + draw.applyTransform(graphics); + draw.draw(graphics); + + // restore the coordinate system + graphics.setTransform(at); + graphics.setRenderingHint(Drawable.GRESTORE, true); + } + + graphics.setTransform(at2); + graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx0); + } +} diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java index 6cb60c9b01..2bebbfa892 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawShape.java @@ -25,11 +25,10 @@ public class DrawShape implements Drawable { if (!(shape instanceof PlaceableShape)) return; PlaceableShape ps = (PlaceableShape)shape; - Rectangle2D anchor = ps.getAnchor(); AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); - if(tx != null) { - anchor = tx.createTransformedShape(anchor).getBounds2D(); - } + final Rectangle2D anchor = (tx != null) + ? tx.createTransformedShape(ps.getAnchor()).getBounds2D() + : ps.getAnchor(); // rotation double rotation = ps.getRotation(); @@ -39,7 +38,8 @@ public class DrawShape implements Drawable { double centerY = anchor.getCenterY(); // normalize rotation - rotation = (360.+(rotation%360.))%360.; + rotation %= 360.; + if (rotation < 0) rotation += 360.; int quadrant = (((int)rotation+45)/90)%4; double scaleX = 1.0, scaleY = 1.0; @@ -53,26 +53,43 @@ public class DrawShape implements Drawable { // think of it, as if you paint the shape on a canvas. First you rotate the canvas, which might // be already (differently) scaled, so you can paint the shape in its default orientation // and later on, turn it around again to compare it with its original size ... - AffineTransform txg = new AffineTransform(); // graphics coordinate space - AffineTransform txs = new AffineTransform(tx); // shape coordinate space + + // graphics coordinate space + AffineTransform txg = new AffineTransform(); txg.translate(centerX, centerY); - txg.rotate(Math.toRadians(quadrant*90)); + txg.rotate(Math.toRadians(90)); txg.translate(-centerX, -centerY); - txs.translate(centerX, centerY); - txs.rotate(Math.toRadians(-quadrant*90)); - txs.translate(-centerX, -centerY); - txg.concatenate(txs); - Rectangle2D anchor2 = txg.createTransformedShape(ps.getAnchor()).getBounds2D(); + + boolean oldVariant = true; + Rectangle2D anchor2; + + if (oldVariant) { + // shape coordinate space + AffineTransform txs = new AffineTransform(tx); + txs.translate(centerX, centerY); + txs.rotate(Math.toRadians(90)); + txs.translate(-centerX, -centerY); + txg.concatenate(txs); + anchor2 = txg.createTransformedShape(ps.getAnchor()).getBounds2D(); + } else { + anchor2 = txg.createTransformedShape(anchor).getBounds2D(); + } + scaleX = anchor.getWidth() == 0. ? 1.0 : anchor.getWidth() / anchor2.getWidth(); scaleY = anchor.getHeight() == 0. ? 1.0 : anchor.getHeight() / anchor2.getHeight(); + + graphics.translate(centerX, centerY); + graphics.rotate(Math.toRadians(rotation-quadrant*90.)); + graphics.scale(scaleX, scaleY); + graphics.rotate(Math.toRadians(quadrant*90)); + graphics.translate(-centerX, -centerY); + } else { + graphics.translate(centerX, centerY); + graphics.rotate(Math.toRadians(rotation)); + graphics.scale(scaleX, scaleY); + graphics.translate(-centerX, -centerY); } - // transformation is applied reversed ... - graphics.translate(centerX, centerY); - graphics.rotate(Math.toRadians(rotation-quadrant*90.)); - graphics.scale(scaleX, scaleY); - graphics.rotate(Math.toRadians(quadrant*90)); - graphics.translate(-centerX, -centerY); } //flip horizontal diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/DrawShapeGroup.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawShapeGroup.java deleted file mode 100644 index ceda9280c4..0000000000 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawShapeGroup.java +++ /dev/null @@ -1,52 +0,0 @@ -package org.apache.poi.sl.draw; - -import java.awt.Graphics2D; -import java.awt.geom.AffineTransform; -import java.awt.geom.Rectangle2D; - -import org.apache.poi.sl.usermodel.*; - - -public class DrawShapeGroup> extends DrawShape implements Drawable { - - public DrawShapeGroup(T shape) { - super(shape); - } - - public void draw(Graphics2D graphics) { - - // the coordinate system of this group of shape - Rectangle2D interior = shape.getInteriorAnchor(); - // anchor of this group relative to the parent shape - Rectangle2D exterior = shape.getAnchor(); - - AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); - AffineTransform tx0 = new AffineTransform(tx); - - double scaleX = interior.getWidth() == 0. ? 1.0 : exterior.getWidth() / interior.getWidth(); - double scaleY = interior.getHeight() == 0. ? 1.0 : exterior.getHeight() / interior.getHeight(); - - tx.translate(exterior.getX(), exterior.getY()); - tx.scale(scaleX, scaleY); - tx.translate(-interior.getX(), -interior.getY()); - - DrawFactory drawFact = DrawFactory.getInstance(graphics); - - for (Shape child : shape) { - // remember the initial transform and restore it after we are done with the drawing - AffineTransform at = graphics.getTransform(); - graphics.setRenderingHint(Drawable.GSAVE, true); - - Drawable draw = drawFact.getDrawable(child); - draw.applyTransform(graphics); - draw.draw(graphics); - - // restore the coordinate system - graphics.setTransform(at); - graphics.setRenderingHint(Drawable.GRESTORE, true); - } - - graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, tx0); - - } -} diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java b/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java index 8789082afd..54128b82c6 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java @@ -32,6 +32,23 @@ public interface Drawable { public boolean isCompatibleValue(Object val) { return true; } + + public String toString() { + switch (intKey()) { + case 1: return "DRAW_FACTORY"; + case 2: return "GROUP_TRANSFORM"; + case 3: return "IMAGE_RENDERER"; + case 4: return "TEXT_RENDERING_MODE"; + case 5: return "GRADIENT_SHAPE"; + case 6: return "PRESET_GEOMETRY_CACHE"; + case 7: return "FONT_HANDLER"; + case 8: return "FONT_FALLBACK"; + case 9: return "FONT_MAP"; + case 10: return "GSAVE"; + case 11: return "GRESTORE"; + default: return "UNKNOWN_ID "+intKey(); + } + } } /** diff --git a/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java b/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java index 9704c2c0a7..f4a298cdb6 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/ImageRenderer.java @@ -30,18 +30,35 @@ import javax.imageio.ImageIO; /** * For now this class renders only images supported by the javax.imageio.ImageIO * framework. Subclasses can override this class to support other formats, for - * example, Use Apache batik to render WMF: + * example, use Apache Batik to render WMF, PICT can be rendered using Apple QuickTime API for Java: * *
  * 
  * public class MyImageRendener extends ImageRendener {
+ *     InputStream data;
  *
  *     public boolean drawImage(Graphics2D graphics,Rectangle2D anchor,Insets clip) {
  *         // draw image
+ *       DataInputStream is = new DataInputStream(data);
+ *       org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore wmfStore =
+ *               new org.apache.batik.transcoder.wmf.tosvg.WMFRecordStore();
+ *       try {
+ *           wmfStore.read(is);
+ *       } catch (IOException e){
+ *           return;
+ *       }
+ *
+ *       float scale = (float)anchor.width/wmfStore.getWidthPixels();
+ *
+ *       org.apache.batik.transcoder.wmf.tosvg.WMFPainter painter =
+ *               new org.apache.batik.transcoder.wmf.tosvg.WMFPainter(wmfStore, 0, 0, scale);
+ *       graphics.translate(anchor.x, anchor.y);
+ *       painter.paint(graphics);
  *     }
  *
  *     public void loadImage(InputStream data, String contentType) throws IOException {
  *         if ("image/wmf".equals(contentType)) {
+ *             this.data = data;
  *             // use Apache Batik to handle WMF
  *         } else {
  *             super.loadImage(data,contentType);
@@ -147,12 +164,14 @@ public class ImageRenderer {
         int iw = img.getWidth();
         int ih = img.getHeight();
 
+        
         double cw = (100000-clip.left-clip.right) / 100000.0;
         double ch = (100000-clip.top-clip.bottom) / 100000.0;
         double sx = anchor.getWidth()/(iw*cw);
         double sy = anchor.getHeight()/(ih*ch);
         double tx = anchor.getX()-(iw*sx*clip.left/100000.0);
         double ty = anchor.getY()-(ih*sy*clip.top/100000.0);
+
         AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ;
 
         Shape clipOld = graphics.getClip();
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java
new file mode 100644
index 0000000000..d71bb253fe
--- /dev/null
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java
@@ -0,0 +1,39 @@
+/* ====================================================================
+   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.sl.usermodel;
+
+import java.awt.geom.Rectangle2D;
+
+public interface GroupShape extends Shape, ShapeContainer, PlaceableShape {
+
+    /**
+     * Gets the coordinate space of this group.  All children are constrained
+     * to these coordinates.
+     *
+     * @param anchor the coordinate space of this group
+     */
+    Rectangle2D getInteriorAnchor();
+    
+    /**
+     * Sets the coordinate space of this group.  All children are constrained
+     * to these coordinates.
+     *
+     * @param anchor the coordinate space of this group
+     */
+    void setInteriorAnchor(Rectangle2D anchor);
+}
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/PictureShape.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/PictureShape.java
index 970f04353c..a2c0824e0c 100644
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/PictureShape.java
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/PictureShape.java
@@ -20,9 +20,17 @@ package org.apache.poi.sl.usermodel;
 import java.awt.Insets;
 
 public interface PictureShape extends SimpleShape {
-	PictureData getPictureData();
+    /**
+     * Returns the picture data for this picture.
+     *
+     * @return the picture data for this picture.
+     */
+    PictureData getPictureData();
 
 	/**
+	 * Returns the clipping values as percent ratio relatively to the image size.
+	 * The clipping are returned as insets converted/scaled to 100000 (=100%).
+	 * 
 	 * @return the clipping rectangle, which is given in percent in relation to the image width/height
 	 */
 	Insets getClipping();
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/ShapeGroup.java
deleted file mode 100644
index 7bd940c36c..0000000000
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/ShapeGroup.java
+++ /dev/null
@@ -1,24 +0,0 @@
-/* ====================================================================
-   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.sl.usermodel;
-
-import java.awt.geom.Rectangle2D;
-
-public interface ShapeGroup extends Shape, ShapeContainer, PlaceableShape {
-    Rectangle2D getInteriorAnchor();
-}
diff --git a/src/scratchpad/src/org/apache/poi/sl/usermodel/Sheet.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/Sheet.java
index 6f4ba0ac67..f94b7727a7 100644
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/Sheet.java
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/Sheet.java
@@ -17,6 +17,8 @@
 
 package org.apache.poi.sl.usermodel;
 
+import java.awt.Graphics2D;
+
 
 /**
  * Common parent of Slides, Notes and Masters
@@ -34,4 +36,11 @@ public interface Sheet extends ShapeConta
 	MasterSheet getMasterSheet();
 
 	Background getBackground();
+	
+	/**
+	 * Convenience method to draw a sheet to a graphics context
+	 *
+	 * @param graphics
+	 */
+	void draw(Graphics2D graphics);
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
index ca9e6c9a2f..14301af2ba 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/AllHSLFModelTests.java
@@ -29,7 +29,6 @@ import org.junit.runners.Suite;
     TestFreeform.class,
     TestHeadersFooters.class,
     TestHyperlink.class,
-    TestImagePainter.class,
     TestLine.class,
     TestMovieShape.class,
     TestOleEmbedding.class,
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestImagePainter.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestImagePainter.java
deleted file mode 100644
index 963beeefbe..0000000000
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestImagePainter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-/* ====================================================================
-   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 java.awt.Graphics2D;
-
-import junit.framework.TestCase;
-
-import org.apache.poi.hslf.blip.BitmapPainter;
-import org.apache.poi.hslf.blip.ImagePainter;
-import org.apache.poi.hslf.usermodel.HSLFPictureData;
-import org.apache.poi.hslf.usermodel.HSLFPictureShape;
-
-/**
- * Test Picture shape.
- *
- * @author Yegor Kozlov
- */
-public final class TestImagePainter extends TestCase {
-
-    private static class CustomImagePainter implements ImagePainter {
-        public CustomImagePainter() {
-            // no fields to initialise
-        }
-        public void paint(Graphics2D graphics, HSLFPictureData pict, HSLFPictureShape parent){
-            //do noting
-        }
-    }
-
-    public void testImagePainter() {
-
-        ImagePainter pntr = HSLFPictureData.getImagePainter(HSLFPictureShape.PNG);
-        assertTrue(HSLFPictureData.getImagePainter(HSLFPictureShape.PNG) instanceof BitmapPainter);
-        assertTrue(HSLFPictureData.getImagePainter(HSLFPictureShape.JPEG) instanceof BitmapPainter);
-        assertTrue(HSLFPictureData.getImagePainter(HSLFPictureShape.DIB) instanceof BitmapPainter);
-
-        HSLFPictureData.setImagePainter(HSLFPictureShape.WMF, new CustomImagePainter());
-        assertTrue(HSLFPictureData.getImagePainter(HSLFPictureShape.WMF) instanceof CustomImagePainter);
-    }
-}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
index 0c5fcb69bb..3337d58c36 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/model/TestTable.java
@@ -43,15 +43,15 @@ public final class TestTable {
 
         HSLFSlide slide = ppt.createSlide();
 
-        Table tbl = new Table(2, 5);
+        HSLFTable tbl = new HSLFTable(2, 5);
         slide.addShape(tbl);
 
-        TableCell cell = tbl.getCell(0, 0);
+        HSLFTableCell cell = tbl.getCell(0, 0);
         //table cells have type=TextHeaderAtom.OTHER_TYPE, see bug #46033
         assertEquals(TextHeaderAtom.OTHER_TYPE, cell.getTextParagraphs().get(0).getRunType());
 
-        assertTrue(slide.getShapes().get(0) instanceof Table);
-        Table tbl2 = (Table)slide.getShapes().get(0);
+        assertTrue(slide.getShapes().get(0) instanceof HSLFTable);
+        HSLFTable tbl2 = (HSLFTable)slide.getShapes().get(0);
         assertEquals(tbl.getNumberOfColumns(), tbl2.getNumberOfColumns());
         assertEquals(tbl.getNumberOfRows(), tbl2.getNumberOfRows());
 
@@ -61,8 +61,8 @@ public final class TestTable {
 
         ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));
         slide = ppt.getSlides().get(0);
-        assertTrue(slide.getShapes().get(0) instanceof Table);
-        Table tbl3 = (Table)slide.getShapes().get(0);
+        assertTrue(slide.getShapes().get(0) instanceof HSLFTable);
+        HSLFTable tbl3 = (HSLFTable)slide.getShapes().get(0);
         assertEquals(tbl.getNumberOfColumns(), tbl3.getNumberOfColumns());
         assertEquals(tbl.getNumberOfRows(), tbl3.getNumberOfRows());
     }
@@ -75,7 +75,7 @@ public final class TestTable {
         HSLFSlideShow ppt = new HSLFSlideShow();
         HSLFSlide slide = ppt.createSlide();
         List shapes;
-        Table tbl1 = new Table(1, 5);
+        HSLFTable tbl1 = new HSLFTable(1, 5);
         assertEquals(5, tbl1.getNumberOfColumns());
         assertEquals(1, tbl1.getNumberOfRows());
         slide.addShape(tbl1);
@@ -83,7 +83,7 @@ public final class TestTable {
         shapes = slide.getShapes();
         assertEquals(1, shapes.size());
 
-        Table tbl2 = (Table)shapes.get(0);
+        HSLFTable tbl2 = (HSLFTable)shapes.get(0);
         assertSame(tbl1.getSpContainer(), tbl2.getSpContainer());
 
         assertEquals(tbl1.getNumberOfColumns(), tbl2.getNumberOfColumns());
@@ -93,13 +93,13 @@ public final class TestTable {
     @Test
     public void testIllegalCOnstruction(){
         try {
-            new Table(0, 5);
+            new HSLFTable(0, 5);
             fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");
         } catch (IllegalArgumentException e){
 
         }
         try {
-            new Table(5, 0);
+            new HSLFTable(5, 0);
             fail("Table(rownum, colnum) must throw IllegalArgumentException if any of tghe arguments is less than 1");
         } catch (IllegalArgumentException e){
 
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestAddingSlides.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestAddingSlides.java
index c0634f05fc..800df623e3 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestAddingSlides.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestAddingSlides.java
@@ -281,7 +281,7 @@ public final class TestAddingSlides extends TestCase {
         assertEquals(14, doc.getNotesSlideListWithText().getSlideAtomsSets().length);
 
         //remove all slides, corresponding notes should be removed too
-        for (int i = 0; i < slides.size(); i++) {
+        for (int i = slides.size(); i > 0; i--) {
             ppt.removeSlide(0);
         }
         assertEquals(0, ppt.getSlides().size());
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
index 12e3ff6c30..b7dba80b82 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestBugs.java
@@ -18,14 +18,10 @@
 package org.apache.poi.hslf.usermodel;
 
 import static org.junit.Assert.*;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.io.InputStream;
+
+import java.awt.Color;
+import java.awt.Rectangle;
+import java.io.*;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
@@ -44,14 +40,14 @@ import org.apache.poi.ddf.EscherProperties;
 import org.apache.poi.hslf.HSLFTestDataSamples;
 import org.apache.poi.hslf.exceptions.OldPowerPointFormatException;
 import org.apache.poi.hslf.model.*;
-import org.apache.poi.hslf.record.Document;
-import org.apache.poi.hslf.record.Record;
-import org.apache.poi.hslf.record.SlideListWithText;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection.TextPropType;
+import org.apache.poi.hslf.record.*;
 import org.apache.poi.hslf.record.SlideListWithText.SlideAtomsSet;
-import org.apache.poi.hslf.record.TextHeaderAtom;
-import org.apache.poi.util.LittleEndian;
-import org.apache.poi.util.StringUtil;
-import org.apache.poi.util.Units;
+import org.apache.poi.poifs.filesystem.DocumentEntry;
+import org.apache.poi.poifs.filesystem.POIFSFileSystem;
+import org.apache.poi.util.*;
+import org.junit.Ignore;
 import org.junit.Test;
 
 /**
@@ -527,7 +523,7 @@ public final class TestBugs {
         for (List tr : _slides.get(0).getTextParagraphs()) {
             if (! tr.get(0).isDrawingBased()) str++;
         }
-        assertEquals(1, str);
+        assertEquals(2, str);
     }
     
     @Test
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java
index 3638bad82e..cc78643a8b 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestFontRendering.java
@@ -17,29 +17,22 @@
 
 package org.apache.poi.hslf.usermodel;
 
-import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.*;
 import static org.junit.Assume.assumeTrue;
 
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Graphics2D;
-import java.awt.GraphicsEnvironment;
-import java.awt.RenderingHints;
+import java.awt.*;
 import java.awt.geom.AffineTransform;
 import java.awt.geom.Rectangle2D;
 import java.awt.image.BufferedImage;
 import java.awt.image.DataBufferByte;
 import java.io.File;
 import java.io.InputStream;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
+import java.util.*;
 
 import javax.imageio.ImageIO;
 
 import org.apache.poi.POIDataSamples;
-import org.apache.poi.hslf.model.TextPainter;
+import org.apache.poi.sl.draw.Drawable;
 import org.apache.poi.util.TempFile;
 import org.junit.Ignore;
 import org.junit.Test;
@@ -50,7 +43,7 @@ import org.junit.Test;
 public class TestFontRendering {
     private static POIDataSamples slTests = POIDataSamples.getSlideShowInstance();
 
-    @Ignore("This fails on some systems because fonts are rendered slightly different")
+    // @Ignore2("This fails on some systems because fonts are rendered slightly different")
     @Test
     public void bug55902mixedFontWithChineseCharacters() throws Exception {
         // font files need to be downloaded first via
@@ -86,7 +79,7 @@ public class TestFontRendering {
         
         Dimension pgsize = ss.getPageSize();
         
-        HSLFSlide slide = ss.getSlides()[0];
+        HSLFSlide slide = ss.getSlides().get(0);
         
         // render it
         double zoom = 1;
@@ -95,8 +88,8 @@ public class TestFontRendering {
         
         BufferedImage imgActual = new BufferedImage((int)Math.ceil(pgsize.width*zoom), (int)Math.ceil(pgsize.height*zoom), BufferedImage.TYPE_3BYTE_BGR);
         Graphics2D graphics = imgActual.createGraphics();
-        graphics.setRenderingHint(TextPainter.KEY_FONTFALLBACK, fallbackMap);
-        graphics.setRenderingHint(TextPainter.KEY_FONTMAP, fontMap);
+        graphics.setRenderingHint(Drawable.FONT_FALLBACK, fallbackMap);
+        graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);
         graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
         graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
         graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
@@ -116,7 +109,7 @@ public class TestFontRendering {
         if(!Arrays.equals(expectedData, actualData)) {
             ImageIO.write(imgActual, "PNG", TempFile.createTempFile("TestFontRendering", ".png"));
         }
-        assertTrue("Expected to have matching raster-arrays, but found differences, size " + expectedData.length + " and " + actualData.length, 
-                Arrays.equals(expectedData, actualData));
+        
+        assertArrayEquals("Expected to have matching raster-arrays, but found differences", expectedData, actualData);
     }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNumberedList3.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNumberedList3.java
index ab358f65a0..5ad2bac1c4 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNumberedList3.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestNumberedList3.java
@@ -79,10 +79,14 @@ public final class TestNumberedList3 {
 		assertEquals(Short.valueOf((short)1), autoNumbers[0].getAutoNumberStartNumber());//Default value = 1 will be used 
 		assertTrue(TextAutoNumberSchemeEnum.ANM_ArabicPeriod == autoNumbersOfTextBox0[0].getAutoNumberScheme());
 		
-		final List textProps = textParass.get(1).get(0).getStyleTextPropAtom().getCharacterStyles();
-		assertEquals(1, textProps.size());
-		final TextPropCollection textProp = textProps.get(0);
-		assertEquals(67, textProp.getCharactersCovered());
+		int chCovered = 0;
+		for (HSLFTextParagraph htp : textParass.get(1)) {
+    		for (HSLFTextRun htr : htp.getTextRuns()) {
+    		    TextPropCollection textProp = htr.getCharacterStyle();
+    		    chCovered += textProp.getCharactersCovered();
+    		}
+		}
+		assertEquals(67, chCovered);
 		
 		assertTrue(textParass.get(0).get(0).isBullet());
 		
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
index 2410d6ca65..8e5652fb81 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPicture.java
@@ -17,18 +17,11 @@
 
 package org.apache.poi.hslf.usermodel;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertSame;
-
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
+import static org.junit.Assert.*;
+
+import java.awt.*;
 import java.awt.image.BufferedImage;
-import java.io.ByteArrayInputStream;
-import java.io.File;
-import java.io.InputStream;
+import java.io.*;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -36,8 +29,12 @@ import javax.imageio.ImageIO;
 
 import org.apache.poi.POIDataSamples;
 import org.apache.poi.ddf.EscherBSERecord;
-import org.apache.poi.hslf.usermodel.*;
+import org.apache.poi.hssf.usermodel.DummyGraphics2d;
+import org.apache.poi.sl.draw.Drawable;
+import org.apache.poi.sl.usermodel.Slide;
+import org.apache.poi.sl.usermodel.SlideShow;
 import org.apache.poi.util.JvmBugs;
+import org.apache.poi.xslf.usermodel.XMLSlideShow;
 import org.junit.Ignore;
 import org.junit.Test;
 
@@ -143,45 +140,42 @@ public final class TestPicture {
     }
 
     @Test
-    @Ignore("Just for visual validation - antialiasing is different on various systems")
+    // @Ignore("Just for visual validation - antialiasing is different on various systems")
     public void bug54541() throws Exception {
-//        InputStream xis = _slTests.openResourceAsStream("54542_cropped_bitmap.pptx");
-//        XMLSlideShow xss = new XMLSlideShow(xis);
-//        xis.close();
-//        
-//        Dimension xpg = xss.getPageSize();
-//        for(XSLFSlide slide : xss.getSlides()) {
-//            BufferedImage img = new BufferedImage(xpg.width, xpg.height, BufferedImage.TYPE_INT_RGB);
-//            Graphics2D graphics = img.createGraphics();
-//            fixFonts(graphics);
-//            slide.draw(graphics);
-//            ImageIO.write(img, "PNG", new File("testx.png"));
-//        }
-//
-//        System.out.println("########################");
-        
-        InputStream is = _slTests.openResourceAsStream("54541_cropped_bitmap.ppt");
-        HSLFSlideShow ss = new HSLFSlideShow(is);
+        String file = new String[]{
+            "54542_cropped_bitmap.pptx",
+            "54541_cropped_bitmap.ppt",
+            "54541_cropped_bitmap2.ppt",
+            "sample_pptx_grouping_issues.pptx"
+        }[3];
+        InputStream is = _slTests.openResourceAsStream(file);
+        SlideShow ss = file.endsWith("pptx") ? new XMLSlideShow(is) : new HSLFSlideShow(is);
         is.close();
         
+        boolean debugOut = false;
         Dimension pg = ss.getPageSize();
         int i=1;
-        for(HSLFSlide slide : ss.getSlides()) {
-            BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB);
-            Graphics2D graphics = img.createGraphics();
-            fixFonts(graphics);
-            slide.draw(graphics);
-            ImageIO.write(img, "PNG", new File("test"+(i++)+".png"));
+        for(Slide slide : ss.getSlides()) {
+            if (debugOut) {
+                DummyGraphics2d graphics = new DummyGraphics2d();
+                slide.draw(graphics);
+            } else {
+                BufferedImage img = new BufferedImage(pg.width, pg.height, BufferedImage.TYPE_INT_RGB);
+                Graphics2D graphics = img.createGraphics();
+                fixFonts(graphics);
+                slide.draw(graphics);
+                ImageIO.write(img, "PNG", new File("test"+(i++)+"hslf.png"));
+            }
         }
     }
     
     @SuppressWarnings("unchecked")
     private void fixFonts(Graphics2D graphics) {
         if (!JvmBugs.hasLineBreakMeasurerBug()) return;
-        Map fontMap = (Map)graphics.getRenderingHint(TextPainter.KEY_FONTMAP);
+        Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP);
         if (fontMap == null) fontMap = new HashMap();
         fontMap.put("Calibri", "Lucida Sans");
         fontMap.put("Cambria", "Lucida Bright");
-        graphics.setRenderingHint(TextPainter.KEY_FONTMAP, fontMap);        
+        graphics.setRenderingHint(Drawable.FONT_MAP, fontMap);        
     }
 }
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 25f61e1b10..61c309106d 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestPictures.java
@@ -22,17 +22,12 @@ import static org.junit.Assert.assertArrayEquals;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.net.URL;
+import java.util.List;
 
 import junit.framework.TestCase;
 
 import org.apache.poi.POIDataSamples;
-import org.apache.poi.hslf.blip.DIB;
-import org.apache.poi.hslf.blip.EMF;
-import org.apache.poi.hslf.blip.JPEG;
-import org.apache.poi.hslf.blip.PICT;
-import org.apache.poi.hslf.blip.PNG;
-import org.apache.poi.hslf.blip.WMF;
-import org.apache.poi.hslf.model.*;
+import org.apache.poi.hslf.blip.*;
 
 /**
  * Test adding/reading pictures
@@ -65,9 +60,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can read this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -110,9 +105,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can read this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -156,9 +151,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can get this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -195,9 +190,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can read this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -235,9 +230,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can read this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -274,9 +269,9 @@ public final class TestPictures extends TestCase{
         ppt = new HSLFSlideShow(new HSLFSlideShowImpl(new ByteArrayInputStream(out.toByteArray())));
 
         //make sure we can read this picture shape and it refers to the correct picture data
-        HSLFShape[] sh = ppt.getSlides()[0].getShapes();
-        assertEquals(1, sh.length);
-        pict = (HSLFPictureShape)sh[0];
+        List sh = ppt.getSlides().get(0).getShapes();
+        assertEquals(1, sh.size());
+        pict = (HSLFPictureShape)sh.get(0);
         assertEquals(idx, pict.getPictureIndex());
 
         //check picture data
@@ -302,11 +297,11 @@ public final class TestPictures extends TestCase{
         HSLFPictureData pdata;
 
         HSLFSlideShow ppt = new HSLFSlideShow(slTests.openResourceAsStream("pictures.ppt"));
-        HSLFSlide[] slides = ppt.getSlides();
+        List slides = ppt.getSlides();
         HSLFPictureData[] pictures = ppt.getPictureData();
         assertEquals(5, pictures.length);
 
-        pict = (HSLFPictureShape)slides[0].getShapes()[0]; //the first slide contains JPEG
+        pict = (HSLFPictureShape)slides.get(0).getShapes().get(0); //the first slide contains JPEG
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof JPEG);
         assertEquals(HSLFPictureShape.JPEG, pdata.getType());
@@ -314,7 +309,7 @@ public final class TestPictures extends TestCase{
         ppt_bytes = slTests.readFile("clock.jpg");
         assertArrayEquals(src_bytes, ppt_bytes);
 
-        pict = (HSLFPictureShape)slides[1].getShapes()[0]; //the second slide contains PNG
+        pict = (HSLFPictureShape)slides.get(1).getShapes().get(0); //the second slide contains PNG
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof PNG);
         assertEquals(HSLFPictureShape.PNG, pdata.getType());
@@ -322,7 +317,7 @@ public final class TestPictures extends TestCase{
         ppt_bytes = slTests.readFile("tomcat.png");
         assertArrayEquals(src_bytes, ppt_bytes);
 
-        pict = (HSLFPictureShape)slides[2].getShapes()[0]; //the third slide contains WMF
+        pict = (HSLFPictureShape)slides.get(2).getShapes().get(0); //the third slide contains WMF
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(HSLFPictureShape.WMF, pdata.getType());
@@ -336,7 +331,7 @@ public final class TestPictures extends TestCase{
         System.arraycopy(ppt_bytes, 22, b2, 0, b2.length);
         assertArrayEquals(b1, b2);
 
-        pict = (HSLFPictureShape)slides[3].getShapes()[0]; //the forth slide contains PICT
+        pict = (HSLFPictureShape)slides.get(3).getShapes().get(0); //the forth slide contains PICT
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof PICT);
         assertEquals(HSLFPictureShape.PICT, pdata.getType());
@@ -350,7 +345,7 @@ public final class TestPictures extends TestCase{
         System.arraycopy(ppt_bytes, 512, b2, 0, b2.length);
         assertArrayEquals(b1, b2);
 
-        pict = (HSLFPictureShape)slides[4].getShapes()[0]; //the fifth slide contains EMF
+        pict = (HSLFPictureShape)slides.get(4).getShapes().get(0); //the fifth slide contains EMF
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof EMF);
         assertEquals(HSLFPictureShape.EMF, pdata.getType());
@@ -375,20 +370,20 @@ public final class TestPictures extends TestCase{
 
 		// Now test what happens when we use the SlideShow interface
 		HSLFSlideShow ppt = new HSLFSlideShow(hslf);
-        HSLFSlide[] slides = ppt.getSlides();
+        List slides = ppt.getSlides();
         HSLFPictureData[] pictures = ppt.getPictureData();
-        assertEquals(12, slides.length);
+        assertEquals(12, slides.size());
         assertEquals(2, pictures.length);
 
 		HSLFPictureShape pict;
 		HSLFPictureData pdata;
 
-        pict = (HSLFPictureShape)slides[0].getShapes()[1]; // 2nd object on 1st slide
+        pict = (HSLFPictureShape)slides.get(0).getShapes().get(1); // 2nd object on 1st slide
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(HSLFPictureShape.WMF, pdata.getType());
 
-        pict = (HSLFPictureShape)slides[0].getShapes()[2]; // 3rd object on 1st slide
+        pict = (HSLFPictureShape)slides.get(0).getShapes().get(2); // 3rd object on 1st slide
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(HSLFPictureShape.WMF, pdata.getType());
@@ -411,20 +406,20 @@ public final class TestPictures extends TestCase{
 
 		// Now test what happens when we use the SlideShow interface
 		HSLFSlideShow ppt = new HSLFSlideShow(hslf);
-        HSLFSlide[] slides = ppt.getSlides();
+        List slides = ppt.getSlides();
         HSLFPictureData[] pictures = ppt.getPictureData();
-        assertEquals(27, slides.length);
+        assertEquals(27, slides.size());
         assertEquals(2, pictures.length);
 
 		HSLFPictureShape pict;
 		HSLFPictureData pdata;
 
-        pict = (HSLFPictureShape)slides[6].getShapes()[13];
+        pict = (HSLFPictureShape)slides.get(6).getShapes().get(13);
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(HSLFPictureShape.WMF, pdata.getType());
 
-        pict = (HSLFPictureShape)slides[7].getShapes()[13];
+        pict = (HSLFPictureShape)slides.get(7).getShapes().get(13);
         pdata = pict.getPictureData();
         assertTrue(pdata instanceof WMF);
         assertEquals(HSLFPictureShape.WMF, pdata.getType());
@@ -446,9 +441,9 @@ public final class TestPictures extends TestCase{
 
     public void testGetPictureName() throws Exception {
         HSLFSlideShow ppt = new HSLFSlideShow(slTests.openResourceAsStream("ppt_with_png.ppt"));
-        HSLFSlide slide = ppt.getSlides()[0];
+        HSLFSlide slide = ppt.getSlides().get(0);
 
-        HSLFPictureShape p = (HSLFPictureShape)slide.getShapes()[0]; //the first slide contains JPEG
+        HSLFPictureShape p = (HSLFPictureShape)slide.getShapes().get(0); //the first slide contains JPEG
         assertEquals("test", p.getPictureName());
     }
 
@@ -469,7 +464,7 @@ public final class TestPictures extends TestCase{
 
         ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));
 
-        HSLFPictureShape p = (HSLFPictureShape)ppt.getSlides()[0].getShapes()[0];
+        HSLFPictureShape p = (HSLFPictureShape)ppt.getSlides().get(0).getShapes().get(0);
         assertEquals("tomcat.png", p.getPictureName());
     }
 }
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTable.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTable.java
index 4a456dc771..2270cd420f 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTable.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTable.java
@@ -19,10 +19,12 @@
 
 package org.apache.poi.hslf.usermodel;
 
-import junit.framework.TestCase;
+import static org.junit.Assert.*;
+
+import java.util.List;
 
-import org.apache.poi.hslf.model.Table;
 import org.apache.poi.POIDataSamples;
+import org.junit.Test;
 
 
 /**
@@ -30,40 +32,39 @@ import org.apache.poi.POIDataSamples;
  * 
  * @author Alex Nikiforov [mailto:anikif@gmail.com]
  */
-public final class TestTable extends TestCase {
+public class TestTable {
     private static POIDataSamples _slTests = POIDataSamples.getSlideShowInstance();
 
-	protected void setUp() throws Exception {
-	}
-
-	public void testTable() throws Exception {
+    @Test
+    public void testTable() throws Exception {
 		HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("54111.ppt"));
 		assertTrue("No Exceptions while reading file", true);
 
-		final HSLFSlide[] slides = ppt.getSlides();
-		assertEquals(1, slides.length);
-		checkSlide(slides[0]);
+		List slides = ppt.getSlides();
+		assertEquals(1, slides.size());
+		checkSlide(slides.get(0));
 	}
+	
 	private void checkSlide(final HSLFSlide s) {
-		HSLFTextParagraph[] textRuns = s.getTextParagraphs();
-		assertEquals(2, textRuns.length);
+		List> textRuns = s.getTextParagraphs();
+		assertEquals(2, textRuns.size());
 
-		HSLFTextRun textRun = textRuns[0].getTextRuns()[0];
+		HSLFTextRun textRun = textRuns.get(0).get(0).getTextRuns().get(0);
 		assertEquals("Table sample", textRun.getRawText().trim());
-		assertEquals(1, textRuns[0].getTextRuns().length);
-		assertFalse(textRun.isBullet());
+		assertEquals(1, textRuns.get(0).get(0).getTextRuns().size());
+		assertFalse(textRun.getTextParagraph().isBullet());
 
-		assertEquals("Dummy text", textRuns[1].getRawText());
+		assertEquals("Dummy text", HSLFTextParagraph.getRawText(textRuns.get(1)));
 		
-		final HSLFShape[] shapes = s.getShapes();
+		List shapes = s.getShapes();
 		assertNotNull(shapes);
-		assertEquals(3, shapes.length);
-		assertTrue(shapes[2] instanceof Table);
-		final Table table = (Table) shapes[2];
+		assertEquals(3, shapes.size());
+		assertTrue(shapes.get(2) instanceof HSLFTable);
+		final HSLFTable table = (HSLFTable) shapes.get(2);
 		assertEquals(4, table.getNumberOfColumns());
 		assertEquals(6, table.getNumberOfRows());
 		for (int x = 0; x < 4; x ++) {
-			assertEquals("TH Cell " + (x + 1), table.getCell(0, x).getTextParagraphs().getRawText());
+			assertEquals("TH Cell " + (x + 1), HSLFTextParagraph.getRawText(table.getCell(0, x).getTextParagraphs()));
 			for (int y = 1; y < 6; y++) {
 				assertEquals("Row " + y + ", Cell " + (x + 1), table.getCell(y, x).getText());
 			}
diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
index 3b1313e4ea..9dda9af244 100644
--- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
+++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestTextRun.java
@@ -19,10 +19,12 @@ package org.apache.poi.hslf.usermodel;
 
 import static org.junit.Assert.*;
 
-import java.io.IOException;
+import java.awt.Color;
+import java.io.*;
 import java.util.List;
 
 import org.apache.poi.POIDataSamples;
+import org.apache.poi.hslf.model.textproperties.TextPropCollection;
 import org.apache.poi.hslf.record.*;
 import org.junit.Before;
 import org.junit.Test;
@@ -99,7 +101,7 @@ public final class TestTextRun {
 
 		// Ensure trailing \n's are NOT stripped, it is legal to set a text with a trailing '\r'
 		tr.setText(changeTo + "\n");
-		assertEquals(changeTo + "\n", tr.getRawText());
+		assertEquals(changeTo + "\r", tr.getRawText());
 	}
 
 	/**
@@ -121,7 +123,6 @@ public final class TestTextRun {
 		    else if (r instanceof TextBytesAtom) tba = (TextBytesAtom)r;
 		    else if (r instanceof TextCharsAtom) tca = (TextCharsAtom)r;
 		}
-		
 
 		// Bytes -> Bytes
 		assertNull(tca);
@@ -197,18 +198,13 @@ public final class TestTextRun {
 		List trB = textParass.get(1);
 
 		assertEquals(1, trA.size());
-		assertEquals(1, trB.size());
+		assertEquals(2, trB.size());
 
 		HSLFTextRun rtrA = trA.get(0).getTextRuns().get(0);
 		HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
 
 		assertEquals(HSLFTextParagraph.getRawText(trA), rtrA.getRawText());
-		assertEquals(HSLFTextParagraph.getRawText(trB), rtrB.getRawText());
-
-//		assertNull(rtrA._getRawCharacterStyle());
-//		assertNull(rtrA._getRawParagraphStyle());
-//		assertNull(rtrB._getRawCharacterStyle());
-//		assertNull(rtrB._getRawParagraphStyle());
+		assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
 	}
 
 	/**
@@ -284,39 +280,39 @@ public final class TestTextRun {
 		HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
 		HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
 		HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
-//		TextPropCollection tpBP = rtrB._getRawParagraphStyle();
-//		TextPropCollection tpBC = rtrB._getRawCharacterStyle();
-//		TextPropCollection tpCP = rtrC._getRawParagraphStyle();
-//		TextPropCollection tpCC = rtrC._getRawCharacterStyle();
-//		TextPropCollection tpDP = rtrD._getRawParagraphStyle();
-//		TextPropCollection tpDC = rtrD._getRawCharacterStyle();
+		TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpBC = rtrB.getCharacterStyle();
+		TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpCC = rtrC.getCharacterStyle();
+		TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpDC = rtrD.getCharacterStyle();
 
 //		assertEquals(trB.getRawText().substring(0, 30), rtrB.getRawText());
-//		assertNotNull(tpBP);
-//		assertNotNull(tpBC);
-//		assertNotNull(tpCP);
-//		assertNotNull(tpCC);
-//		assertNotNull(tpDP);
-//		assertNotNull(tpDC);
-//		assertTrue(tpBP.equals(tpCP));
-//		assertTrue(tpBP.equals(tpDP));
-//		assertTrue(tpCP.equals(tpDP));
-//		assertFalse(tpBC.equals(tpCC));
-//		assertFalse(tpBC.equals(tpDC));
-//		assertFalse(tpCC.equals(tpDC));
+		assertNotNull(tpBP);
+		assertNotNull(tpBC);
+		assertNotNull(tpCP);
+		assertNotNull(tpCC);
+		assertNotNull(tpDP);
+		assertNotNull(tpDC);
+		assertEquals(tpBP,tpCP);
+		assertEquals(tpBP,tpDP);
+		assertEquals(tpCP,tpDP);
+		assertNotEquals(tpBC,tpCC);
+		assertNotEquals(tpBC,tpDC);
+		assertNotEquals(tpCC,tpDC);
 
 		// Change text via normal
-//		trB.setText("Test Foo Test");
+		HSLFTextParagraph.setText(trB, "Test Foo Test");
 
 		// Ensure now have first style
-//		assertEquals(1, trB.getTextRuns().length);
-//		rtrB = trB.getTextRuns().get(0);
-//		assertEquals("Test Foo Test", trB.getRawText());
-//		assertEquals("Test Foo Test", rtrB.getRawText());
-//		assertNotNull(rtrB._getRawCharacterStyle());
-//		assertNotNull(rtrB._getRawParagraphStyle());
-//		assertEquals( tpBP, rtrB._getRawParagraphStyle() );
-//		assertEquals( tpBC, rtrB._getRawCharacterStyle() );
+		assertEquals(1, trB.get(0).getTextRuns().size());
+		rtrB = trB.get(0).getTextRuns().get(0);
+		assertEquals("Test Foo Test", HSLFTextParagraph.getRawText(trB));
+		assertEquals("Test Foo Test", rtrB.getRawText());
+		assertNotNull(rtrB.getCharacterStyle());
+		assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
+		assertEquals( tpBP, rtrB.getTextParagraph().getParagraphStyle() );
+		assertEquals( tpBC, rtrB.getCharacterStyle() );
 	}
 
 	/**
@@ -328,21 +324,21 @@ public final class TestTextRun {
 		HSLFSlide slideOne = ss.getSlides().get(0);
 		List> textRuns = slideOne.getTextParagraphs();
 		List trB = textRuns.get(1);
-//		assertEquals(1, trB.getTextRuns().length);
-//
-//		HSLFTextRun rtrB = trB.getTextRuns().get(0);
-//		assertEquals(trB.getRawText(), rtrB.getRawText());
-//		assertNull(rtrB._getRawCharacterStyle());
-//		assertNull(rtrB._getRawParagraphStyle());
+		assertEquals(1, trB.get(0).getTextRuns().size());
+
+		HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
+		assertEquals(HSLFTextParagraph.getRawText(trB.subList(0, 1)), rtrB.getRawText());
+		assertNotNull(rtrB.getCharacterStyle());
+		assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
 
 		// Change text via rich
-//		rtrB.setText("Test Test Test");
-//		assertEquals("Test Test Test", trB.getRawText());
-//		assertEquals("Test Test Test", rtrB.getRawText());
+		rtrB.setText("Test Test Test");
+		assertEquals("Test Test Test", HSLFTextParagraph.getRawText(trB.subList(0, 1)));
+		assertEquals("Test Test Test", rtrB.getRawText());
 
 		// Will now have dummy props
-//		assertNotNull(rtrB._getRawCharacterStyle());
-//		assertNotNull(rtrB._getRawParagraphStyle());
+        assertNotNull(rtrB.getCharacterStyle());
+        assertNotNull(rtrB.getTextParagraph().getParagraphStyle());
 	}
 
 	/**
@@ -357,31 +353,31 @@ public final class TestTextRun {
 		assertEquals(3, trB.size());
 
 		// We start with 3 text runs, each with their own set of styles,
-		//  but all sharing the same paragraph styles
+		// but all sharing the same paragraph styles
 		HSLFTextRun rtrB = trB.get(0).getTextRuns().get(0);
 		HSLFTextRun rtrC = trB.get(1).getTextRuns().get(0);
 		HSLFTextRun rtrD = trB.get(2).getTextRuns().get(0);
-//		TextPropCollection tpBP = rtrB._getRawParagraphStyle();
-//		TextPropCollection tpBC = rtrB._getRawCharacterStyle();
-//		TextPropCollection tpCP = rtrC._getRawParagraphStyle();
-//		TextPropCollection tpCC = rtrC._getRawCharacterStyle();
-//		TextPropCollection tpDP = rtrD._getRawParagraphStyle();
-//		TextPropCollection tpDC = rtrD._getRawCharacterStyle();
+		TextPropCollection tpBP = rtrB.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpBC = rtrB.getCharacterStyle();
+		TextPropCollection tpCP = rtrC.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpCC = rtrC.getCharacterStyle();
+		TextPropCollection tpDP = rtrD.getTextParagraph().getParagraphStyle();
+		TextPropCollection tpDC = rtrD.getCharacterStyle();
 
 		// Check text and stylings
-//		assertEquals(trB.getRawText().substring(0, 30), rtrB.getRawText());
-//		assertNotNull(tpBP);
-//		assertNotNull(tpBC);
-//		assertNotNull(tpCP);
-//		assertNotNull(tpCC);
-//		assertNotNull(tpDP);
-//		assertNotNull(tpDC);
-//		assertTrue(tpBP.equals(tpCP));
-//		assertTrue(tpBP.equals(tpDP));
-//		assertTrue(tpCP.equals(tpDP));
-//		assertFalse(tpBC.equals(tpCC));
-//		assertFalse(tpBC.equals(tpDC));
-//		assertFalse(tpCC.equals(tpDC));
+		assertEquals(HSLFTextParagraph.getRawText(trB).substring(0, 30), rtrB.getRawText());
+		assertNotNull(tpBP);
+		assertNotNull(tpBC);
+		assertNotNull(tpCP);
+		assertNotNull(tpCC);
+		assertNotNull(tpDP);
+		assertNotNull(tpDC);
+		assertEquals(tpBP, tpCP);
+		assertEquals(tpBP, tpDP);
+		assertEquals(tpCP, tpDP);
+		assertNotEquals(tpBC, tpCC);
+		assertNotEquals(tpBC, tpDC);
+		assertNotEquals(tpCC, tpDC);
 
 		// Check text in the rich runs
 		assertEquals("This is the subtitle, in bold\r", rtrB.getRawText());
@@ -394,32 +390,32 @@ public final class TestTextRun {
 		rtrB.setText(newBText);
 		rtrC.setText(newCText);
 		rtrD.setText(newDText);
-		assertEquals(newBText, rtrB.getRawText());
-		assertEquals(newCText, rtrC.getRawText());
-		assertEquals(newDText, rtrD.getRawText());
+		HSLFTextParagraph.storeText(trB);
 
-//		assertEquals(newBText + newCText + newDText, trB.getRawText());
+		assertEquals(newBText.replace('\n','\r'), rtrB.getRawText());
+		assertEquals(newCText.replace('\n','\r'), rtrC.getRawText());
+		assertEquals(newDText.replace('\n','\r'), rtrD.getRawText());
 
-		// The styles should have been updated for the new sizes
-//		assertEquals(newBText.length(), tpBC.getCharactersCovered());
-//		assertEquals(newCText.length(), tpCC.getCharactersCovered());
-//		assertEquals(newDText.length()+1, tpDC.getCharactersCovered()); // Last one is always one larger
+		assertEquals(newBText.replace('\n','\r') + newCText.replace('\n','\r') + newDText.replace('\n','\r'), HSLFTextParagraph.getRawText(trB));
 
-//		assertEquals(
-//				newBText.length() + newCText.length() + newDText.length(),
-//				tpBP.getCharactersCovered()
-//		);
+		// The styles should have been updated for the new sizes
+		assertEquals(newBText.length(), tpBC.getCharactersCovered());
+		assertEquals(newCText.length(), tpCC.getCharactersCovered());
+		assertEquals(newDText.length()+1, tpDC.getCharactersCovered()); // Last one is always one larger
 
-		// Paragraph style should be sum of text length
-//		assertEquals(newBText.length() + newCText.length() + newDText.length(), tpBP.getCharactersCovered());
+        // Paragraph style should be sum of text length
+		assertEquals(
+			newBText.length() + newCText.length() + newDText.length() +1,
+			tpBP.getCharactersCovered() + tpCP.getCharactersCovered() + tpDP.getCharactersCovered()
+		);
 
 		// Check stylings still as expected
-//		TextPropCollection ntpBC = rtrB._getRawCharacterStyle();
-//		TextPropCollection ntpCC = rtrC._getRawCharacterStyle();
-//		TextPropCollection ntpDC = rtrD._getRawCharacterStyle();
-//		assertEquals(tpBC.getTextPropList(), ntpBC.getTextPropList());
-//		assertEquals(tpCC.getTextPropList(), ntpCC.getTextPropList());
-//		assertEquals(tpDC.getTextPropList(), ntpDC.getTextPropList());
+		TextPropCollection ntpBC = rtrB.getCharacterStyle();
+		TextPropCollection ntpCC = rtrC.getCharacterStyle();
+		TextPropCollection ntpDC = rtrD.getCharacterStyle();
+		assertEquals(tpBC.getTextPropList(), ntpBC.getTextPropList());
+		assertEquals(tpCC.getTextPropList(), ntpCC.getTextPropList());
+		assertEquals(tpDC.getTextPropList(), ntpDC.getTextPropList());
 	}
 
 
@@ -467,82 +463,86 @@ public final class TestTextRun {
 		assertEquals(0, slide.getTextParagraphs().size());
 
 		HSLFTextBox shape1 = new HSLFTextBox();
-//		HSLFTextParagraph run1 = shape1.getTextParagraphs();
-//		assertSame(run1, shape1.createTextRun());
-//		run1.setText("Text 1");
+		List run1 = shape1.getTextParagraphs();
+		shape1.setText("Text 1");
 		slide.addShape(shape1);
 
 		//The array of Slide's text runs must be updated when new text shapes are added.
-//		HSLFTextParagraph[] runs = slide.getTextParagraphs();
-//		assertNotNull(runs);
-//		assertSame(run1, runs.get(0));
-//
-//		HSLFTextBox shape2 = new HSLFTextBox();
-//		HSLFTextParagraph run2 = shape2.getTextParagraphs();
-//		assertSame(run2, shape2.createTextRun());
-//		run2.setText("Text 2");
-//		slide.addShape(shape2);
-//
-//		runs = slide.getTextParagraphs();
-//		assertEquals(2, runs.length);
-//
-//		assertSame(run1, runs.get(0));
-//		assertSame(run2, runs.get(1));
-//
-//		//as getShapes()
-//		HSLFShape[] sh = slide.getShapes();
-//		assertEquals(2, sh.length);
-//		assertTrue(sh.get(0) instanceof HSLFTextBox);
-//		HSLFTextBox box1 = (HSLFTextBox)sh.get(0);
-//		assertSame(run1, box1.getTextParagraphs());
-//		HSLFTextBox box2 = (HSLFTextBox)sh.get(1);
-//		assertSame(run2, box2.getTextParagraphs());
-//
-//		//test Table - a complex group of shapes containing text objects
-//		HSLFSlide slide2 = ppt.createSlide();
-//		assertNull(slide2.getTextParagraphs());
-//		Table table = new Table(2, 2);
-//		slide2.addShape(table);
-//		runs = slide2.getTextParagraphs();
-//		assertNotNull(runs);
-//		assertEquals(4, runs.length);
+		List> runs = slide.getTextParagraphs();
+		assertNotNull(runs);
+		assertSame(run1, runs.get(0));
+
+		HSLFTextBox shape2 = new HSLFTextBox();
+		List run2 = shape2.getTextParagraphs();
+		shape2.setText("Text 2");
+		slide.addShape(shape2);
+
+		runs = slide.getTextParagraphs();
+		assertEquals(2, runs.size());
+
+		assertSame(run1, runs.get(0));
+		assertSame(run2, runs.get(1));
+
+		// as getShapes()
+		List sh = slide.getShapes();
+		assertEquals(2, sh.size());
+		assertTrue(sh.get(0) instanceof HSLFTextBox);
+		HSLFTextBox box1 = (HSLFTextBox)sh.get(0);
+		assertSame(run1, box1.getTextParagraphs());
+		HSLFTextBox box2 = (HSLFTextBox)sh.get(1);
+		assertSame(run2, box2.getTextParagraphs());
+
+		// test Table - a complex group of shapes containing text objects
+		HSLFSlide slide2 = ppt.createSlide();
+		assertTrue(slide2.getTextParagraphs().isEmpty());
+		HSLFTable table = new HSLFTable(2, 2);
+		slide2.addShape(table);
+		runs = slide2.getTextParagraphs();
+		assertNotNull(runs);
+		assertEquals(4, runs.size());
 	}
 
 	@Test
 	public void test48916() throws IOException {
-//        HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("SampleShow.ppt"));
-//        for(HSLFSlide slide : ppt.getSlides()){
-//            for(HSLFShape sh : slide.getShapes()){
-//                if(sh instanceof HSLFTextShape){
-//                    HSLFTextShape tx = (HSLFTextShape)sh;
-//                    HSLFTextParagraph run = tx.getTextParagraphs();
-//                    //verify that records cached in  TextRun and EscherTextboxWrapper are the same
-//                    Record[] runChildren = run.getRecords();
-//                    Record[] txboxChildren = tx.getEscherTextboxWrapper().getChildRecords();
-//                    assertEquals(runChildren.length, txboxChildren.length);
-//                    for(int i=0; i < txboxChildren.length; i++){
-//                        assertSame(txboxChildren.get(i), runChildren.get(i));
-//                    }
-//                    //caused NPE prior to fix of Bugzilla #48916 
-//                    run.getTextRuns().get(0).setBold(true);
-//                    run.getTextRuns().get(0).setFontColor(Color.RED);
-//                }
-//            }
-//        }
-//        ByteArrayOutputStream out = new ByteArrayOutputStream();
-//        ppt.write(out);
-//        ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));
-//        for(HSLFSlide slide : ppt.getSlides()){
-//            for(HSLFShape sh : slide.getShapes()){
-//                if(sh instanceof HSLFTextShape){
-//                    HSLFTextShape tx = (HSLFTextShape)sh;
-//                    HSLFTextParagraph run = tx.getTextParagraphs();
-//                    HSLFTextRun rt = run.getTextRuns().get(0);
-//                    assertTrue(rt.isBold());
-//                    assertEquals(rt.getFontColor(), Color.RED);
-//                }
-//            }
-//        }
+        HSLFSlideShow ppt = new HSLFSlideShow(_slTests.openResourceAsStream("SampleShow.ppt"));
+        List slides = ppt.getSlides();
+        for(HSLFSlide slide : slides){
+            for(HSLFShape sh : slide.getShapes()){
+                if (!(sh instanceof HSLFTextShape)) continue;
+                HSLFTextShape tx = (HSLFTextShape)sh;
+                List paras = tx.getTextParagraphs();
+                //verify that records cached in  TextRun and EscherTextboxWrapper are the same
+                Record[] runChildren = paras.get(0).getRecords();
+                Record[] txboxChildren = tx.getEscherTextboxWrapper().getChildRecords();
+                assertEquals(runChildren.length, txboxChildren.length);
+                for(int i=0; i < txboxChildren.length; i++){
+                    assertSame(txboxChildren[i], runChildren[i]);
+                }
+                // caused NPE prior to fix of Bugzilla #48916
+                for (HSLFTextParagraph p : paras) {
+                    for (HSLFTextRun rt : p.getTextRuns()) {
+                        rt.setBold(true);
+                        rt.setFontColor(Color.RED);
+                    }
+                }
+                tx.storeText();
+            }
+        }
+        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        ppt.write(out);
+        
+        ppt = new HSLFSlideShow(new ByteArrayInputStream(out.toByteArray()));
+        for(HSLFSlide slide : ppt.getSlides()){
+            for(HSLFShape sh : slide.getShapes()){
+                if(sh instanceof HSLFTextShape){
+                    HSLFTextShape tx = (HSLFTextShape)sh;
+                    List run = tx.getTextParagraphs();
+                    HSLFTextRun rt = run.get(0).getTextRuns().get(0);
+                    assertTrue(rt.isBold());
+                    assertEquals(rt.getFontColor(), Color.RED);
+                }
+            }
+        }
 
     }
 
diff --git a/test-data/slideshow/54541_cropped_bitmap2.ppt b/test-data/slideshow/54541_cropped_bitmap2.ppt
new file mode 100644
index 0000000000..3d79dbd33b
Binary files /dev/null and b/test-data/slideshow/54541_cropped_bitmap2.ppt differ
diff --git a/test-data/slideshow/sample_pptx_grouping_issues.pptx b/test-data/slideshow/sample_pptx_grouping_issues.pptx
new file mode 100644
index 0000000000..bf0c70253f
Binary files /dev/null and b/test-data/slideshow/sample_pptx_grouping_issues.pptx differ