From 4acc6a84a15d46cb8cf08beebf8472de72e898e2 Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Wed, 10 Jun 2015 22:23:47 +0000 Subject: [PATCH] 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 --- .../poi/hslf/examples/ApacheconEU08.java | 4 +- .../poi/hslf/examples/Graphics2DDemo.java | 4 +- .../apache/poi/hslf/examples/TableDemo.java | 8 +- .../poi/ddf/EscherChildAnchorRecord.java | 27 +- .../org/apache/poi/ddf/EscherProperties.java | 2 +- .../apache/poi/ddf/EscherTextboxRecord.java | 3 +- .../poi/hssf/usermodel/DummyGraphics2d.java | 17 +- .../poi/xslf/usermodel/XSLFGroupShape.java | 7 +- .../poi/xslf/usermodel/XSLFPictureShape.java | 1 + .../apache/poi/xslf/usermodel/XSLFSheet.java | 1 + .../apache/poi/xslf/usermodel/XSLFSlide.java | 1 - .../apache/poi/hslf/blip/BitmapPainter.java | 95 ------ .../apache/poi/hslf/blip/ImagePainter.java | 72 ---- .../hslf/extractor/PowerPointExtractor.java | 8 +- .../src/org/apache/poi/hslf/model/Line.java | 63 +++- .../textproperties/TextPropCollection.java | 1 + .../poi/hslf/record/EscherTextboxWrapper.java | 4 +- .../org/apache/poi/hslf/record/PPDrawing.java | 43 +-- .../poi/hslf/record/RecordContainer.java | 4 +- .../poi/hslf/record/SlideListWithText.java | 4 +- .../apache/poi/hslf/usermodel/HSLFFill.java | 5 +- .../poi/hslf/usermodel/HSLFGroupShape.java | 60 ++-- .../apache/poi/hslf/usermodel/HSLFNotes.java | 13 +- .../poi/hslf/usermodel/HSLFPictureData.java | 51 +-- .../poi/hslf/usermodel/HSLFPictureShape.java | 17 +- .../apache/poi/hslf/usermodel/HSLFShape.java | 29 +- .../poi/hslf/usermodel/HSLFShapeFactory.java | 2 +- .../apache/poi/hslf/usermodel/HSLFSheet.java | 9 +- .../poi/hslf/usermodel/HSLFSimpleShape.java | 159 +++++---- .../apache/poi/hslf/usermodel/HSLFSlide.java | 4 +- .../poi/hslf/usermodel/HSLFSlideMaster.java | 13 +- .../Table.java => usermodel/HSLFTable.java} | 37 ++- .../HSLFTableCell.java} | 35 +- .../poi/hslf/usermodel/HSLFTextParagraph.java | 276 ++++++++------- .../poi/hslf/usermodel/HSLFTextRun.java | 2 +- .../poi/hslf/usermodel/HSLFTextShape.java | 132 ++++---- .../poi/hslf/usermodel/HSLFTitleMaster.java | 8 +- .../org/apache/poi/sl/draw/DrawFactory.java | 8 +- ...rawShapeGroup.java => DrawGroupShape.java} | 7 +- .../src/org/apache/poi/sl/draw/DrawShape.java | 55 +-- .../src/org/apache/poi/sl/draw/Drawable.java | 17 + .../org/apache/poi/sl/draw/ImageRenderer.java | 21 +- .../{ShapeGroup.java => GroupShape.java} | 17 +- .../apache/poi/sl/usermodel/PictureShape.java | 10 +- .../org/apache/poi/sl/usermodel/Sheet.java | 9 + .../poi/hslf/model/AllHSLFModelTests.java | 1 - .../poi/hslf/model/TestImagePainter.java | 55 --- .../org/apache/poi/hslf/model/TestTable.java | 20 +- .../poi/hslf/usermodel/TestAddingSlides.java | 2 +- .../apache/poi/hslf/usermodel/TestBugs.java | 26 +- .../poi/hslf/usermodel/TestFontRendering.java | 27 +- .../poi/hslf/usermodel/TestNumberedList3.java | 12 +- .../poi/hslf/usermodel/TestPicture.java | 68 ++-- .../poi/hslf/usermodel/TestPictures.java | 79 +++-- .../apache/poi/hslf/usermodel/TestTable.java | 43 +-- .../poi/hslf/usermodel/TestTextRun.java | 314 +++++++++--------- test-data/slideshow/54541_cropped_bitmap2.ppt | Bin 0 -> 199168 bytes .../sample_pptx_grouping_issues.pptx | Bin 0 -> 39436 bytes 58 files changed, 976 insertions(+), 1036 deletions(-) delete mode 100644 src/scratchpad/src/org/apache/poi/hslf/blip/BitmapPainter.java delete mode 100644 src/scratchpad/src/org/apache/poi/hslf/blip/ImagePainter.java rename src/scratchpad/src/org/apache/poi/hslf/{model/Table.java => usermodel/HSLFTable.java} (91%) rename src/scratchpad/src/org/apache/poi/hslf/{model/TableCell.java => usermodel/HSLFTableCell.java} (81%) rename src/scratchpad/src/org/apache/poi/sl/draw/{DrawShapeGroup.java => DrawGroupShape.java} (87%) rename src/scratchpad/src/org/apache/poi/sl/usermodel/{ShapeGroup.java => GroupShape.java} (68%) delete mode 100644 src/scratchpad/testcases/org/apache/poi/hslf/model/TestImagePainter.java create mode 100644 test-data/slideshow/54541_cropped_bitmap2.ppt create mode 100644 test-data/slideshow/sample_pptx_grouping_issues.pptx 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 494c7f9c3..c27e8e4e8 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 19fe49676..cd900902d 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 abc40750c..b931ba1b5 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 b6193cea4..bbb84bae8 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 7ec219006..2bee6b92e 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 c21c22796..dbf28531f 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 beadac464..8ada60169 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 685ae46da..309f39fd9 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 b815e00d4..3d532bb95 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 43ec70130..e1fff264c 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 b6ee1bc8e..8b5e59670 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 cfeffb800..000000000 --- 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 76359dc30..000000000 --- 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 5dceebf9c..1167cc355 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 b28c88ec6..c77735172 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/textproperties/TextPropCollection.java b/src/scratchpad/src/org/apache/poi/hslf/model/textproperties/TextPropCollection.java index 0e46e6a7f..17d0c1d2c 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 715eb8179..10744e49d 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 09f21d1ba..09854b5ab 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 be732d44c..91a590afe 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 315455d90..195b59c08 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 9b74711fc..09d97a3b7 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 5e47776d6..5133ff122 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 b848a2cb9..230a6401d 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 c97c28168..03a9861ae 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 a6be80f92..a68f3a74e 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 0f9dd2815..9f715fb38 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 547e095ef..0247a7593 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 6c053d49f..5ddf311e5 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 048655fa6..e23875103 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 1c6518e5e..a6dfedf07 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/model/Table.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java similarity index 91% rename from src/scratchpad/src/org/apache/poi/hslf/model/Table.java rename to src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java index 8f3b245c1..b028758e6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/Table.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java @@ -15,9 +15,10 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hslf.model; +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; @@ -31,7 +32,7 @@ import java.awt.*; * * @author Yegor Kozlov */ -public final class Table extends HSLFGroupShape { +public final class HSLFTable extends HSLFGroupShape { protected static final int BORDER_TOP = 1; protected static final int BORDER_RIGHT = 2; @@ -44,7 +45,7 @@ public final class Table extends HSLFGroupShape { protected static final int BORDERS_NONE = 8; - protected TableCell[][] cells; + protected HSLFTableCell[][] cells; /** * Create a new Table of the given number of rows and columns @@ -52,23 +53,23 @@ public final class Table extends HSLFGroupShape { * @param numrows the number of rows * @param numcols the number of columns */ - public Table(int numrows, int numcols) { + 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 TableCell[numrows][numcols]; + 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 TableCell(this); - Rectangle anchor = new Rectangle(x, y, TableCell.DEFAULT_WIDTH, TableCell.DEFAULT_HEIGHT); + cells[i][j] = new HSLFTableCell(this); + Rectangle anchor = new Rectangle(x, y, HSLFTableCell.DEFAULT_WIDTH, HSLFTableCell.DEFAULT_HEIGHT); cells[i][j].setAnchor(anchor); - x += TableCell.DEFAULT_WIDTH; + x += HSLFTableCell.DEFAULT_WIDTH; } - y += TableCell.DEFAULT_HEIGHT; + y += HSLFTableCell.DEFAULT_HEIGHT; } tblWidth = x; tblHeight = y; @@ -94,7 +95,7 @@ public final class Table extends HSLFGroupShape { * @param escherRecord EscherSpContainer container which holds information about this shape * @param parent the parent of the shape */ - public Table(EscherContainerRecord escherRecord, ShapeContainer parent) { + public HSLFTable(EscherContainerRecord escherRecord, ShapeContainer parent) { super(escherRecord, parent); } @@ -105,7 +106,7 @@ public final class Table extends HSLFGroupShape { * @param col the column index (0-based) * @return the cell */ - public TableCell getCell(int row, int col) { + public HSLFTableCell getCell(int row, int col) { return cells[row][col]; } @@ -124,13 +125,13 @@ public final class Table extends HSLFGroupShape { 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]; + 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++) { - TableCell c = cells[i][j]; + HSLFTableCell c = cells[i][j]; addShape(c); Line bt = c.getBorderTop(); @@ -177,12 +178,12 @@ public final class Table extends HSLFGroupShape { maxrowlen = Math.max(maxrowlen, row.size()); } } - cells = new TableCell[lst.size()][maxrowlen]; + 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 TableCell(tx.getSpContainer(), getParent()); + cells[i][j] = new HSLFTableCell(tx.getSpContainer(), getParent()); cells[i][j].setSheet(tx.getSheet()); } } @@ -256,7 +257,7 @@ public final class Table extends HSLFGroupShape { 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]; + HSLFTableCell cell = cells[i][j]; cell.setBorderTop(cloneBorder(line)); cell.setBorderLeft(cloneBorder(line)); if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); @@ -273,7 +274,7 @@ public final class Table extends HSLFGroupShape { 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]; + HSLFTableCell cell = cells[i][j]; if(j == 0) cell.setBorderLeft(cloneBorder(line)); if(j == cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); @@ -300,7 +301,7 @@ public final class Table extends HSLFGroupShape { 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]; + HSLFTableCell cell = cells[i][j]; if(j != cells[i].length - 1) cell.setBorderRight(cloneBorder(line)); diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java similarity index 81% rename from src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java rename to src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java index cfdee7663..b1e7c3e4b 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/TableCell.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java @@ -15,15 +15,14 @@ limitations under the License. ==================================================================== */ -package org.apache.poi.hslf.model; +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.usermodel.HSLFShape; -import org.apache.poi.hslf.usermodel.HSLFTextBox; +import org.apache.poi.hslf.model.Line; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.ShapeType; @@ -32,7 +31,7 @@ import org.apache.poi.sl.usermodel.ShapeType; * * @author Yegor Kozlov */ -public final class TableCell extends HSLFTextBox { +public final class HSLFTableCell extends HSLFTextBox { protected static final int DEFAULT_WIDTH = 100; protected static final int DEFAULT_HEIGHT = 40; @@ -47,7 +46,7 @@ public final class TableCell extends HSLFTextBox { * @param escherRecord {@link EscherSpContainer} container which holds information about this shape * @param parent the parent of the shape */ - protected TableCell(EscherContainerRecord escherRecord, ShapeContainer parent){ + protected HSLFTableCell(EscherContainerRecord escherRecord, ShapeContainer parent){ super(escherRecord, parent); } @@ -57,7 +56,7 @@ public final class TableCell extends HSLFTextBox { * @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){ + public HSLFTableCell(ShapeContainer parent){ super(parent); setShapeType(ShapeType.RECT); @@ -81,25 +80,25 @@ public final class TableCell extends HSLFTextBox { Rectangle cellAnchor = getAnchor(); Rectangle lineAnchor = new Rectangle(); switch(type){ - case Table.BORDER_TOP: + case HSLFTable.BORDER_TOP: lineAnchor.x = cellAnchor.x; lineAnchor.y = cellAnchor.y; lineAnchor.width = cellAnchor.width; lineAnchor.height = 0; break; - case Table.BORDER_RIGHT: + case HSLFTable.BORDER_RIGHT: lineAnchor.x = cellAnchor.x + cellAnchor.width; lineAnchor.y = cellAnchor.y; lineAnchor.width = 0; lineAnchor.height = cellAnchor.height; break; - case Table.BORDER_BOTTOM: + case HSLFTable.BORDER_BOTTOM: lineAnchor.x = cellAnchor.x; lineAnchor.y = cellAnchor.y + cellAnchor.height; lineAnchor.width = cellAnchor.width; lineAnchor.height = 0; break; - case Table.BORDER_LEFT: + case HSLFTable.BORDER_LEFT: lineAnchor.x = cellAnchor.x; lineAnchor.y = cellAnchor.y; lineAnchor.width = 0; @@ -116,7 +115,7 @@ public final class TableCell extends HSLFTextBox { } public void setBorderLeft(Line line) { - if(line != null) anchorBorder(Table.BORDER_LEFT, line); + if(line != null) anchorBorder(HSLFTable.BORDER_LEFT, line); this.borderLeft = line; } @@ -125,7 +124,7 @@ public final class TableCell extends HSLFTextBox { } public void setBorderRight(Line line) { - if(line != null) anchorBorder(Table.BORDER_RIGHT, line); + if(line != null) anchorBorder(HSLFTable.BORDER_RIGHT, line); this.borderRight = line; } @@ -134,7 +133,7 @@ public final class TableCell extends HSLFTextBox { } public void setBorderTop(Line line) { - if(line != null) anchorBorder(Table.BORDER_TOP, line); + if(line != null) anchorBorder(HSLFTable.BORDER_TOP, line); this.borderTop = line; } @@ -143,16 +142,16 @@ public final class TableCell extends HSLFTextBox { } public void setBorderBottom(Line line) { - if(line != null) anchorBorder(Table.BORDER_BOTTOM, line); + if(line != null) anchorBorder(HSLFTable.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); + 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 ca6ddf6e8..f80bd3fc9 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 0fb443b60..1e1dca888 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 e05ae79a6..60e9eb84e 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 fc6131d95..b1e38d977 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 df4d635a6..6eb30eb70 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/DrawShapeGroup.java b/src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java similarity index 87% rename from src/scratchpad/src/org/apache/poi/sl/draw/DrawShapeGroup.java rename to src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java index ceda9280c..31f2496b3 100644 --- a/src/scratchpad/src/org/apache/poi/sl/draw/DrawShapeGroup.java +++ b/src/scratchpad/src/org/apache/poi/sl/draw/DrawGroupShape.java @@ -7,9 +7,9 @@ import java.awt.geom.Rectangle2D; import org.apache.poi.sl.usermodel.*; -public class DrawShapeGroup> extends DrawShape implements Drawable { +public class DrawGroupShape> extends DrawShape implements Drawable { - public DrawShapeGroup(T shape) { + public DrawGroupShape(T shape) { super(shape); } @@ -31,6 +31,7 @@ public class DrawShapeGroup> extends DrawS 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 @@ -46,7 +47,7 @@ public class DrawShapeGroup> extends DrawS 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 6cb60c9b0..2bebbfa89 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/Drawable.java b/src/scratchpad/src/org/apache/poi/sl/draw/Drawable.java index 8789082af..54128b82c 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 9704c2c0a..f4a298cdb 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/ShapeGroup.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java
similarity index 68%
rename from src/scratchpad/src/org/apache/poi/sl/usermodel/ShapeGroup.java
rename to src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java
index 7bd940c36..d71bb253f 100644
--- a/src/scratchpad/src/org/apache/poi/sl/usermodel/ShapeGroup.java
+++ b/src/scratchpad/src/org/apache/poi/sl/usermodel/GroupShape.java
@@ -19,6 +19,21 @@ package org.apache.poi.sl.usermodel;
 
 import java.awt.geom.Rectangle2D;
 
-public interface ShapeGroup extends Shape, ShapeContainer, PlaceableShape {
+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 970f04353..a2c0824e0 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/Sheet.java b/src/scratchpad/src/org/apache/poi/sl/usermodel/Sheet.java
index 6f4ba0ac6..f94b7727a 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 ca9e6c9a2..14301af2b 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 963beeefb..000000000
--- 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 0c5fcb69b..3337d58c3 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 c0634f05f..800df623e 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 12e3ff6c3..b7dba80b8 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 3638bad82..cc78643a8 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 ab358f65a..5ad2bac1c 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 2410d6ca6..8e5652fb8 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 static org.junit.Assert.*;
 
-import java.awt.Dimension;
-import java.awt.Graphics2D;
-import java.awt.Rectangle;
+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 25f61e1b1..61c309106 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 4a456dc77..2270cd420 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 3b1313e4e..9dda9af24 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());
+
+		assertEquals(newBText.replace('\n','\r') + newCText.replace('\n','\r') + newDText.replace('\n','\r'), HSLFTextParagraph.getRawText(trB));
 
 		// 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.length(), tpBC.getCharactersCovered());
+		assertEquals(newCText.length(), tpCC.getCharactersCovered());
+		assertEquals(newDText.length()+1, tpDC.getCharactersCovered()); // Last one is always one larger
 
-//		assertEquals(
-//				newBText.length() + newCText.length() + newDText.length(),
-//				tpBP.getCharactersCovered()
-//		);
-
-		// 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 0000000000000000000000000000000000000000..3d79dbd33b2db3de403c66ad0345d8f4aca336b9
GIT binary patch
literal 199168
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
J`VbHz{0|2N*d+h}

literal 0
HcmV?d00001

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 0000000000000000000000000000000000000000..bf0c70253f54147f479bad319a555855af4ce46d
GIT binary patch
literal 39436
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

literal 0
HcmV?d00001