From: Andreas Beeker Date: Wed, 8 Feb 2017 01:12:22 +0000 (+0000) Subject: #60625 - Rendering issue with background and shape overlayed by image X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=5c4ab1dbdf44cb9fdc9060c74a5ea5d033f3ae77;p=poi.git #60625 - Rendering issue with background and shape overlayed by image git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1782096 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java index 3bcedbe850..a39cf7d68e 100644 --- a/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java +++ b/src/java/org/apache/poi/sl/draw/DrawMasterSheet.java @@ -17,7 +17,13 @@ package org.apache.poi.sl.draw; -import org.apache.poi.sl.usermodel.*; +import java.awt.Graphics2D; + +import org.apache.poi.sl.usermodel.MasterSheet; +import org.apache.poi.sl.usermodel.Placeholder; +import org.apache.poi.sl.usermodel.Shape; +import org.apache.poi.sl.usermodel.SimpleShape; +import org.apache.poi.sl.usermodel.Slide; public class DrawMasterSheet extends DrawSheet { @@ -33,12 +39,15 @@ public class DrawMasterSheet extends DrawSheet { * for instance, slide masters and layouts don't display placeholders */ @Override - protected boolean canDraw(Shape shape) { + protected boolean canDraw(Graphics2D graphics, Shape shape) { if (shape instanceof SimpleShape) { + // in XSLF, slidenumber and date shapes aren't marked as placeholders opposed to HSLF Placeholder ph = ((SimpleShape)shape).getPlaceholder(); - return ph == null; - } else { - return true; + if (ph != null) { + Slide slide = (Slide)graphics.getRenderingHint(Drawable.CURRENT_SLIDE); + return slide.getDisplayPlaceholder(ph); + } } + return true; } } diff --git a/src/java/org/apache/poi/sl/draw/DrawShape.java b/src/java/org/apache/poi/sl/draw/DrawShape.java index b7b0915c18..bb1f490bf6 100644 --- a/src/java/org/apache/poi/sl/draw/DrawShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawShape.java @@ -38,18 +38,34 @@ public class DrawShape implements Drawable { this.shape = shape; } + /** + * Sometimes it's necessary to distinguish between XSLF/HSLF for the rendering. + * Use this method on the shape to determine, if we work on the BIFF implementation + * + * @param shape the shape to render + * @return {@code true} if HSLF implementation is used + */ + protected static boolean isHSLF(Shape shape) { + return shape.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf"); + } + /** * Apply 2-D transforms before drawing this shape. This includes rotation and flipping. * * @param graphics the graphics whos transform matrix will be modified */ + @Override public void applyTransform(Graphics2D graphics) { - if (!(shape instanceof PlaceableShape)) return; + if (!(shape instanceof PlaceableShape)) { + return; + } PlaceableShape ps = (PlaceableShape)shape; - final boolean isHSLF = ps.getClass().getCanonicalName().toLowerCase(Locale.ROOT).contains("hslf"); + final boolean isHSLF = isHSLF(shape); AffineTransform tx = (AffineTransform)graphics.getRenderingHint(Drawable.GROUP_TRANSFORM); - if (tx == null) tx = new AffineTransform(); + if (tx == null) { + tx = new AffineTransform(); + } final Rectangle2D anchor = tx.createTransformedShape(ps.getAnchor()).getBounds2D(); char cmds[] = isHSLF ? new char[]{ 'h','v','r' } : new char[]{ 'r','h','v' }; @@ -81,7 +97,9 @@ public class DrawShape implements Drawable { // normalize rotation rotation %= 360.; - if (rotation < 0) rotation += 360.; + if (rotation < 0) { + rotation += 360.; + } int quadrant = (((int)rotation+45)/90)%4; double scaleX = 1.0, scaleY = 1.0; @@ -148,9 +166,11 @@ public class DrawShape implements Drawable { return (dim2 == 0.) ? 1 : dim1/dim2; } + @Override public void draw(Graphics2D graphics) { } + @Override public void drawContent(Graphics2D graphics) { } @@ -176,7 +196,10 @@ public class DrawShape implements Drawable { protected static BasicStroke getStroke(StrokeStyle strokeStyle) { float lineWidth = (float) strokeStyle.getLineWidth(); - if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt + if (lineWidth == 0.0f) { + // Both PowerPoint and OOo draw zero-length lines as 0.25pt + lineWidth = 0.25f; + } LineDash lineDash = strokeStyle.getLineDash(); if (lineDash == null) { @@ -194,7 +217,9 @@ public class DrawShape implements Drawable { } LineCap lineCapE = strokeStyle.getLineCap(); - if (lineCapE == null) lineCapE = LineCap.FLAT; + if (lineCapE == null) { + lineCapE = LineCap.FLAT; + } int lineCap; switch (lineCapE) { case ROUND: diff --git a/src/java/org/apache/poi/sl/draw/DrawSheet.java b/src/java/org/apache/poi/sl/draw/DrawSheet.java index dbe82ea6c8..5c8d76abeb 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSheet.java +++ b/src/java/org/apache/poi/sl/draw/DrawSheet.java @@ -17,13 +17,14 @@ package org.apache.poi.sl.draw; -import java.awt.Dimension; import java.awt.Color; +import java.awt.Dimension; import java.awt.Graphics2D; - import java.awt.geom.AffineTransform; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.MasterSheet; +import org.apache.poi.sl.usermodel.Shape; +import org.apache.poi.sl.usermodel.Sheet; public class DrawSheet implements Drawable { @@ -34,6 +35,7 @@ public class DrawSheet implements Drawable { this.sheet = sheet; } + @Override public void draw(Graphics2D graphics) { Dimension dim = sheet.getSlideShow().getPageSize(); Color whiteTrans = new Color(1f,1f,1f,0f); @@ -51,7 +53,9 @@ public class DrawSheet implements Drawable { graphics.setRenderingHint(Drawable.GROUP_TRANSFORM, new AffineTransform()); for (Shape shape : sheet.getShapes()) { - if(!canDraw(shape)) continue; + if(!canDraw(graphics, shape)) { + continue; + } // remember the initial transform and restore it after we are done with drawing AffineTransform at = graphics.getTransform(); @@ -73,9 +77,11 @@ public class DrawSheet implements Drawable { } } + @Override public void applyTransform(Graphics2D context) { } + @Override public void drawContent(Graphics2D context) { } @@ -85,7 +91,7 @@ public class DrawSheet implements Drawable { * Subclasses can override it and skip certain shapes from drawings, * for instance, slide masters and layouts don't display placeholders */ - protected boolean canDraw(Shape shape){ + protected boolean canDraw(Graphics2D graphics, Shape shape){ return true; } } diff --git a/src/java/org/apache/poi/sl/draw/DrawSlide.java b/src/java/org/apache/poi/sl/draw/DrawSlide.java index ae4baa27fb..2320e907a6 100644 --- a/src/java/org/apache/poi/sl/draw/DrawSlide.java +++ b/src/java/org/apache/poi/sl/draw/DrawSlide.java @@ -29,6 +29,8 @@ public class DrawSlide extends DrawSheet { } public void draw(Graphics2D graphics) { + graphics.setRenderingHint(Drawable.CURRENT_SLIDE, this.sheet); + Background bg = sheet.getBackground(); if(bg != null) { DrawFactory drawFact = DrawFactory.getInstance(graphics); @@ -37,5 +39,6 @@ public class DrawSlide extends DrawSheet { } super.draw(graphics); + graphics.setRenderingHint(Drawable.CURRENT_SLIDE, null); } } diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java index b1fbd9b067..f2b24b0ff2 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -41,10 +41,12 @@ import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PlaceableShape; import org.apache.poi.sl.usermodel.ShapeContainer; import org.apache.poi.sl.usermodel.Sheet; +import org.apache.poi.sl.usermodel.Slide; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle; import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; import org.apache.poi.sl.usermodel.TextRun; +import org.apache.poi.sl.usermodel.TextRun.FieldType; import org.apache.poi.sl.usermodel.TextRun.TextCap; import org.apache.poi.sl.usermodel.TextShape; import org.apache.poi.sl.usermodel.TextShape.TextDirection; @@ -82,6 +84,7 @@ public class DrawTextParagraph implements Drawable { /** * Resolves instances being deserialized to the predefined constants. */ + @Override protected Object readResolve() throws InvalidObjectException { if (HYPERLINK_HREF.getName().equals(getName())) { return HYPERLINK_HREF; @@ -116,8 +119,11 @@ public class DrawTextParagraph implements Drawable { autoNbrIdx = index; } + @Override public void draw(Graphics2D graphics){ - if (lines.isEmpty()) return; + if (lines.isEmpty()) { + return; + } double penY = y; @@ -144,7 +150,9 @@ public class DrawTextParagraph implements Drawable { //The vertical line spacing Double spacing = paragraph.getLineSpacing(); - if (spacing == null) spacing = 100d; + if (spacing == null) { + spacing = 100d; + } for(DrawTextFragment line : lines){ double penX; @@ -176,7 +184,9 @@ public class DrawTextParagraph implements Drawable { double rightInset = insets.right; TextAlign ta = paragraph.getTextAlign(); - if (ta == null) ta = TextAlign.LEFT; + if (ta == null) { + ta = TextAlign.LEFT; + } switch (ta) { case CENTER: penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset - leftMargin) / 2; @@ -217,9 +227,11 @@ public class DrawTextParagraph implements Drawable { return (lines.isEmpty() || rawText.trim().isEmpty()); } + @Override public void applyTransform(Graphics2D graphics) { } + @Override public void drawContent(Graphics2D graphics) { } @@ -243,10 +255,14 @@ public class DrawTextParagraph implements Drawable { double wrappingWidth = getWrappingWidth(lines.size() == 0, graphics) + 1; // add a pixel to compensate rounding errors // shape width can be smaller that the sum of insets (this was proved by a test file) - if(wrappingWidth < 0) wrappingWidth = 1; + if(wrappingWidth < 0) { + wrappingWidth = 1; + } int nextBreak = text.indexOf("\n", startIndex + 1); - if (nextBreak == -1) nextBreak = it.getEndIndex(); + if (nextBreak == -1) { + nextBreak = it.getEndIndex(); + } TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true); if (layout == null) { @@ -279,7 +295,9 @@ public class DrawTextParagraph implements Drawable { maxLineHeight = Math.max(maxLineHeight, line.getHeight()); - if(endIndex == it.getEndIndex()) break; + if(endIndex == it.getEndIndex()) { + break; + } } rawText = text.toString(); @@ -287,7 +305,9 @@ public class DrawTextParagraph implements Drawable { protected DrawTextFragment getBullet(Graphics2D graphics, AttributedCharacterIterator firstLineAttr) { BulletStyle bulletStyle = paragraph.getBulletStyle(); - if (bulletStyle == null) return null; + if (bulletStyle == null) { + return null; + } String buCharacter; AutoNumberingScheme ans = bulletStyle.getAutoNumberingScheme(); @@ -296,10 +316,14 @@ public class DrawTextParagraph implements Drawable { } else { buCharacter = bulletStyle.getBulletCharacter(); } - if (buCharacter == null) return null; + if (buCharacter == null) { + return null; + } String buFont = bulletStyle.getBulletFont(); - if (buFont == null) buFont = paragraph.getDefaultFontFamily(); + if (buFont == null) { + buFont = paragraph.getDefaultFontFamily(); + } assert(buFont != null); PlaceableShape ps = getParagraphShape(); @@ -313,9 +337,14 @@ public class DrawTextParagraph implements Drawable { float fontSize = (Float)firstLineAttr.getAttribute(TextAttribute.SIZE); Double buSz = bulletStyle.getBulletFontSize(); - if (buSz == null) buSz = 100d; - if (buSz > 0) fontSize *= buSz* 0.01; - else fontSize = (float)-buSz; + if (buSz == null) { + buSz = 100d; + } + if (buSz > 0) { + fontSize *= buSz* 0.01; + } else { + fontSize = (float)-buSz; + } AttributedString str = new AttributedString(mapFontCharset(buCharacter,buFont)); @@ -328,7 +357,11 @@ public class DrawTextParagraph implements Drawable { return fact.getTextFragment(layout, str); } - protected String getRenderableText(TextRun tr) { + protected String getRenderableText(Graphics2D graphics, TextRun tr) { + if (tr.getFieldType() == FieldType.SLIDE_NUMBER) { + Slide slide = (Slide)graphics.getRenderingHint(Drawable.CURRENT_SLIDE); + return (slide == null) ? "" : Integer.toString(slide.getSlideNumber()); + } StringBuilder buf = new StringBuilder(); TextCap cap = tr.getTextCap(); String tabs = null; @@ -364,18 +397,24 @@ public class DrawTextParagraph implements Drawable { private String tab2space(TextRun tr) { AttributedString string = new AttributedString(" "); String fontFamily = tr.getFontFamily(); - if (fontFamily == null) fontFamily = "Lucida Sans"; + if (fontFamily == null) { + fontFamily = "Lucida Sans"; + } string.addAttribute(TextAttribute.FAMILY, fontFamily); Double fs = tr.getFontSize(); - if (fs == null) fs = 12d; + if (fs == null) { + fs = 12d; + } string.addAttribute(TextAttribute.SIZE, fs.floatValue()); TextLayout l = new TextLayout(string.getIterator(), new FontRenderContext(null, true, true)); double wspace = l.getAdvance(); Double tabSz = paragraph.getDefaultTabSize(); - if (tabSz == null) tabSz = wspace*4; + if (tabSz == null) { + tabSz = wspace*4; + } int numSpaces = (int)Math.ceil(tabSz / wspace); StringBuilder buf = new StringBuilder(); @@ -449,10 +488,13 @@ public class DrawTextParagraph implements Drawable { } if (firstLine && !isHSLF()) { if (bullet != null){ - if (indent > 0) width -= indent; + if (indent > 0) { + width -= indent; + } } else { - if (indent > 0) width -= indent; // first line indentation - else if (indent < 0) { // hanging indentation: the first line start at the left margin + if (indent > 0) { + width -= indent; // first line indentation + } else if (indent < 0) { // hanging indentation: the first line start at the left margin width += leftMargin; } } @@ -480,25 +522,36 @@ public class DrawTextParagraph implements Drawable { @SuppressWarnings("rawtypes") private PlaceableShape getParagraphShape() { return new PlaceableShape(){ + @Override public ShapeContainer getParent() { return null; } + @Override public Rectangle2D getAnchor() { return paragraph.getParentShape().getAnchor(); } + @Override public void setAnchor(Rectangle2D anchor) {} + @Override public double getRotation() { return 0; } + @Override public void setRotation(double theta) {} + @Override public void setFlipHorizontal(boolean flip) {} + @Override public void setFlipVertical(boolean flip) {} + @Override public boolean getFlipHorizontal() { return false; } + @Override public boolean getFlipVertical() { return false; } + @Override public Sheet getSheet() { return paragraph.getParentShape().getSheet(); } }; } protected AttributedString getAttributedString(Graphics2D graphics, StringBuilder text){ List attList = new ArrayList(); - if (text == null) text = new StringBuilder(); + if (text == null) { + text = new StringBuilder(); + } PlaceableShape ps = getParagraphShape(); - DrawFontManager fontHandler = (DrawFontManager)graphics.getRenderingHint(Drawable.FONT_HANDLER); @SuppressWarnings("unchecked") Map fontMap = (Map)graphics.getRenderingHint(Drawable.FONT_MAP); @@ -506,9 +559,11 @@ public class DrawTextParagraph implements Drawable { Map fallbackMap = (Map)graphics.getRenderingHint(Drawable.FONT_FALLBACK); for (TextRun run : paragraph){ - String runText = getRenderableText(run); + String runText = getRenderableText(graphics, run); // skip empty runs - if (runText.isEmpty()) continue; + if (runText.isEmpty()) { + continue; + } // user can pass an custom object to convert fonts String mappedFont = run.getFontFamily(); @@ -633,8 +688,11 @@ public class DrawTextParagraph implements Drawable { return string; } + /** + * @return {@code true} if the HSLF implementation is used + */ protected boolean isHSLF() { - return paragraph.getClass().getName().contains("HSLF"); + return DrawShape.isHSLF(paragraph.getParentShape()); } /** diff --git a/src/java/org/apache/poi/sl/draw/DrawTextShape.java b/src/java/org/apache/poi/sl/draw/DrawTextShape.java index 3a1d8faeea..45819624bb 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextShape.java @@ -138,6 +138,7 @@ public class DrawTextShape extends DrawSimpleShape { double y0 = y; //noinspection RedundantCast + @SuppressWarnings("cast") Iterator> paragraphs = (Iterator>) getShape().iterator(); diff --git a/src/java/org/apache/poi/sl/draw/Drawable.java b/src/java/org/apache/poi/sl/draw/Drawable.java index 7df8533b56..cc85dde905 100644 --- a/src/java/org/apache/poi/sl/draw/Drawable.java +++ b/src/java/org/apache/poi/sl/draw/Drawable.java @@ -130,6 +130,12 @@ public interface Drawable { DrawableHint GSAVE = new DrawableHint(10); DrawableHint GRESTORE = new DrawableHint(11); + /** + * The Common SL Draw API works sometimes cascading, but there are places + * where the current slide context need to be evaluated, e.g. when slide numbers + * are printed. In this situation we need to have a way to access the current slide + */ + DrawableHint CURRENT_SLIDE = new DrawableHint(12); /** diff --git a/src/java/org/apache/poi/sl/usermodel/Placeholder.java b/src/java/org/apache/poi/sl/usermodel/Placeholder.java index a3bc9c7c88..e546e8bee2 100644 --- a/src/java/org/apache/poi/sl/usermodel/Placeholder.java +++ b/src/java/org/apache/poi/sl/usermodel/Placeholder.java @@ -114,12 +114,30 @@ public enum Placeholder { this.ooxmlId = ooxmlId; } - public static Placeholder lookupNative(int nativeId) { + public static Placeholder lookupNativeSlide(int nativeId) { + return lookupNative(nativeId, 0); + } + + public static Placeholder lookupNativeSlideMaster(int nativeId) { + return lookupNative(nativeId, 1); + } + + public static Placeholder lookupNativeNotes(int nativeId) { + return lookupNative(nativeId, 2); + } + + public static Placeholder lookupNativeNotesMaster(int nativeId) { + return lookupNative(nativeId, 3); + } + + + private static Placeholder lookupNative(int nativeId, int type) { for (Placeholder ph : values()) { - if (ph.nativeSlideId == nativeId || - ph.nativeSlideMasterId == nativeId || - ph.nativeNotesId == nativeId || - ph.nativeNotesMasterId == nativeId + if ( + type == 0 && ph.nativeSlideId == nativeId || + type == 1 && ph.nativeSlideMasterId == nativeId || + type == 2 && ph.nativeNotesId == nativeId || + type == 3 && ph.nativeNotesMasterId == nativeId ) { return ph; } diff --git a/src/java/org/apache/poi/sl/usermodel/Slide.java b/src/java/org/apache/poi/sl/usermodel/Slide.java index dae2f42e67..1f77ba6e5d 100644 --- a/src/java/org/apache/poi/sl/usermodel/Slide.java +++ b/src/java/org/apache/poi/sl/usermodel/Slide.java @@ -43,4 +43,14 @@ public interface Slide< */ String getTitle(); + /** + * In XSLF, slidenumber and date shapes aren't marked as placeholders + * whereas in HSLF they are activated via a HeadersFooter configuration. + * This method is used to generalize that handling. + * + * @param placeholder + * @return {@code true} if the placeholder should be displayed/rendered + * @since POI 3.16-beta2 + */ + boolean getDisplayPlaceholder(Placeholder placeholder); } diff --git a/src/java/org/apache/poi/sl/usermodel/TextRun.java b/src/java/org/apache/poi/sl/usermodel/TextRun.java index 014d3036b3..32b9c9933d 100644 --- a/src/java/org/apache/poi/sl/usermodel/TextRun.java +++ b/src/java/org/apache/poi/sl/usermodel/TextRun.java @@ -20,6 +20,7 @@ package org.apache.poi.sl.usermodel; import java.awt.Color; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; +import org.apache.poi.util.Internal; /** * Some text. @@ -30,6 +31,10 @@ public interface TextRun { SMALL, ALL } + + enum FieldType { + SLIDE_NUMBER, DATE_TIME + } String getRawText(); void setText(String text); @@ -176,4 +181,12 @@ public interface TextRun { * @since POI 3.14-Beta2 */ Hyperlink createHyperlink(); + + /** + * Experimental method to determine the field type, e.g. slide number + * + * @return the field type or {@code null} if text run is not a field + */ + @Internal + FieldType getFieldType(); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFLineBreak.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFLineBreak.java index bdbc461f4b..934c0e3eec 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFLineBreak.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFLineBreak.java @@ -19,20 +19,11 @@ package org.apache.poi.xslf.usermodel; -import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; class XSLFLineBreak extends XSLFTextRun { - private final CTTextCharacterProperties _brProps; - - XSLFLineBreak(CTRegularTextRun r, XSLFTextParagraph p, CTTextCharacterProperties brProps){ + protected XSLFLineBreak(CTTextLineBreak r, XSLFTextParagraph p) { super(r, p); - _brProps = brProps; - } - - @Override - protected CTTextCharacterProperties getRPr(boolean create){ - return _brProps; } public void setText(String text){ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPropertiesDelegate.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPropertiesDelegate.java index f6e5ad0a95..a15d90cc4d 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPropertiesDelegate.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPropertiesDelegate.java @@ -1341,106 +1341,138 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperti this.props = props; } + @Override public CTNoFillProperties getNoFill() { return isSetNoFill() ? (CTNoFillProperties)props : null; } + @Override public boolean isSetNoFill() { return (props instanceof CTNoFillProperties); } + @Override public void setNoFill(CTNoFillProperties noFill) {} + @Override public CTNoFillProperties addNewNoFill() { return null; } + @Override public void unsetNoFill() {} + @Override public CTSolidColorFillProperties getSolidFill() { return isSetSolidFill() ? (CTSolidColorFillProperties)props : null; } + @Override public boolean isSetSolidFill() { return (props instanceof CTSolidColorFillProperties); } + @Override public void setSolidFill(CTSolidColorFillProperties solidFill) {} + @Override public CTSolidColorFillProperties addNewSolidFill() { return null; } + @Override public void unsetSolidFill() {} + @Override public CTGradientFillProperties getGradFill() { return isSetGradFill() ? (CTGradientFillProperties)props : null; } + @Override public boolean isSetGradFill() { return (props instanceof CTGradientFillProperties); } + @Override public void setGradFill(CTGradientFillProperties gradFill) {} + @Override public CTGradientFillProperties addNewGradFill() { return null; } + @Override public void unsetGradFill() {} + @Override public CTBlipFillProperties getBlipFill() { return isSetBlipFill() ? (CTBlipFillProperties)props : null; } + @Override public boolean isSetBlipFill() { return (props instanceof CTBlipFillProperties); } + @Override public void setBlipFill(CTBlipFillProperties blipFill) {} + @Override public CTBlipFillProperties addNewBlipFill() { return null; } + @Override public void unsetBlipFill() {} + @Override public CTPatternFillProperties getPattFill() { return isSetPattFill() ? (CTPatternFillProperties)props : null; } + @Override public boolean isSetPattFill() { return (props instanceof CTPatternFillProperties); } + @Override public void setPattFill(CTPatternFillProperties pattFill) {} + @Override public CTPatternFillProperties addNewPattFill() { return null; } + @Override public void unsetPattFill() {} + @Override public CTGroupFillProperties getGrpFill() { return isSetGrpFill() ? (CTGroupFillProperties)props : null; } + @Override public boolean isSetGrpFill() { return (props instanceof CTGroupFillProperties); } + @Override public void setGrpFill(CTGroupFillProperties grpFill) {} + @Override public CTGroupFillProperties addNewGrpFill() { return null; } + @Override public void unsetGrpFill() {} + @Override public boolean isSetMatrixStyle() { return false; } + @Override public CTStyleMatrixReference getMatrixStyle() { return null; } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 70df3f806a..d73dcc8c0a 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -32,6 +32,7 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.ColorStyle; +import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint; import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint; @@ -57,6 +58,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillPropertie import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrix; import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; import org.openxmlformats.schemas.drawingml.x2006.main.STPathShadeType; +import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal; import org.openxmlformats.schemas.presentationml.x2006.main.CTApplicationNonVisualDrawingProps; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackgroundProperties; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; @@ -150,6 +152,7 @@ public abstract class XSLFShape implements Shape { protected PaintStyle getFillPaint() { final XSLFTheme theme = getSheet().getTheme(); + final boolean hasPlaceholder = getPlaceholder() != null; PropertyFetcher fetcher = new PropertyFetcher() { public boolean fetch(XSLFShape shape) { XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(shape.getShapeProperties()); @@ -163,7 +166,7 @@ public abstract class XSLFShape implements Shape { } PackagePart pp = shape.getSheet().getPackagePart(); - PaintStyle paint = selectPaint(fp, null, pp, theme); + PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); if (paint != null) { setValue(paint); return true; @@ -172,7 +175,7 @@ public abstract class XSLFShape implements Shape { CTShapeStyle style = shape.getSpStyle(); if (style != null) { fp = XSLFPropertiesDelegate.getFillDelegate(style.getFillRef()); - paint = selectPaint(fp, null, pp, theme); + paint = selectPaint(fp, null, pp, theme, hasPlaceholder); } if (paint != null) { setValue(paint); @@ -230,6 +233,9 @@ public abstract class XSLFShape implements Shape { if (cur.toChild(namespace, nodename)) { child = (T)cur.getObject(); } + if (cur.toChild("http://schemas.openxmlformats.org/drawingml/2006/main", nodename)) { + child = (T)cur.getObject(); + } cur.dispose(); return child; } @@ -290,61 +296,71 @@ public abstract class XSLFShape implements Shape { } /** - * Walk up the inheritance tree and fetch shape properties. + * Walk up the inheritance tree and fetch shape properties.

* - * The following order of inheritance is assumed: - *

- * slide <-- slideLayout <-- slideMaster - *

+ * The following order of inheritance is assumed:

+ *

    + *
  1. slide + *
  2. slideLayout + *
  3. slideMaster + *
+ * + * Currently themes and their defaults aren't correctly handled * * @param visitor the object that collects the desired property * @return true if the property was fetched */ protected boolean fetchShapeProperty(PropertyFetcher visitor) { - boolean ok = visitor.fetch(this); + // try shape properties in slide + if (visitor.fetch(this)) { + return true; + } - XSLFSimpleShape masterShape; - XSLFSheet masterSheet = (XSLFSheet)getSheet().getMasterSheet(); CTPlaceholder ph = getCTPlaceholder(); - - if (masterSheet != null && ph != null) { - if (!ok) { - masterShape = masterSheet.getPlaceholder(ph); - if (masterShape != null) { - ok = visitor.fetch(masterShape); - } + if (ph == null) { + return false; + } + MasterSheet sm = getSheet().getMasterSheet(); + + // try slide layout + if (sm instanceof XSLFSlideLayout) { + XSLFSlideLayout slideLayout = (XSLFSlideLayout)sm; + XSLFSimpleShape placeholderShape = slideLayout.getPlaceholder(ph); + if (placeholderShape != null && visitor.fetch(placeholderShape)) { + return true; } - - // try slide master - if (!ok ) { - int textType; - if ( !ph.isSetType()) textType = STPlaceholderType.INT_BODY; - else { - switch (ph.getType().intValue()) { - case STPlaceholderType.INT_TITLE: - case STPlaceholderType.INT_CTR_TITLE: - textType = STPlaceholderType.INT_TITLE; - break; - case STPlaceholderType.INT_FTR: - case STPlaceholderType.INT_SLD_NUM: - case STPlaceholderType.INT_DT: - textType = ph.getType().intValue(); - break; - default: - textType = STPlaceholderType.INT_BODY; - break; - } - } - XSLFSheet master = (XSLFSheet)masterSheet.getMasterSheet(); - if (master != null) { - masterShape = master.getPlaceholderByType(textType); - if (masterShape != null) { - ok = visitor.fetch(masterShape); - } - } + sm = slideLayout.getMasterSheet(); + } + + // try slide master + if (sm instanceof XSLFSlideMaster) { + XSLFSlideMaster master = (XSLFSlideMaster)sm; + int textType = getPlaceholderType(ph); + XSLFSimpleShape masterShape = master.getPlaceholderByType(textType); + if (masterShape != null && visitor.fetch(masterShape)) { + return true; } } - return ok; + + return false; + } + + private static int getPlaceholderType(CTPlaceholder ph) { + if ( !ph.isSetType()) { + return STPlaceholderType.INT_BODY; + } + + switch (ph.getType().intValue()) { + case STPlaceholderType.INT_TITLE: + case STPlaceholderType.INT_CTR_TITLE: + return STPlaceholderType.INT_TITLE; + case STPlaceholderType.INT_FTR: + case STPlaceholderType.INT_SLD_NUM: + case STPlaceholderType.INT_DT: + return ph.getType().intValue(); + default: + return STPlaceholderType.INT_BODY; + } } /** @@ -358,7 +374,7 @@ public abstract class XSLFShape implements Shape { * * @return the applied Paint or null if none was applied */ - protected static PaintStyle selectPaint(XSLFFillProperties fp, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme) { + protected static PaintStyle selectPaint(XSLFFillProperties fp, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme, boolean hasPlaceholder) { if (fp == null || fp.isSetNoFill()) { return null; } else if (fp.isSetSolidFill()) { @@ -368,15 +384,23 @@ public abstract class XSLFShape implements Shape { } else if (fp.isSetGradFill()) { return selectPaint(fp.getGradFill(), phClr, theme); } else if (fp.isSetMatrixStyle()) { - return selectPaint(fp.getMatrixStyle(), theme, fp.isLineStyle()); + return selectPaint(fp.getMatrixStyle(), theme, fp.isLineStyle(), hasPlaceholder); } else { return null; } } protected static PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr, final XSLFTheme theme) { - if (phClr == null && solidFill.isSetSchemeClr()) { - phClr = solidFill.getSchemeClr(); + if (solidFill.isSetSchemeClr()) { + // if there's a reference to the placeholder color, + // stop evaluating further and let the caller select + // the next style inheritance level + if (STSchemeColorVal.PH_CLR.equals(solidFill.getSchemeClr().getVal())) { + return null; + } + if (phClr == null) { + phClr = solidFill.getSchemeClr(); + } } final XSLFColor c = new XSLFColor(solidFill, theme, phClr); return DrawPaint.createSolidPaint(c.getColorStyle()); @@ -483,7 +507,7 @@ public abstract class XSLFShape implements Shape { }; } - protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle) { + protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme, boolean isLineStyle, boolean hasPlaceholder) { if (fillRef == null) return null; // The idx attribute refers to the index of a fill style or @@ -492,7 +516,6 @@ public abstract class XSLFShape implements Shape { // values 1-999 refer to the index of a fill style within the fillStyleLst element // values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element. int idx = (int)fillRef.getIdx(); - CTSchemeColor phClr = fillRef.getSchemeClr(); CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme(); final XmlObject styleLst; int childIdx; @@ -511,8 +534,16 @@ public abstract class XSLFShape implements Shape { fp = XSLFPropertiesDelegate.getFillDelegate(cur.getObject()); } cur.dispose(); - - return selectPaint(fp, phClr, theme.getPackagePart(), theme); + + CTSchemeColor phClr = fillRef.getSchemeClr(); + PaintStyle res = selectPaint(fp, phClr, theme.getPackagePart(), theme, hasPlaceholder); + // check for empty placeholder value + // see http://officeopenxml.com/prSlide-color.php - "Color Placeholders within Themes" + if (res != null || hasPlaceholder) { + return res; + } + XSLFColor col = new XSLFColor(fillRef, theme, phClr); + return DrawPaint.createSolidPaint(col.getColorStyle()); } @Override diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index ce058f15a0..5d1d23191c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -121,6 +121,7 @@ public abstract class XSLFSimpleShape extends XSLFShape protected CTTransform2D getXfrm(boolean create) { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { XmlObject xo = shape.getShapeProperties(); if (xo instanceof CTShapeProperties && ((CTShapeProperties)xo).isSetXfrm()) { @@ -234,20 +235,32 @@ public abstract class XSLFSimpleShape extends XSLFShape */ CTLineProperties getDefaultLineProperties() { CTShapeStyle style = getSpStyle(); - if (style == null) return null; + if (style == null) { + return null; + } CTStyleMatrixReference lnRef = style.getLnRef(); - if (lnRef == null) return null; + if (lnRef == null) { + return null; + } // 1-based index of a line style within the style matrix int idx = (int)lnRef.getIdx(); XSLFTheme theme = getSheet().getTheme(); - if (theme == null) return null; + if (theme == null) { + return null; + } CTBaseStyles styles = theme.getXmlObject().getThemeElements(); - if (styles == null) return null; + if (styles == null) { + return null; + } CTStyleMatrix styleMatrix = styles.getFmtScheme(); - if (styleMatrix == null) return null; + if (styleMatrix == null) { + return null; + } CTLineStyleList lineStyles = styleMatrix.getLnStyleLst(); - if (lineStyles == null || lineStyles.sizeOfLnArray() < idx) return null; + if (lineStyles == null || lineStyles.sizeOfLnArray() < idx) { + return null; + } return lineStyles.getLnArray(idx - 1); } @@ -301,12 +314,14 @@ public abstract class XSLFSimpleShape extends XSLFShape protected PaintStyle getLinePaint() { XSLFSheet sheet = getSheet(); final XSLFTheme theme = sheet.getTheme(); + final boolean hasPlaceholder = getPlaceholder() != null; PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { CTLineProperties spPr = getLn(shape, false); XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(spPr); PackagePart pp = shape.getSheet().getPackagePart(); - PaintStyle paint = selectPaint(fp, null, pp, theme); + PaintStyle paint = selectPaint(fp, null, pp, theme, hasPlaceholder); if (paint != null) { setValue(paint); return true; @@ -315,7 +330,7 @@ public abstract class XSLFSimpleShape extends XSLFShape CTShapeStyle style = shape.getSpStyle(); if (style != null) { fp = XSLFPropertiesDelegate.getFillDelegate(style.getLnRef()); - paint = selectPaint(fp, null, pp, theme); + paint = selectPaint(fp, null, pp, theme, hasPlaceholder); } if (paint != null) { setValue(paint); @@ -327,11 +342,15 @@ public abstract class XSLFSimpleShape extends XSLFShape fetchShapeProperty(fetcher); PaintStyle paint = fetcher.getValue(); - if (paint != null) return paint; + if (paint != null) { + return paint; + } // line color was not found, check if it is defined in the theme CTShapeStyle style = getSpStyle(); - if (style == null) return null; + if (style == null) { + return null; + } // get a reference to a line style within the style matrix. CTStyleMatrixReference lnRef = style.getLnRef(); @@ -341,7 +360,7 @@ public abstract class XSLFSimpleShape extends XSLFShape CTLineProperties props = theme.getXmlObject().getThemeElements().getFmtScheme().getLnStyleLst().getLnArray(idx - 1); XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(props); PackagePart pp = sheet.getPackagePart(); - paint = selectPaint(fp, phClr, pp, theme); + paint = selectPaint(fp, phClr, pp, theme, hasPlaceholder); } return paint; @@ -387,6 +406,7 @@ public abstract class XSLFSimpleShape extends XSLFShape */ public double getLineWidth() { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { CTLineProperties ln = getLn(shape, false); if (ln != null) { @@ -409,7 +429,9 @@ public abstract class XSLFSimpleShape extends XSLFShape if (fetcher.getValue() == null) { CTLineProperties defaultLn = getDefaultLineProperties(); if (defaultLn != null) { - if (defaultLn.isSetW()) lineWidth = Units.toPoints(defaultLn.getW()); + if (defaultLn.isSetW()) { + lineWidth = Units.toPoints(defaultLn.getW()); + } } } else { lineWidth = fetcher.getValue(); @@ -460,6 +482,7 @@ public abstract class XSLFSimpleShape extends XSLFShape */ public LineCompound getLineCompound() { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { CTLineProperties ln = getLn(shape, false); if (ln != null) { @@ -522,6 +545,7 @@ public abstract class XSLFSimpleShape extends XSLFShape public LineDash getLineDash() { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { CTLineProperties ln = getLn(shape, false); if (ln == null || !ln.isSetPrstDash()) { @@ -569,6 +593,7 @@ public abstract class XSLFSimpleShape extends XSLFShape */ public LineCap getLineCap() { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { CTLineProperties ln = getLn(shape, false); if (ln != null && ln.isSetCap()) { @@ -640,8 +665,10 @@ public abstract class XSLFSimpleShape extends XSLFShape /** * @return shadow of this shape or null if shadow is disabled */ + @Override public XSLFShadow getShadow() { PropertyFetcher fetcher = new PropertyFetcher() { + @Override public boolean fetch(XSLFShape shape) { XSLFEffectProperties ep = XSLFPropertiesDelegate.getEffectDelegate(shape.getShapeProperties()); if (ep != null && ep.isSetEffectLst()) { @@ -675,6 +702,7 @@ public abstract class XSLFSimpleShape extends XSLFShape * * @return definition of the shape geometry */ + @Override public CustomGeometry getGeometry() { XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties()); @@ -949,6 +977,7 @@ public abstract class XSLFSimpleShape extends XSLFShape return ph != null; } + @Override public Guide getAdjustValue(String name) { XSLFGeometryProperties gp = XSLFPropertiesDelegate.getGeometryDelegate(getShapeProperties()); @@ -963,28 +992,35 @@ public abstract class XSLFSimpleShape extends XSLFShape return null; } + @Override public LineDecoration getLineDecoration() { return new LineDecoration() { + @Override public DecorationShape getHeadShape() { return getLineHeadDecoration(); } + @Override public DecorationSize getHeadWidth() { return getLineHeadWidth(); } + @Override public DecorationSize getHeadLength() { return getLineHeadLength(); } + @Override public DecorationShape getTailShape() { return getLineTailDecoration(); } + @Override public DecorationSize getTailWidth() { return getLineTailWidth(); } + @Override public DecorationSize getTailLength() { return getLineTailLength(); } @@ -996,32 +1032,40 @@ public abstract class XSLFSimpleShape extends XSLFShape * * @return either Color or GradientPaint or TexturePaint or null */ + @Override public FillStyle getFillStyle() { return new FillStyle() { + @Override public PaintStyle getPaint() { return XSLFSimpleShape.this.getFillPaint(); } }; } + @Override public StrokeStyle getStrokeStyle() { return new StrokeStyle() { + @Override public PaintStyle getPaint() { return XSLFSimpleShape.this.getLinePaint(); } + @Override public LineCap getLineCap() { return XSLFSimpleShape.this.getLineCap(); } + @Override public LineDash getLineDash() { return XSLFSimpleShape.this.getLineDash(); } + @Override public double getLineWidth() { return XSLFSimpleShape.this.getLineWidth(); } + @Override public LineCompound getLineCompound() { return XSLFSimpleShape.this.getLineCompound(); } 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 140921aa33..dcd9642617 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -207,7 +207,7 @@ implements Slide { @Override public boolean getFollowMasterGraphics(){ - return _slide.isSetShowMasterSp() && _slide.getShowMasterSp(); + return _slide.getShowMasterSp(); } /** @@ -306,4 +306,9 @@ implements Slide { Drawable draw = drawFact.getDrawable(this); draw.draw(graphics); } + + @Override + public boolean getDisplayPlaceholder(Placeholder placeholder) { + return false; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java index 704d09ea94..b4e53f4feb 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java @@ -104,7 +104,7 @@ implements MasterSheet { @Override public boolean getFollowMasterGraphics() { - return _layout.isSetShowMasterSp() && _layout.getShowMasterSp(); + return _layout.getShowMasterSp(); } /** diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java index 7b13e5842a..7c7cceac51 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableCell.java @@ -145,7 +145,9 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell(); _shape = shape; - for(XmlObject ch : _p.selectPath("*")){ - if(ch instanceof CTRegularTextRun){ - CTRegularTextRun r = (CTRegularTextRun)ch; - _runs.add(newTextRun(r)); - } else if (ch instanceof CTTextLineBreak){ - CTTextLineBreak br = (CTTextLineBreak)ch; - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(br.getRPr()); - r.setT("\n"); - _runs.add(newTextRun(r)); - } else if (ch instanceof CTTextField){ - CTTextField f = (CTTextField)ch; - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(f.getRPr()); - r.setT(f.getT()); - _runs.add(newTextRun(r)); + XmlCursor c = _p.newCursor(); + try { + if (c.toFirstChild()) { + do { + XmlObject r = c.getObject(); + if (r instanceof CTTextLineBreak) { + _runs.add(new XSLFLineBreak((CTTextLineBreak)r, this)); + } else if (r instanceof CTRegularTextRun || r instanceof CTTextField) { + _runs.add(new XSLFTextRun(r, this)); + } + } while (c.toNextSibling()); } + } finally { + c.dispose(); } } @@ -147,17 +144,13 @@ public class XSLFTextParagraph implements TextParagraph 0){ // by default line break has the font size of the last text run CTTextCharacterProperties prevRun = _runs.get(_runs.size() - 1).getRPr(true); brProps.set(prevRun); } - CTRegularTextRun r = CTRegularTextRun.Factory.newInstance(); - r.setRPr(brProps); - r.setT("\n"); - XSLFTextRun run = new XSLFLineBreak(r, this, brProps); _runs.add(run); return run; } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java index b0bbd45570..7bf2a6c6ef 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -18,6 +18,7 @@ package org.apache.poi.xslf.usermodel; import java.awt.Color; +import org.apache.poi.openxml4j.exceptions.OpenXML4JRuntimeException; import org.apache.poi.openxml4j.opc.PackagePart; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.PaintStyle; @@ -26,13 +27,16 @@ import org.apache.poi.sl.usermodel.TextRun; import org.apache.poi.util.Beta; import org.apache.poi.xslf.model.CharacterPropertyFetcher; import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties; +import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTHyperlink; import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeStyle; import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextField; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextNormalAutofit; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; @@ -45,12 +49,15 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; */ @Beta public class XSLFTextRun implements TextRun { - private final CTRegularTextRun _r; + private final XmlObject _r; private final XSLFTextParagraph _p; - protected XSLFTextRun(CTRegularTextRun r, XSLFTextParagraph p){ + protected XSLFTextRun(XmlObject r, XSLFTextParagraph p){ _r = r; _p = p; + if (!(r instanceof CTRegularTextRun || r instanceof CTTextLineBreak || r instanceof CTTextField)) { + throw new OpenXML4JRuntimeException("unsupported text run of type "+r.getClass()); + } } XSLFTextParagraph getParentParagraph(){ @@ -58,11 +65,28 @@ public class XSLFTextRun implements TextRun { } public String getRawText(){ - return _r.getT(); + if (_r instanceof CTTextField) { + return ((CTTextField)_r).getT(); + } else if (_r instanceof CTTextLineBreak) { + return "\n"; + } + return ((CTRegularTextRun)_r).getT(); } String getRenderableText(){ - String txt = _r.getT(); + if (_r instanceof CTTextField) { + CTTextField tf = (CTTextField)_r; + XSLFSheet sheet = _p.getParentShape().getSheet(); + if ("slidenum".equals(tf.getType()) && sheet instanceof XSLFSlide) { + return Integer.toString(((XSLFSlide)sheet).getSlideNumber()); + } + return tf.getT(); + } else if (_r instanceof CTTextLineBreak) { + return "\n"; + } + + + String txt = ((CTRegularTextRun)_r).getT(); TextCap cap = getTextCap(); StringBuffer buf = new StringBuffer(); for(int i = 0; i < txt.length(); i++) { @@ -88,10 +112,24 @@ public class XSLFTextRun implements TextRun { } public void setText(String text){ - _r.setT(text); + if (_r instanceof CTTextField) { + ((CTTextField)_r).setT(text); + } else if (_r instanceof CTTextLineBreak) { + // ignored + return; + } else { + ((CTRegularTextRun)_r).setT(text); + } } - public CTRegularTextRun getXmlObject(){ + /** + * Return the text run xmlbeans object. + * Depending on the type of text run, this can be {@link CTTextField}, + * {@link CTTextLineBreak} or usually a {@link CTRegularTextRun} + * + * @return the xmlbeans object + */ + public XmlObject getXmlObject(){ return _r; } @@ -117,6 +155,7 @@ public class XSLFTextRun implements TextRun { @Override public PaintStyle getFontColor(){ + final boolean hasPlaceholder = getParentParagraph().getParentShape().getPlaceholder() != null; CharacterPropertyFetcher fetcher = new CharacterPropertyFetcher(_p.getIndentLevel()){ public boolean fetch(CTTextCharacterProperties props){ if (props == null) { @@ -134,7 +173,7 @@ public class XSLFTextRun implements TextRun { XSLFSheet sheet = shape.getSheet(); PackagePart pp = sheet.getPackagePart(); XSLFTheme theme = sheet.getTheme(); - PaintStyle ps = XSLFShape.selectPaint(fp, phClr, pp, theme); + PaintStyle ps = XSLFShape.selectPaint(fp, phClr, pp, theme, hasPlaceholder); if (ps != null) { setValue(ps); @@ -459,13 +498,29 @@ public class XSLFTextRun implements TextRun { * @return the character properties or null if create was false and the properties haven't exist */ protected CTTextCharacterProperties getRPr(boolean create) { - if (_r.isSetRPr()) { - return _r.getRPr(); - } else if (create) { - return _r.addNewRPr(); + if (_r instanceof CTTextField) { + CTTextField tf = (CTTextField)_r; + if (tf.isSetRPr()) { + return tf.getRPr(); + } else if (create) { + return tf.addNewRPr(); + } + } else if (_r instanceof CTTextLineBreak) { + CTTextLineBreak tlb = (CTTextLineBreak)_r; + if (tlb.isSetRPr()) { + return tlb.getRPr(); + } else if (create) { + return tlb.addNewRPr(); + } } else { - return null; + CTRegularTextRun tr = (CTRegularTextRun)_r; + if (tr.isSetRPr()) { + return tr.getRPr(); + } else if (create) { + return tr.addNewRPr(); + } } + return null; } @Override @@ -476,15 +531,17 @@ public class XSLFTextRun implements TextRun { @Override public XSLFHyperlink createHyperlink(){ XSLFHyperlink hl = getHyperlink(); - if (hl == null) { - hl = new XSLFHyperlink(_r.getRPr().addNewHlinkClick(), _p.getParentShape().getSheet()); + if (hl != null) { + return hl; } - return hl; + + CTTextCharacterProperties rPr = getRPr(true); + return new XSLFHyperlink(rPr.addNewHlinkClick(), _p.getParentShape().getSheet()); } @Override public XSLFHyperlink getHyperlink(){ - CTTextCharacterProperties rPr = _r.getRPr(); + CTTextCharacterProperties rPr = getRPr(false); if (rPr == null) { return null; } @@ -498,33 +555,33 @@ public class XSLFTextRun implements TextRun { private boolean fetchCharacterProperty(CharacterPropertyFetcher fetcher){ XSLFTextShape shape = _p.getParentShape(); XSLFSheet sheet = shape.getSheet(); - boolean ok = false; - if (_r.isSetRPr()) ok = fetcher.fetch(getRPr(false)); - if (ok) return true; + CTTextCharacterProperties rPr = getRPr(false); + if (rPr != null && fetcher.fetch(rPr)) { + return true; + } - ok = shape.fetchShapeProperty(fetcher); - if (ok) return true; + if (shape.fetchShapeProperty(fetcher)) { + return true; + } CTPlaceholder ph = shape.getCTPlaceholder(); if (ph == null){ // if it is a plain text box then take defaults from presentation.xml @SuppressWarnings("resource") XMLSlideShow ppt = sheet.getSlideShow(); + // TODO: determine master shape CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(_p.getIndentLevel()); - if (themeProps != null) { - // TODO: determine master shape - ok = fetcher.fetch(themeProps); + if (themeProps != null && fetcher.fetch(themeProps)) { + return true; } } - if (ok) return true; + // TODO: determine master shape CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle(); - if(defaultProps != null) { - // TODO: determine master shape - ok = fetcher.fetch(defaultProps); + if(defaultProps != null && fetcher.fetch(defaultProps)) { + return true; } - if (ok) return true; return false; } @@ -557,4 +614,16 @@ public class XSLFTextRun implements TextRun { boolean strike = r.isStrikethrough(); if(strike != isStrikethrough()) setStrikethrough(strike); } + + + @Override + public FieldType getFieldType() { + if (_r instanceof CTTextField) { + CTTextField tf = (CTTextField)_r; + if ("slidenum".equals(tf.getType())) { + return FieldType.SLIDE_NUMBER; + } + } + return null; + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java index 809f745c42..d3bd3a847c 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java @@ -169,5 +169,4 @@ public class XSLFTheme extends POIXMLDocumentPart { } return null; } - } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java index 95dd57d154..31b116f949 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/TestXSLFSlideShow.java @@ -108,7 +108,8 @@ public class TestXSLFSlideShow { // And again for the master CTSlideMasterIdListEntry[] masters = xml.getSlideMasterReferences().getSldMasterIdArray(); - assertEquals(2147483648l, masters[0].getId()); + // see SlideAtom.USES_MASTER_SLIDE_ID + assertEquals(0x80000000L, masters[0].getId()); assertEquals("rId1", masters[0].getId2()); assertNotNull(xml.getSlideMaster(masters[0])); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java index 0e58d410c3..3ef1988b28 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXMLSlideShow.java @@ -105,7 +105,8 @@ public class TestXMLSlideShow extends BaseTestSlideShow { // Next up look for the slide master CTSlideMasterIdListEntry[] masters = xml.getCTPresentation().getSldMasterIdLst().getSldMasterIdArray(); - assertEquals(2147483648l, masters[0].getId()); + // see SlideAtom.USES_MASTER_SLIDE_ID + assertEquals(0x80000000L, masters[0].getId()); assertEquals("rId1", masters[0].getId2()); assertNotNull(xml.getSlideMasters().get(0)); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java index 798dc472aa..3104cbb677 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFAutoShape.java @@ -16,22 +16,26 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; import java.io.IOException; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.ShapeType; import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; import org.apache.poi.sl.usermodel.TextShape.TextAutofit; import org.apache.poi.sl.usermodel.TextShape.TextDirection; +import org.apache.poi.sl.usermodel.VerticalAlignment; import org.apache.poi.util.Units; import org.junit.Test; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; -/** - * @author Yegor Kozlov - */ public class TestXSLFAutoShape { @Test public void testTextBodyProperies() throws IOException { @@ -222,56 +226,58 @@ public class TestXSLFAutoShape { assertEquals(1, shape.getTextParagraphs().size()); assertEquals(0, p.getTextRuns().size()); XSLFTextRun r = p.addNewTextRun(); + CTTextCharacterProperties rPr = r.getRPr(false); + assertNotNull(rPr); assertEquals(1, p.getTextRuns().size()); assertSame(r, p.getTextRuns().get(0)); + assertEquals(18.0, r.getFontSize(), 0); // default font size for text boxes - assertFalse(r.getXmlObject().getRPr().isSetSz()); + assertFalse(rPr.isSetSz()); r.setFontSize(10.0); - assertTrue(r.getXmlObject().isSetRPr()); - assertEquals(1000, r.getXmlObject().getRPr().getSz()); + assertEquals(1000, rPr.getSz()); r.setFontSize(12.5); - assertEquals(1250, r.getXmlObject().getRPr().getSz()); + assertEquals(1250, rPr.getSz()); r.setFontSize(null); - assertFalse(r.getXmlObject().getRPr().isSetSz()); + assertFalse(rPr.isSetSz()); - assertFalse(r.getXmlObject().getRPr().isSetLatin()); + assertFalse(rPr.isSetLatin()); assertEquals("Calibri", r.getFontFamily()); // comes from the slide master r.setFontFamily(null); assertEquals("Calibri", r.getFontFamily()); // comes from the slide master r.setFontFamily("Arial"); assertEquals("Arial", r.getFontFamily()); - assertEquals("Arial", r.getXmlObject().getRPr().getLatin().getTypeface()); + assertEquals("Arial", rPr.getLatin().getTypeface()); r.setFontFamily("Symbol"); assertEquals("Symbol", r.getFontFamily()); - assertEquals("Symbol", r.getXmlObject().getRPr().getLatin().getTypeface()); + assertEquals("Symbol", rPr.getLatin().getTypeface()); r.setFontFamily(null); assertEquals("Calibri", r.getFontFamily()); // comes from the slide master - assertFalse(r.getXmlObject().getRPr().isSetLatin()); + assertFalse(rPr.isSetLatin()); assertFalse(r.isStrikethrough()); - assertFalse(r.getXmlObject().getRPr().isSetStrike()); + assertFalse(rPr.isSetStrike()); r.setStrikethrough(true); assertTrue(r.isStrikethrough()); - assertEquals(STTextStrikeType.SNG_STRIKE, r.getXmlObject().getRPr().getStrike()); + assertEquals(STTextStrikeType.SNG_STRIKE, rPr.getStrike()); assertFalse(r.isBold()); - assertFalse(r.getXmlObject().getRPr().isSetB()); + assertFalse(rPr.isSetB()); r.setBold(true); assertTrue(r.isBold()); - assertEquals(true, r.getXmlObject().getRPr().getB()); + assertEquals(true, rPr.getB()); assertFalse(r.isItalic()); - assertFalse(r.getXmlObject().getRPr().isSetI()); + assertFalse(rPr.isSetI()); r.setItalic(true); assertTrue(r.isItalic()); - assertEquals(true, r.getXmlObject().getRPr().getI()); + assertEquals(true, rPr.getI()); assertFalse(r.isUnderlined()); - assertFalse(r.getXmlObject().getRPr().isSetU()); + assertFalse(rPr.isSetU()); r.setUnderlined(true); assertTrue(r.isUnderlined()); - assertEquals(STTextUnderlineType.SNG, r.getXmlObject().getRPr().getU()); + assertEquals(STTextUnderlineType.SNG, rPr.getU()); r.setText("Apache"); assertEquals("Apache", r.getRawText()); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java index 0ff02d0853..a705bfbbe2 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFHyperlink.java @@ -123,10 +123,11 @@ public class TestXSLFHyperlink { XSLFTextBox tb3 = sl3.createTextBox(); tb3.setAnchor(anchor); tb3.setText("text1 "); - XSLFTextRun r3 = tb3.appendText("lin\u000bk", false); + tb3.appendText("lin\u000bk", false); tb3.appendText(" text2", false); - XSLFHyperlink hl3 = r3.createHyperlink(); - hl3.linkToSlide(slide1); + List tb3runs = tb3.getTextParagraphs().get(0).getTextRuns(); + tb3runs.get(1).createHyperlink().linkToSlide(slide1); // "lin" + tb3runs.get(3).createHyperlink().linkToSlide(slide1); // "k" XSLFTextBox tb4 = ppt1.createSlide().createTextBox(); tb4.setAnchor(anchor); XSLFTextRun r4 = tb4.setText("page4"); @@ -155,6 +156,8 @@ public class TestXSLFHyperlink { assertEquals(HyperlinkType.DOCUMENT, hl2.getTypeEnum()); tb3 = (XSLFTextBox)slides.get(2).getShapes().get(0); + XSLFHyperlink hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(1).getHyperlink(); + assertNotNull(hl3); hl3 = tb3.getTextParagraphs().get(0).getTextRuns().get(3).getHyperlink(); assertNotNull(hl3); assertEquals("/ppt/slides/slide1.xml", hl3.getAddress()); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFShape.java index fa56117893..403ccb6790 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFShape.java @@ -16,18 +16,17 @@ ==================================================================== */ package org.apache.poi.xslf.usermodel; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.io.IOException; +import java.util.List; import org.apache.poi.xslf.XSLFTestDataSamples; import org.junit.Test; import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; -import java.io.IOException; -import java.util.List; - -/** - * @author Yegor Kozlov - */ public class TestXSLFShape { @Test @@ -63,7 +62,7 @@ public class TestXSLFShape { assertEquals("PPTX ", r2.get(0).getRawText()); assertEquals("Title", r2.get(1).getRawText()); // Title is underlined - assertEquals(STTextUnderlineType.SNG, r2.get(1).getXmlObject().getRPr().getU()); + assertEquals(STTextUnderlineType.SNG, r2.get(1).getRPr(false).getU()); assertTrue(shapes2.get(1) instanceof XSLFAutoShape); @@ -78,7 +77,7 @@ public class TestXSLFShape { assertEquals(1, paragraphs2.get(1).getTextRuns().size()); assertEquals("Subtitle", paragraphs2.get(0).getTextRuns().get(0).getRawText()); - assertTrue(paragraphs2.get(0).getTextRuns().get(0).getXmlObject().getRPr().getB()); + assertTrue(paragraphs2.get(0).getTextRuns().get(0).getRPr(false).getB()); assertEquals("And second line", paragraphs2.get(1).getTextRuns().get(0).getRawText()); ppt.close(); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java index 0af1347aba..fad3f82a00 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java @@ -111,7 +111,7 @@ public class TestXSLFSlide { assertEquals(0, ppt.getSlides().size()); XSLFSlide slide = ppt.createSlide(); - assertFalse(slide.getFollowMasterGraphics()); + assertTrue(slide.getFollowMasterGraphics()); slide.setFollowMasterGraphics(false); assertFalse(slide.getFollowMasterGraphics()); slide.setFollowMasterGraphics(true); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java index 771fd9d915..13024e6fdb 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -714,7 +714,7 @@ public class TestXSLFTextShape { // level 5: text properties are defined in the text run CTTextParagraphProperties lv5PPr = paragraph.getXmlObject().addNewPPr(); - CTTextCharacterProperties lv5CPr = textRun.getXmlObject().getRPr(); + CTTextCharacterProperties lv5CPr = textRun.getRPr(false); lv5CPr.setSz(3600); assertEquals(36.0, textRun.getFontSize(), 0); lv5CPr.addNewLatin().setTypeface("Calibri"); @@ -899,11 +899,11 @@ public class TestXSLFTextShape { // level 5: text properties are defined in the text run lv1PPr = p1.getXmlObject().isSetPPr() ? p1.getXmlObject().getPPr() : p1.getXmlObject().addNewPPr(); - lv1CPr = r1.getXmlObject().getRPr(); + lv1CPr = r1.getRPr(false); lv2PPr = p2.getXmlObject().isSetPPr() ? p2.getXmlObject().getPPr() : p2.getXmlObject().addNewPPr(); - lv2CPr = r2.getXmlObject().getRPr(); + lv2CPr = r2.getRPr(false); lv3PPr = p3.getXmlObject().isSetPPr() ? p3.getXmlObject().getPPr() : p3.getXmlObject().addNewPPr(); - lv3CPr = r3.getXmlObject().getRPr(); + lv3CPr = r3.getRPr(false); lv1CPr.setSz(3600); assertEquals(36.0, r1.getFontSize(), 0); diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java index 912ecc0f3d..a181e4f8ff 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTheme.java @@ -17,12 +17,15 @@ package org.apache.poi.xslf.usermodel; import static org.apache.poi.sl.TestCommonSL.sameColor; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import java.awt.Color; import java.util.List; -import org.apache.poi.sl.usermodel.*; +import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.GradientPaint; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; import org.apache.poi.sl.usermodel.PaintStyle.TexturePaint; @@ -54,7 +57,9 @@ public class TestXSLFTheme { private XSLFShape getShape(XSLFSheet sheet, String name){ for(XSLFShape sh : sheet.getShapes()){ - if(sh.getShapeName().equals(name)) return sh; + if(sh.getShapeName().equals(name)) { + return sh; + } } throw new IllegalArgumentException("Shape not found: " + name); } @@ -99,7 +104,7 @@ public class TestXSLFTheme { assertTrue(sameColor(new Color(148, 198, 0), run2.getFontColor())); assertNull(sh2.getFillColor()); // no fill - assertFalse(slide.getSlideLayout().getFollowMasterGraphics()); + assertTrue(slide.getSlideLayout().getFollowMasterGraphics()); } void slide5(XSLFSlide slide){ @@ -113,7 +118,7 @@ public class TestXSLFTheme { // font size is 40pt and scale factor is 90% assertEquals(36.0, run2.getFontSize(), 0); - assertFalse(slide.getSlideLayout().getFollowMasterGraphics()); + assertTrue(slide.getSlideLayout().getFollowMasterGraphics()); } void slide6(XSLFSlide slide){ diff --git a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java index 9bfa5f3254..ab96c7dbe6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java +++ b/src/scratchpad/src/org/apache/poi/hslf/model/HeadersFooters.java @@ -21,13 +21,14 @@ import org.apache.poi.hslf.record.CString; import org.apache.poi.hslf.record.Document; import org.apache.poi.hslf.record.HeadersFootersAtom; import org.apache.poi.hslf.record.HeadersFootersContainer; -import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.record.Record; import org.apache.poi.hslf.record.RecordTypes; import org.apache.poi.hslf.record.SheetContainer; import org.apache.poi.hslf.usermodel.HSLFSheet; +import org.apache.poi.hslf.usermodel.HSLFSimpleShape; import org.apache.poi.hslf.usermodel.HSLFSlideShow; import org.apache.poi.hslf.usermodel.HSLFTextShape; +import org.apache.poi.sl.usermodel.Placeholder; /** * Header / Footer settings. @@ -87,7 +88,7 @@ public final class HeadersFooters { */ public String getHeaderText(){ CString cs = _container == null ? null : _container.getHeaderAtom(); - return getPlaceholderText(OEPlaceholderAtom.MasterHeader, cs); + return getPlaceholderText(Placeholder.HEADER, cs); } /** @@ -112,7 +113,7 @@ public final class HeadersFooters { */ public String getFooterText(){ CString cs = _container == null ? null : _container.getFooterAtom(); - return getPlaceholderText(OEPlaceholderAtom.MasterFooter, cs); + return getPlaceholderText(Placeholder.FOOTER, cs); } /** @@ -137,7 +138,7 @@ public final class HeadersFooters { */ public String getDateTimeText(){ CString cs = _container == null ? null : _container.getUserDateAtom(); - return getPlaceholderText(OEPlaceholderAtom.MasterDate, cs); + return getPlaceholderText(Placeholder.DATETIME, cs); } /** @@ -160,7 +161,7 @@ public final class HeadersFooters { * whether the footer text is displayed. */ public boolean isFooterVisible(){ - return isVisible(HeadersFootersAtom.fHasFooter, OEPlaceholderAtom.MasterFooter); + return isVisible(HeadersFootersAtom.fHasFooter, Placeholder.FOOTER); } /** @@ -174,7 +175,7 @@ public final class HeadersFooters { * whether the header text is displayed. */ public boolean isHeaderVisible(){ - return isVisible(HeadersFootersAtom.fHasHeader, OEPlaceholderAtom.MasterHeader); + return isVisible(HeadersFootersAtom.fHasHeader, Placeholder.HEADER); } /** @@ -188,7 +189,7 @@ public final class HeadersFooters { * whether the date is displayed in the footer. */ public boolean isDateTimeVisible(){ - return isVisible(HeadersFootersAtom.fHasDate, OEPlaceholderAtom.MasterDate); + return isVisible(HeadersFootersAtom.fHasDate, Placeholder.DATETIME); } /** @@ -202,7 +203,7 @@ public final class HeadersFooters { * whether the custom user date is used instead of today's date. */ public boolean isUserDateVisible(){ - return isVisible(HeadersFootersAtom.fHasUserDate, OEPlaceholderAtom.MasterDate); + return isVisible(HeadersFootersAtom.fHasUserDate, Placeholder.DATETIME); } /** @@ -216,7 +217,7 @@ public final class HeadersFooters { * whether the slide number is displayed in the footer. */ public boolean isSlideNumberVisible(){ - return isVisible(HeadersFootersAtom.fHasSlideNumber, OEPlaceholderAtom.MasterSlideNumber); + return isVisible(HeadersFootersAtom.fHasSlideNumber, Placeholder.SLIDE_NUMBER); } /** @@ -244,31 +245,29 @@ public final class HeadersFooters { _container.getHeadersFootersAtom().setFormatId(formatId); } - private boolean isVisible(int flag, int placeholderId){ + private boolean isVisible(int flag, Placeholder placeholderId){ boolean visible; if(_ppt2007){ - HSLFTextShape placeholder = _sheet.getPlaceholder(placeholderId); - visible = placeholder != null && placeholder.getText() != null; + HSLFSimpleShape ss = _sheet.getPlaceholder(placeholderId); + visible = ss instanceof HSLFTextShape && ((HSLFTextShape)ss).getText() != null; } else { visible = _container.getHeadersFootersAtom().getFlag(flag); } return visible; } - private String getPlaceholderText(int placeholderId, CString cs){ - String text = null; + private String getPlaceholderText(Placeholder ph, CString cs) { + String text; if (_ppt2007) { - HSLFTextShape placeholder = _sheet.getPlaceholder(placeholderId); - if (placeholder != null) { - text = placeholder.getText(); - } + HSLFSimpleShape ss = _sheet.getPlaceholder(ph); + text = (ss instanceof HSLFTextShape) ? ((HSLFTextShape)ss).getText() : null; // default text in master placeholders is not visible if("*".equals(text)) { text = null; } } else { - text = cs == null ? null : cs.getText(); + text = (cs == null) ? null : cs.getText(); } return text; } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java index 54d5d86221..3bdea4e4b2 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/OEPlaceholderAtom.java @@ -17,17 +17,18 @@ package org.apache.poi.hslf.record; -import org.apache.poi.util.LittleEndian; import java.io.IOException; import java.io.OutputStream; +import org.apache.poi.sl.usermodel.Placeholder; +import org.apache.poi.util.LittleEndian; + /** - * OEPlaceholderAtom (3011). - *

- * An atom record that specifies whether a shape is a placeholder shape. - *

+ * OEPlaceholderAtom (3011).

+ * + * An atom record that specifies whether a shape is a placeholder shape. * - * @author Yegor Kozlov + * @see Placeholder */ public final class OEPlaceholderAtom extends RecordAtom{ @@ -47,155 +48,6 @@ public final class OEPlaceholderAtom extends RecordAtom{ */ public static final int PLACEHOLDER_QUARTSIZE = 2; - /** - * MUST NOT be used for this field. - */ - public static final byte None = 0; - - /** - * The corresponding shape contains the master title text. - * The corresponding slide MUST be a main master slide. - */ - public static final byte MasterTitle = 1; - - /** - * The corresponding shape contains the master body text. - * The corresponding slide MUST be a main master slide. - */ - public static final byte MasterBody = 2; - - /** - * The corresponding shape contains the master center title text. - * The corresponding slide MUST be a title master slide. - */ - public static final byte MasterCenteredTitle = 3; - - /** - * The corresponding shape contains the master sub-title text. - * The corresponding slide MUST be a title master slide. - */ - public static final byte MasterSubTitle = 4; - - /** - * The corresponding shape contains the shared properties for slide image shapes. - * The corresponding slide MUST be a notes master slide. - */ - public static final byte MasterNotesSlideImage = 5; - - /** - * The corresponding shape contains the master body text. - * The corresponding slide MUST be a notes master slide. - */ - public static final byte MasterNotesBody = 6; - - /** - * The corresponding shape contains the date text field. - * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. - */ - public static final byte MasterDate = 7; - - /** - * The corresponding shape contains a slide number text field. - * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. - */ - public static final byte MasterSlideNumber = 8; - - /** - * The corresponding shape contains a footer text field. - * The corresponding slide MUST be a main master slide, title master slide, notes master slide, or handout master slide. - */ - public static final byte MasterFooter = 9; - - /** - * The corresponding shape contains a header text field. - * The corresponding slide must be a notes master slide or handout master slide. - */ - public static final byte MasterHeader = 10; - - /** - * The corresponding shape contains a presentation slide image. - * The corresponding slide MUST be a notes slide. - */ - public static final byte NotesSlideImage = 11; - - /** - * The corresponding shape contains the notes text. - * The corresponding slide MUST be a notes slide. - */ - public static final byte NotesBody = 12; - - /** - * The corresponding shape contains the title text. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Title = 13; - - /** - * The corresponding shape contains the body text. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Body = 14; - - /** - * The corresponding shape contains the title text. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte CenteredTitle = 15; - - /** - * The corresponding shape contains the sub-title text. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Subtitle = 16; - - /** - * The corresponding shape contains the title text with vertical text flow. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte VerticalTextTitle = 17; - - /** - * The corresponding shape contains the body text with vertical text flow. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte VerticalTextBody = 18; - - /** - * The corresponding shape contains a generic object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Object = 19; - - /** - * The corresponding shape contains a chart object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Graph = 20; - - /** - * The corresponding shape contains a table object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte Table = 21; - - /** - * The corresponding shape contains a clipart object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte ClipArt = 22; - - /** - * The corresponding shape contains an organization chart object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte OrganizationChart = 23; - - /** - * The corresponding shape contains a media object. - * The corresponding slide MUST be a presentation slide. - */ - public static final byte MediaClip = 24; - private byte[] _header; private int placementId; @@ -205,7 +57,7 @@ public final class OEPlaceholderAtom extends RecordAtom{ /** - * Create a new instance of OEPlaceholderAtom + * Create a new instance of {@code OEPlaceholderAtom} */ public OEPlaceholderAtom(){ _header = new byte[8]; @@ -219,7 +71,7 @@ public final class OEPlaceholderAtom extends RecordAtom{ } /** - * Build an instance of OEPlaceholderAtom from on-disk data + * Build an instance of {@code OEPlaceholderAtom} from on-disk data */ protected OEPlaceholderAtom(byte[] source, int start, int len) { _header = new byte[8]; @@ -236,15 +88,15 @@ public final class OEPlaceholderAtom extends RecordAtom{ /** * @return type of this record {@link RecordTypes#OEPlaceholderAtom}. */ - public long getRecordType() { return RecordTypes.OEPlaceholderAtom.typeID; } + @Override + public long getRecordType() { return RecordTypes.OEPlaceholderAtom.typeID; } /** - * Returns the placement Id. - *

+ * Returns the placement Id.

+ * * The placement Id is a number assigned to the placeholder. It goes from -1 to the number of placeholders. * It SHOULD be unique among all PlacholderAtom records contained in the corresponding slide. * The value 0xFFFFFFFF specifies that the corresponding shape is not a placeholder shape. - *

* * @return the placement Id. */ @@ -253,12 +105,11 @@ public final class OEPlaceholderAtom extends RecordAtom{ } /** - * Sets the placement Id. - *

+ * Sets the placement Id.

+ * * The placement Id is a number assigned to the placeholder. It goes from -1 to the number of placeholders. * It SHOULD be unique among all PlacholderAtom records contained in the corresponding slide. * The value 0xFFFFFFFF specifies that the corresponding shape is not a placeholder shape. - *

* * @param id the placement Id. */ @@ -267,12 +118,10 @@ public final class OEPlaceholderAtom extends RecordAtom{ } /** - * Returns the placeholder Id. + * Returns the placeholder Id.

* - *

* placeholder Id specifies the type of the placeholder shape. * The value MUST be one of the static constants defined in this class - *

* * @return the placeholder Id. */ @@ -281,12 +130,11 @@ public final class OEPlaceholderAtom extends RecordAtom{ } /** - * Sets the placeholder Id. + * Sets the placeholder Id.

* - *

* placeholder Id specifies the type of the placeholder shape. * The value MUST be one of the static constants defined in this class - *

+ * * @param id the placeholder Id. */ public void setPlaceholderId(byte id){ @@ -314,10 +162,10 @@ public final class OEPlaceholderAtom extends RecordAtom{ } /** - * Write the contents of the record back, so it can be written - * to disk + * Write the contents of the record back, so it can be written to disk */ - public void writeOut(OutputStream out) throws IOException { + @Override + public void writeOut(OutputStream out) throws IOException { out.write(_header); byte[] recdata = new byte[8]; diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java index 16be0c6a80..9d553740bf 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtom.java @@ -20,23 +20,21 @@ package org.apache.poi.hslf.record; import java.io.IOException; import java.io.OutputStream; -import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.poi.hslf.record.SlideAtomLayout.SlideLayoutType; import org.apache.poi.util.LittleEndian; /** * A Slide Atom (type 1007). Holds information on the parent Slide, what - * Master Slide it uses, what Notes is attached to it, that sort of thing. - * It also has a SSlideLayoutAtom embeded in it, but without the Atom header - * - * @author Nick Burch + * Master Slide it uses, what Notes is attached to it, that sort of thing. + * It also has a SSlideLayoutAtom embedded in it, but without the Atom header */ -public final class SlideAtom extends RecordAtom -{ - private byte[] _header; +public final class SlideAtom extends RecordAtom { + public static final int USES_MASTER_SLIDE_ID = 0x80000000; + // private static final int MASTER_SLIDE_ID = 0x00000000; + + private byte[] _header; private static long _type = 1007l; - public static final int MASTER_SLIDE_ID = 0; - public static final int USES_MASTER_SLIDE_ID = -2147483648; private int masterID; private int notesID; @@ -44,7 +42,7 @@ public final class SlideAtom extends RecordAtom private boolean followMasterObjects; private boolean followMasterScheme; private boolean followMasterBackground; - private SSlideLayoutAtom layoutAtom; + private SlideAtomLayout layoutAtom; private byte[] reserved; @@ -54,8 +52,8 @@ public final class SlideAtom extends RecordAtom public void setMasterID(int id) { masterID = id; } /** Get the ID of the notes for this slide. 0 if doesn't have one */ public int getNotesID() { return notesID; } - /** Get the embeded SSlideLayoutAtom */ - public SSlideLayoutAtom getSSlideLayoutAtom() { return layoutAtom; } + /** Get the embedded SSlideLayoutAtom */ + public SlideAtomLayout getSSlideLayoutAtom() { return layoutAtom; } /** Change the ID of the notes for this slide. 0 if it no longer has one */ public void setNotesID(int id) { notesID = id; } @@ -85,7 +83,7 @@ public final class SlideAtom extends RecordAtom byte[] SSlideLayoutAtomData = new byte[12]; System.arraycopy(source,start+8,SSlideLayoutAtomData,0,12); // Use them to build up the SSlideLayoutAtom - layoutAtom = new SSlideLayoutAtom(SSlideLayoutAtomData); + layoutAtom = new SlideAtomLayout(SSlideLayoutAtomData); // Get the IDs of the master and notes masterID = LittleEndian.getInt(source,start+12+8); @@ -125,13 +123,13 @@ public final class SlideAtom extends RecordAtom LittleEndian.putInt(_header, 4, 24); byte[] ssdate = new byte[12]; - layoutAtom = new SSlideLayoutAtom(ssdate); - layoutAtom.setGeometryType(SSlideLayoutAtom.BLANK_SLIDE); + layoutAtom = new SlideAtomLayout(ssdate); + layoutAtom.setGeometryType(SlideLayoutType.BLANK_SLIDE); followMasterObjects = true; followMasterScheme = true; followMasterBackground = true; - masterID = -2147483648; + masterID = USES_MASTER_SLIDE_ID; // -2147483648; notesID = 0; reserved = new byte[2]; } @@ -168,70 +166,4 @@ public final class SlideAtom extends RecordAtom // Reserved data out.write(reserved); } - - - /** - * Holds the geometry of the Slide, and the ID of the placeholders - * on the slide. - * (Embeded inside SlideAtom is a SSlideLayoutAtom, without the - * usual record header. Since it's a fixed size and tied to - * the SlideAtom, we'll hold it here.) - */ - public static class SSlideLayoutAtom { - // The different kinds of geometry - public static final int TITLE_SLIDE = 0; - public static final int TITLE_BODY_SLIDE = 1; - public static final int TITLE_MASTER_SLIDE = 2; - public static final int MASTER_SLIDE = 3; - public static final int MASTER_NOTES = 4; - public static final int NOTES_TITLE_BODY = 5; - public static final int HANDOUT = 6; // Only header, footer and date placeholders - public static final int TITLE_ONLY = 7; - public static final int TITLE_2_COLUMN_BODY = 8; - public static final int TITLE_2_ROW_BODY = 9; - public static final int TITLE_2_COLUNM_RIGHT_2_ROW_BODY = 10; - public static final int TITLE_2_COLUNM_LEFT_2_ROW_BODY = 11; - public static final int TITLE_2_ROW_BOTTOM_2_COLUMN_BODY = 12; - public static final int TITLE_2_ROW_TOP_2_COLUMN_BODY = 13; - public static final int FOUR_OBJECTS = 14; - public static final int BIG_OBJECT = 15; - public static final int BLANK_SLIDE = 16; - public static final int VERTICAL_TITLE_BODY_LEFT = 17; - public static final int VERTICAL_TITLE_2_ROW_BODY_LEFT = 17; - - /** What geometry type we are */ - private int geometry; - /** What placeholder IDs we have */ - private byte[] placeholderIDs; - - /** Retrieve the geometry type */ - public int getGeometryType() { return geometry; } - /** Set the geometry type */ - public void setGeometryType(int geom) { geometry = geom; } - - /** - * Create a new Embeded SSlideLayoutAtom, from 12 bytes of data - */ - public SSlideLayoutAtom(byte[] data) { - if(data.length != 12) { - throw new HSLFException("SSlideLayoutAtom created with byte array not 12 bytes long - was " + data.length + " bytes in size"); - } - - // Grab out our data - geometry = LittleEndian.getInt(data,0); - placeholderIDs = new byte[8]; - System.arraycopy(data,4,placeholderIDs,0,8); - } - - /** - * Write the contents of the record back, so it can be written - * to disk. Skips the record header - */ - public void writeOut(OutputStream out) throws IOException { - // Write the geometry - writeLittleEndian(geometry,out); - // Write the placeholder IDs - out.write(placeholderIDs); - } - } } diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java new file mode 100644 index 0000000000..c6bd795757 --- /dev/null +++ b/src/scratchpad/src/org/apache/poi/hslf/record/SlideAtomLayout.java @@ -0,0 +1,133 @@ +/* ==================================================================== + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You under the Apache License, Version 2.0 + (the "License"); you may not use this file except in compliance with + the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +==================================================================== */ + +package org.apache.poi.hslf.record; + +import java.io.IOException; +import java.io.OutputStream; + +import org.apache.poi.hslf.exceptions.HSLFException; +import org.apache.poi.util.Internal; +import org.apache.poi.util.LittleEndian; + +/** + * Holds the geometry of the Slide, and the ID of the placeholders on the slide. + * Embedded inside a SlideAtom is a SlideAtomLayout, without the usual record header. + * Since it's a fixed size and tied to the SlideAtom, we'll hold it here.

+ * + * This might eventually merged with the XSLF counterpart + */ +@Internal +public class SlideAtomLayout { + // The different kinds of geometry + public enum SlideLayoutType { + /** One title and one subtitle placeholder shapes. */ + TITLE_SLIDE(0x0000), + /** Presentation slide or main master slide layout with one title and one body placeholder shape. */ + TITLE_BODY(0x0001), + /** Title master slide layout with one title and one subtitle placeholder shape. */ + MASTER_TITLE(0x0002), + /** ??? (not documented in spec) */ + MASTER_SLIDE(0x0003), + /** ??? (not documented in spec) */ + MASTER_NOTES(0x0004), + /** ??? (not documented in spec) */ + NOTES_TITLE_BODY(0x0005), + /** Only header, footer and date placeholders */ + HANDOUT(0x0006), + /** Presentation slide layout with one title placeholder shape. */ + TITLE_ONLY(0x0007), + /** Presentation slide layout with one title and two body placeholder shapes stacked horizontally. */ + TWO_COLUMNS(0x0008), + /** Presentation slide layout with one title and two body placeholder shapes stacked vertically. */ + TWO_ROWS(0x0009), + /** Presentation slide layout with one title and three body placeholder shapes split into two columns. The right column has two rows. */ + COLUMN_TWO_ROWS(0x000A), + /** Presentation slide layout with one title and three body placeholder shapes split into two columns. The left column has two rows. */ + TWO_ROWS_COLUMN(0x000B), + /** ??? (not documented in spec) */ + TITLE_2_ROW_BOTTOM_2_COLUMN_BODY(0x000C), + /** Presentation slide layout with one title and three body placeholder shapes split into two rows. The top row has two columns. */ + TWO_COLUMNS_ROW(0x000D), + /** Presentation slide layout with one title and four body placeholder shapes. */ + FOUR_OBJECTS(0x000E), + /** Presentation slide layout with one body placeholder shape. */ + BIG_OBJECT(0x000F), + /** Presentation slide layout with no placeholder shape. */ + BLANK_SLIDE(0x0010), + /** Presentation slide layout with a vertical title placeholder shape on the right and a body placeholder shape on the left. */ + VERTICAL_TITLE_BODY(0x0011), + /** Presentation slide layout with a vertical title placeholder shape on the right and two body placeholder shapes in two columns on the left. */ + VERTICAL_TWO_ROWS(0x0012); + + private int nativeId; + SlideLayoutType(int nativeId) { + this.nativeId = nativeId; + } + + public int getNativeId() { + return nativeId; + } + + public static SlideLayoutType forNativeID(int nativeId) { + for (SlideLayoutType ans : values()) { + if (ans.nativeId == nativeId) { + return ans; + } + } + return null; + } + } + + /** What geometry type we are */ + private SlideLayoutType geometry; + /** What placeholder IDs we have */ + private byte[] placeholderIDs; + + /** Retrieve the geometry type */ + public SlideLayoutType getGeometryType() { return geometry; } + /** Set the geometry type */ + public void setGeometryType(SlideLayoutType geom) { geometry = geom; } + + /** + * Create a new Embedded SSlideLayoutAtom, from 12 bytes of data + */ + public SlideAtomLayout(byte[] data) { + if(data.length != 12) { + throw new HSLFException("SSlideLayoutAtom created with byte array not 12 bytes long - was " + data.length + " bytes in size"); + } + + // Grab out our data + geometry = SlideLayoutType.forNativeID(LittleEndian.getInt(data,0)); + placeholderIDs = new byte[8]; + System.arraycopy(data,4,placeholderIDs,0,8); + } + + /** + * Write the contents of the record back, so it can be written + * to disk. Skips the record header + */ + public void writeOut(OutputStream out) throws IOException { + // Write the geometry + byte[] buf = new byte[4]; + LittleEndian.putInt(buf, 0, geometry.getNativeId()); + out.write(buf); + // Write the placeholder IDs + out.write(placeholderIDs); + } + +} diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java index 238385b684..5291eb2fe3 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/StyleTextPropAtom.java @@ -42,15 +42,11 @@ import org.apache.poi.util.POILogger; * the style applies to, and what style elements make up the style (another * list, this time of TextProps). Each TextProp has a value, which somehow * encapsulates a property of the style - * - * @author Nick Burch - * @author Yegor Kozlov */ -public final class StyleTextPropAtom extends RecordAtom -{ +public final class StyleTextPropAtom extends RecordAtom { + public static final long _type = RecordTypes.StyleTextPropAtom.typeID; private byte[] _header; - private static final long _type = RecordTypes.StyleTextPropAtom.typeID; private byte[] reserved; private byte[] rawContents; // Holds the contents between write-outs diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java index 43a5abf8aa..0badc3e31d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextBytesAtom.java @@ -17,25 +17,23 @@ package org.apache.poi.hslf.record; +import java.io.IOException; +import java.io.OutputStream; + import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; -import java.io.IOException; -import java.io.OutputStream; /** * A TextBytesAtom (type 4008). Holds text in ascii form (unknown * code page, for now assumed to be the default of * org.apache.poi.util.StringUtil, which is the Excel default). * The trailing return character is always stripped from this - * - * @author Nick Burch */ -public final class TextBytesAtom extends RecordAtom -{ +public final class TextBytesAtom extends RecordAtom { + public static final long _type = RecordTypes.TextBytesAtom.typeID; private byte[] _header; - private static long _type = RecordTypes.TextBytesAtom.typeID; /** The bytes that make up the text */ private byte[] _text; @@ -87,13 +85,15 @@ public final class TextBytesAtom extends RecordAtom /** * We are of type 4008 */ - public long getRecordType() { return _type; } + @Override + public long getRecordType() { return _type; } /** * Write the contents of the record back, so it can be written * to disk */ - public void writeOut(OutputStream out) throws IOException { + @Override + public void writeOut(OutputStream out) throws IOException { // Header - size or type unchanged out.write(_header); @@ -105,7 +105,8 @@ public final class TextBytesAtom extends RecordAtom * dump debug info; use getText() to return a string * representation of the atom */ - public String toString() { + @Override + public String toString() { StringBuffer out = new StringBuffer(); out.append( "TextBytesAtom:\n"); out.append( HexDump.dump(_text, 0, 0) ); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java index 3449250ada..fa8ff836d1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextCharsAtom.java @@ -17,23 +17,21 @@ package org.apache.poi.hslf.record; +import java.io.IOException; +import java.io.OutputStream; + import org.apache.poi.util.HexDump; import org.apache.poi.util.LittleEndian; import org.apache.poi.util.StringUtil; -import java.io.IOException; -import java.io.OutputStream; /** * A TextCharsAtom (type 4000). Holds text in byte swapped unicode form. * The trailing return character is always stripped from this - * - * @author Nick Burch */ -public final class TextCharsAtom extends RecordAtom -{ +public final class TextCharsAtom extends RecordAtom { + public static final long _type = RecordTypes.TextCharsAtom.typeID; private byte[] _header; - private static long _type = RecordTypes.TextCharsAtom.typeID; /** The bytes that make up the text */ private byte[] _text; @@ -83,13 +81,15 @@ public final class TextCharsAtom extends RecordAtom /** * We are of type 4000 */ - public long getRecordType() { return _type; } + @Override + public long getRecordType() { return _type; } /** * Write the contents of the record back, so it can be written * to disk */ - public void writeOut(OutputStream out) throws IOException { + @Override + public void writeOut(OutputStream out) throws IOException { // Header - size or type unchanged out.write(_header); @@ -101,7 +101,8 @@ public final class TextCharsAtom extends RecordAtom * dump debug info; use getText() to return a string * representation of the atom */ - public String toString() { + @Override + public String toString() { StringBuffer out = new StringBuffer(); out.append( "TextCharsAtom:\n"); out.append( HexDump.dump(_text, 0, 0) ); diff --git a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java index 2d4f41bddc..05c61a667d 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java +++ b/src/scratchpad/src/org/apache/poi/hslf/record/TextHeaderAtom.java @@ -27,14 +27,11 @@ import org.apache.poi.util.LittleEndian; * A TextHeaderAtom (type 3999). Holds information on what kind of * text is contained in the TextBytesAtom / TextCharsAtom that follows * straight after - * - * @author Nick Burch */ -public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecord -{ +public final class TextHeaderAtom extends RecordAtom implements ParentAwareRecord { + public static final long _type = RecordTypes.TextHeaderAtom.typeID; private byte[] _header; - private static long _type = RecordTypes.TextHeaderAtom.typeID; private RecordContainer parentRecord; public static final int TITLE_TYPE = 0; 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 5bfd3bbc9c..cc41706347 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFSheet.java @@ -30,15 +30,14 @@ import org.apache.poi.ddf.EscherRecord; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.record.CString; import org.apache.poi.hslf.record.ColorSchemeAtom; -import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.record.PPDrawing; import org.apache.poi.hslf.record.RecordContainer; import org.apache.poi.hslf.record.RecordTypes; -import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; import org.apache.poi.hslf.record.SheetContainer; import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.Drawable; import org.apache.poi.sl.usermodel.PictureData; +import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.ShapeType; import org.apache.poi.sl.usermodel.Sheet; import org.apache.poi.util.Internal; @@ -108,6 +107,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet> trs = getTextParagraphs(); - if (trs == null) return; + if (trs == null) { + return; + } for (List ltp : trs) { HSLFTextParagraph.supplySheet(ltp, this); HSLFTextParagraph.applyHyperlinks(ltp); @@ -192,6 +194,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet= dgg.getShapeIdMax()) + if (result >= dgg.getShapeIdMax()) { dgg.setShapeIdMax( result + 1 ); + } return result; } } @@ -238,8 +242,9 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet= dgg.getShapeIdMax()) + if (result >= dgg.getShapeIdMax()) { dgg.setShapeIdMax( result + 1 ); + } return result; } @@ -249,6 +254,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheettrue if the shape was deleted. */ + @Override public boolean removeShape(HSLFShape shape) { PPDrawing ppdrawing = getPPDrawing(); @@ -274,6 +280,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, SheetTextShape or null + * @return {@code SimpleShape} or {@code null} */ - public HSLFTextShape getPlaceholder(int type){ + public HSLFSimpleShape getPlaceholder(Placeholder type){ for (HSLFShape shape : getShapes()) { - if(shape instanceof HSLFTextShape){ - HSLFTextShape tx = (HSLFTextShape)shape; - int placeholderId = 0; - OEPlaceholderAtom oep = tx.getPlaceholderAtom(); - if(oep != null) { - placeholderId = oep.getPlaceholderId(); - } else { - //special case for files saved in Office 2007 - RoundTripHFPlaceholder12 hldr = tx.getClientDataRecord(RecordTypes.RoundTripHFPlaceholder12.typeID); - if(hldr != null) placeholderId = hldr.getPlaceholderId(); - } - if(placeholderId == type){ - return tx; + if (shape instanceof HSLFSimpleShape) { + HSLFSimpleShape ss = (HSLFSimpleShape)shape; + if (type == ss.getPlaceholder()) { + return ss; } } } @@ -382,7 +381,9 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet iterator() { return getShapes().iterator(); } @@ -400,6 +402,7 @@ public abstract class HSLFSheet implements HSLFShapeContainer, Sheet l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { - if (!_paragraphs.contains(l)) _paragraphs.add(l); + if (!_paragraphs.contains(l)) { + _paragraphs.add(l); + } } } /** * Returns an array of all the TextRuns found */ + @Override public List> getTextParagraphs() { return _paragraphs; } @@ -63,6 +68,7 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { /** * Returns null since SlideMasters doen't have master sheet. */ + @Override public HSLFMasterSheet getMasterSheet() { return null; } @@ -71,8 +77,11 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { * Pickup a style attribute from the master. * This is the "workhorse" which returns the default style attributes. */ + @Override public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) { - if (_txmaster.length <= txtype) return null; + if (_txmaster.length <= txtype) { + return null; + } TxMasterStyleAtom t = _txmaster[txtype]; List styles = isCharacter ? t.getCharacterStyles() : t.getParagraphStyles(); @@ -81,7 +90,9 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { prop = styles.get(i).findByName(name); } - if (prop != null) return prop; + if (prop != null) { + return prop; + } switch (txtype) { case TextHeaderAtom.CENTRE_BODY_TYPE: @@ -146,6 +157,7 @@ public final class HSLFSlideMaster extends HSLFMasterSheet { } } + @Override protected void onAddTextShape(HSLFTextShape shape) { List runs = shape.getTextParagraphs(); _paragraphs.add(runs); 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 60232d50a7..afe5d71264 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextParagraph.java @@ -36,14 +36,35 @@ import org.apache.poi.hslf.model.textproperties.TextPFException9; import org.apache.poi.hslf.model.textproperties.TextProp; 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.ColorSchemeAtom; +import org.apache.poi.hslf.record.EscherTextboxWrapper; +import org.apache.poi.hslf.record.FontCollection; +import org.apache.poi.hslf.record.InteractiveInfo; +import org.apache.poi.hslf.record.MasterTextPropAtom; +import org.apache.poi.hslf.record.OutlineTextRefAtom; +import org.apache.poi.hslf.record.PPDrawing; +import org.apache.poi.hslf.record.Record; +import org.apache.poi.hslf.record.RecordContainer; +import org.apache.poi.hslf.record.RecordTypes; +import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; +import org.apache.poi.hslf.record.SlideListWithText; +import org.apache.poi.hslf.record.SlidePersistAtom; +import org.apache.poi.hslf.record.StyleTextProp9Atom; +import org.apache.poi.hslf.record.StyleTextPropAtom; +import org.apache.poi.hslf.record.TextBytesAtom; +import org.apache.poi.hslf.record.TextCharsAtom; +import org.apache.poi.hslf.record.TextHeaderAtom; +import org.apache.poi.hslf.record.TextRulerAtom; +import org.apache.poi.hslf.record.TextSpecInfoAtom; +import org.apache.poi.hslf.record.TxInteractiveInfoAtom; import org.apache.poi.sl.draw.DrawPaint; import org.apache.poi.sl.usermodel.AutoNumberingScheme; +import org.apache.poi.sl.usermodel.MasterSheet; import org.apache.poi.sl.usermodel.PaintStyle; import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; +import org.apache.poi.sl.usermodel.Placeholder; import org.apache.poi.sl.usermodel.TextParagraph; import org.apache.poi.util.Internal; -import org.apache.poi.util.LocaleUtil; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; import org.apache.poi.util.StringUtil; @@ -53,8 +74,6 @@ import org.apache.poi.util.Units; * This class represents a run of text in a powerpoint document. That * run could be text on a sheet, or text in a note. * It is only a very basic class for now - * - * @author Nick Burch */ public final class HSLFTextParagraph implements TextParagraph { @@ -74,6 +93,7 @@ public final class HSLFTextParagraph implements TextParagraph _runs = new ArrayList(); @@ -139,7 +159,7 @@ public final class HSLFTextParagraph implements TextParagraph createEmptyParagraph() { - EscherTextboxWrapper wrapper = new EscherTextboxWrapper(); - return createEmptyParagraph(wrapper); - } - - protected static List createEmptyParagraph(EscherTextboxWrapper wrapper) { - TextHeaderAtom tha = new TextHeaderAtom(); - tha.setParentRecord(wrapper); - wrapper.appendChildRecord(tha); - - TextBytesAtom tba = new TextBytesAtom(); - tba.setText("".getBytes(LocaleUtil.CHARSET_1252)); - wrapper.appendChildRecord(tba); - - StyleTextPropAtom sta = new StyleTextPropAtom(1); - TextPropCollection paraStyle = sta.addParagraphTextPropCollection(1); - TextPropCollection charStyle = sta.addCharacterTextPropCollection(1); - wrapper.appendChildRecord(sta); - - List paragraphs = new ArrayList(1); - HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, null, paragraphs); - htp.setParagraphStyle(paraStyle); - paragraphs.add(htp); - - HSLFTextRun htr = new HSLFTextRun(htp); - htr.setCharacterStyle(charStyle); - htr.setText(""); - htp.addTextRun(htr); - - return paragraphs; - } - public EscherTextboxWrapper getTextboxWrapper() { return (EscherTextboxWrapper) _headerAtom.getParentRecord(); } @@ -1583,7 +1588,7 @@ public final class HSLFTextParagraph implements TextParagraph 0; } + @Override public byte getPitchAndFamily() { return 0; } @@ -414,4 +423,20 @@ public final class HSLFTextRun implements TextRun { } return link; } + + @Override + public FieldType getFieldType() { + Placeholder ph = getTextParagraph().getParentShape().getPlaceholder(); + if (ph != null) { + switch (ph) { + case SLIDE_NUMBER: + return FieldType.SLIDE_NUMBER; + case DATETIME: + return FieldType.DATE_TIME; + default: + break; + } + } + return null; + } } 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 2af4c07887..4992d1bd45 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTextShape.java @@ -20,7 +20,6 @@ package org.apache.poi.hslf.usermodel; import static org.apache.poi.hslf.record.RecordTypes.OEPlaceholderAtom; import static org.apache.poi.hslf.record.RecordTypes.RoundTripHFPlaceholder12; -import java.awt.font.FontRenderContext; import java.awt.geom.Rectangle2D; import java.io.IOException; import java.util.ArrayList; @@ -34,10 +33,14 @@ import org.apache.poi.ddf.EscherSimpleProperty; import org.apache.poi.ddf.EscherTextboxRecord; import org.apache.poi.hslf.exceptions.HSLFException; import org.apache.poi.hslf.model.HSLFMetroShape; +import org.apache.poi.hslf.model.textproperties.TextPropCollection; import org.apache.poi.hslf.record.EscherTextboxWrapper; import org.apache.poi.hslf.record.OEPlaceholderAtom; import org.apache.poi.hslf.record.PPDrawing; import org.apache.poi.hslf.record.RoundTripHFPlaceholder12; +import org.apache.poi.hslf.record.StyleTextPropAtom; +import org.apache.poi.hslf.record.TextBytesAtom; +import org.apache.poi.hslf.record.TextCharsAtom; import org.apache.poi.hslf.record.TextHeaderAtom; import org.apache.poi.sl.draw.DrawFactory; import org.apache.poi.sl.draw.DrawTextShape; @@ -71,12 +74,12 @@ implements TextShape { BOTTOM_BASELINE (7, VerticalAlignment.BOTTOM, false, true), TOP_CENTER_BASELINE (8, VerticalAlignment.TOP, true, true), BOTTOM_CENTER_BASELINE(9, VerticalAlignment.BOTTOM, true, true); - + public final int nativeId; public final VerticalAlignment vAlign; public final boolean centered; public final Boolean baseline; - + HSLFTextAnchor(int nativeId, VerticalAlignment vAlign, boolean centered, Boolean baseline) { this.nativeId = nativeId; this.vAlign = vAlign; @@ -86,7 +89,9 @@ implements TextShape { static HSLFTextAnchor fromNativeId(int nativeId) { for (HSLFTextAnchor ta : values()) { - if (ta.nativeId == nativeId) return ta; + if (ta.nativeId == nativeId) { + return ta; + } } return null; } @@ -125,13 +130,13 @@ implements TextShape { /** * TextRun object which holds actual text and format data */ - protected List _paragraphs = new ArrayList(); + private List _paragraphs = new ArrayList(); /** * Escher container which holds text attributes such as - * TextHeaderAtom, TextBytesAtom ot TextCharsAtom, StyleTextPropAtom etc. + * TextHeaderAtom, TextBytesAtom or TextCharsAtom, StyleTextPropAtom etc. */ - protected EscherTextboxWrapper _txtbox; + private EscherTextboxWrapper _txtbox; /** * This setting is used for supporting a deprecated alignment @@ -140,11 +145,6 @@ implements TextShape { */ // boolean alignToBaseline = false; - /** - * Used to calculate text bounds - */ - protected static final FontRenderContext _frc = new FontRenderContext(null, true, true); - /** * Create a TextBox object and initialize it from the supplied Record container. * @@ -222,10 +222,14 @@ implements TextShape { } protected EscherTextboxWrapper getEscherTextboxWrapper(){ - if(_txtbox != null) return _txtbox; + if(_txtbox != null) { + return _txtbox; + } EscherTextboxRecord textRecord = getEscherChild(EscherTextboxRecord.RECORD_ID); - if (textRecord == null) return null; + if (textRecord == null) { + return null; + } HSLFSheet sheet = getSheet(); if (sheet != null) { @@ -248,6 +252,66 @@ implements TextShape { return _txtbox; } + private void createEmptyParagraph() { + TextHeaderAtom tha = (TextHeaderAtom)_txtbox.findFirstOfType(TextHeaderAtom._type); + if (tha == null) { + tha = new TextHeaderAtom(); + tha.setParentRecord(_txtbox); + _txtbox.appendChildRecord(tha); + } + + TextBytesAtom tba = (TextBytesAtom)_txtbox.findFirstOfType(TextBytesAtom._type); + TextCharsAtom tca = (TextCharsAtom)_txtbox.findFirstOfType(TextCharsAtom._type); + if (tba == null && tca == null) { + tba = new TextBytesAtom(); + tba.setText(new byte[0]); + _txtbox.appendChildRecord(tba); + } + + final String text = ((tba != null) ? tba.getText() : tca.getText()); + + StyleTextPropAtom sta = (StyleTextPropAtom)_txtbox.findFirstOfType(StyleTextPropAtom._type); + TextPropCollection paraStyle = null, charStyle = null; + if (sta == null) { + int parSiz = text.length(); + sta = new StyleTextPropAtom(parSiz+1); + if (_paragraphs.isEmpty()) { + paraStyle = sta.addParagraphTextPropCollection(parSiz+1); + charStyle = sta.addCharacterTextPropCollection(parSiz+1); + } else { + for (HSLFTextParagraph htp : _paragraphs) { + int runsLen = 0; + for (HSLFTextRun htr : htp.getTextRuns()) { + runsLen += htr.getLength(); + charStyle = sta.addCharacterTextPropCollection(htr.getLength()); + htr.setCharacterStyle(charStyle); + } + paraStyle = sta.addParagraphTextPropCollection(runsLen); + htp.setParagraphStyle(paraStyle); + } + assert (paraStyle != null && charStyle != null); + } + _txtbox.appendChildRecord(sta); + } else { + paraStyle = sta.getParagraphStyles().get(0); + charStyle = sta.getCharacterStyles().get(0); + } + + if (_paragraphs.isEmpty()) { + HSLFTextParagraph htp = new HSLFTextParagraph(tha, tba, tca, _paragraphs); + htp.setParagraphStyle(paraStyle); + htp.setParentShape(this); + _paragraphs.add(htp); + + HSLFTextRun htr = new HSLFTextRun(htp); + htr.setCharacterStyle(charStyle); + htr.setText(text); + htp.addTextRun(htr); + } + } + + + /** * Adjust the size of the shape so it encompasses the text inside it. * @@ -276,7 +340,9 @@ implements TextShape { */ public int getRunType() { getEscherTextboxWrapper(); - if (_txtbox == null) return -1; + if (_txtbox == null) { + return -1; + } List paras = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); return (paras.isEmpty()) ? -1 : paras.get(0).getRunType(); } @@ -289,7 +355,9 @@ implements TextShape { */ public void setRunType(int type) { getEscherTextboxWrapper(); - if (_txtbox == null) return; + if (_txtbox == null) { + return; + } List paras = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); if (!paras.isEmpty()) { paras.get(0).setRunType(type); @@ -562,19 +630,23 @@ implements TextShape { @Override public List getTextParagraphs(){ - if (!_paragraphs.isEmpty()) return _paragraphs; + if (!_paragraphs.isEmpty()) { + return _paragraphs; + } _txtbox = getEscherTextboxWrapper(); if (_txtbox == null) { - _paragraphs.addAll(HSLFTextParagraph.createEmptyParagraph()); - _txtbox = _paragraphs.get(0).getTextboxWrapper(); + _txtbox = new EscherTextboxWrapper(); + createEmptyParagraph(); } else { - _paragraphs = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); - if (_paragraphs == null) { + List pList = HSLFTextParagraph.findTextParagraphs(_txtbox, getSheet()); + if (pList == null) { // there are actually TextBoxRecords without extra data - see #54722 - _paragraphs = HSLFTextParagraph.createEmptyParagraph(_txtbox); + createEmptyParagraph(); + } else { + _paragraphs = pList; } - + if (_paragraphs.isEmpty()) { LOG.log(POILogger.WARN, "TextRecord didn't contained any text lines"); } @@ -587,6 +659,7 @@ implements TextShape { return _paragraphs; } + @Override public void setSheet(HSLFSheet sheet) { super.setSheet(sheet); @@ -611,26 +684,30 @@ implements TextShape { /** * Return {@link RoundTripHFPlaceholder12}, the atom that describes a header/footer placeholder. * Compare the {@link RoundTripHFPlaceholder12#getPlaceholderId()} with - * {@link OEPlaceholderAtom#MasterHeader} or {@link OEPlaceholderAtom#MasterFooter}, to find out + * {@link Placeholder#HEADER} or {@link Placeholder#FOOTER}, to find out * what kind of placeholder this is. * * @return {@link RoundTripHFPlaceholder12} or {@code null} if not found - * + * * @since POI 3.14-Beta2 */ public RoundTripHFPlaceholder12 getHFPlaceholderAtom() { // special case for files saved in Office 2007 return getClientDataRecord(RoundTripHFPlaceholder12.typeID); } - + @Override public boolean isPlaceholder() { OEPlaceholderAtom oep = getPlaceholderAtom(); - if (oep != null) return true; + if (oep != null) { + return true; + } //special case for files saved in Office 2007 RoundTripHFPlaceholder12 hldr = getHFPlaceholderAtom(); - if (hldr != null) return true; + if (hldr != null) { + return true; + } return false; } @@ -710,7 +787,7 @@ implements TextShape { } setEscherProperty(opt, EscherProperties.TEXT__TEXTFLOW, msotxfl); } - + @Override public Double getTextRotation() { // see 2.4.6 MSOCDIR @@ -718,7 +795,7 @@ implements TextShape { EscherSimpleProperty prop = getEscherProperty(opt, EscherProperties.TEXT__FONTROTATION); return (prop == null) ? null : (90. * prop.getPropertyValue()); } - + @Override public void setTextRotation(Double rotation) { AbstractEscherOptRecord opt = getEscherOptRecord(); @@ -729,7 +806,7 @@ implements TextShape { setEscherProperty(EscherProperties.TEXT__FONTROTATION, rot); } } - + /** * Returns the raw text content of the shape. This hasn't had any * changes applied to it, and so is probably unlikely to print @@ -751,7 +828,7 @@ implements TextShape { List paras = getTextParagraphs(); HSLFTextRun htr = HSLFTextParagraph.appendText(paras, text, newParagraph); setTextId(getRawText().hashCode()); - return htr; + return htr; } @Override 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 b1e38d977b..2dfd3b05b6 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTitleMaster.java @@ -25,8 +25,6 @@ import org.apache.poi.hslf.record.SlideAtom; /** * Title masters define the design template for slides with a Title Slide layout. - * - * @author Yegor Kozlov */ public final class HSLFTitleMaster extends HSLFMasterSheet { private final List> _paragraphs = new ArrayList>(); @@ -39,13 +37,16 @@ public final class HSLFTitleMaster extends HSLFMasterSheet { super(record, sheetNo); for (List l : HSLFTextParagraph.findTextParagraphs(getPPDrawing(), this)) { - if (!_paragraphs.contains(l)) _paragraphs.add(l); + if (!_paragraphs.contains(l)) { + _paragraphs.add(l); + } } } /** * Returns an array of all the TextRuns found */ + @Override public List> getTextParagraphs() { return _paragraphs; } @@ -53,20 +54,23 @@ public final class HSLFTitleMaster extends HSLFMasterSheet { /** * Delegate the call to the underlying slide master. */ + @Override public TextProp getStyleAttribute(int txtype, int level, String name, boolean isCharacter) { HSLFMasterSheet master = getMasterSheet(); - return master == null ? null : master.getStyleAttribute(txtype, level, name, isCharacter); + return (master == null) ? null : master.getStyleAttribute(txtype, level, name, isCharacter); } /** * Returns the slide master for this title master. */ + @Override public HSLFMasterSheet getMasterSheet(){ - List master = getSlideShow().getSlideMasters(); SlideAtom sa = ((org.apache.poi.hslf.record.Slide)getSheetContainer()).getSlideAtom(); int masterId = sa.getMasterID(); - for (HSLFSlideMaster sm : master) { - if (masterId == sm._getSheetNumber()) return sm; + for (HSLFSlideMaster sm : getSlideShow().getSlideMasters()) { + if (masterId == sm._getSheetNumber()) { + return sm; + } } return null; } diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlideAtom.java b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlideAtom.java index 7e6137ba47..c32d37bd65 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlideAtom.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/record/TestSlideAtom.java @@ -17,36 +17,43 @@ package org.apache.poi.hslf.record; -import java.io.ByteArrayInputStream; +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + import java.io.ByteArrayOutputStream; +import java.io.IOException; -import org.apache.poi.hslf.record.SlideAtom.SSlideLayoutAtom; +import org.apache.poi.hslf.HSLFTestDataSamples; +import org.apache.poi.hslf.record.SlideAtomLayout.SlideLayoutType; +import org.apache.poi.hslf.usermodel.HSLFSlide; import org.apache.poi.hslf.usermodel.HSLFSlideShow; - -import junit.framework.TestCase; +import org.junit.Test; /** * Tests that SlideAtom works properly - * - * @author Nick Burch (nick at torchbox dot com) */ -public final class TestSlideAtom extends TestCase { +public final class TestSlideAtom { // From a real file - private final byte[] data_a = new byte[] { 1, 0, 0xEF-256, 3, 0x18, 0, 0, 0, + private static final byte[] data_a = new byte[] { 1, 0, 0xEF-256, 3, 0x18, 0, 0, 0, 0, 0, 0, 0, 0x0F, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x80-256, 0, 1, 0, 0, 7, 0, 0x0C, 0x30 }; - public void testRecordType() { + @Test + public void testRecordType() { SlideAtom sa = new SlideAtom(data_a, 0, data_a.length); assertEquals(1007l, sa.getRecordType()); } + + @Test public void testFlags() { SlideAtom sa = new SlideAtom(data_a, 0, data_a.length); // First 12 bytes are a SSlideLayoutAtom, checked elsewhere // Check the IDs - assertEquals(0x80000000, sa.getMasterID()); + assertEquals(SlideAtom.USES_MASTER_SLIDE_ID, sa.getMasterID()); assertEquals(256, sa.getNotesID()); // Check the flags @@ -54,39 +61,37 @@ public final class TestSlideAtom extends TestCase { assertEquals(true, sa.getFollowMasterScheme()); assertEquals(true, sa.getFollowMasterBackground()); } - public void testSSlideLayoutAtom() { + + @Test + public void testSSlideLayoutAtom() { SlideAtom sa = new SlideAtom(data_a, 0, data_a.length); - SSlideLayoutAtom ssla = sa.getSSlideLayoutAtom(); + SlideAtomLayout ssla = sa.getSSlideLayoutAtom(); - assertEquals(0, ssla.getGeometryType()); + assertEquals(SlideLayoutType.TITLE_SLIDE, ssla.getGeometryType()); - // Should also check the placehold IDs at some point + // Should also check the placeholder IDs at some point } - public void testWrite() throws Exception { + @Test + public void testWrite() throws IOException { SlideAtom sa = new SlideAtom(data_a, 0, data_a.length); ByteArrayOutputStream baos = new ByteArrayOutputStream(); sa.writeOut(baos); - byte[] b = baos.toByteArray(); - - assertEquals(data_a.length, b.length); - for(int i=0; i tr : _slides.get(0).getTextParagraphs()) { - if (! tr.get(0).isDrawingBased()) str++; + if (! tr.get(0).isDrawingBased()) { + str++; + } } assertEquals(2, str); @@ -758,7 +767,7 @@ public final class TestBugs { public void bug47904() throws IOException { HSLFSlideShow ppt1 = new HSLFSlideShow(); HSLFSlideMaster sm = ppt1.getSlideMasters().get(0); - HSLFAutoShape as = (HSLFAutoShape)sm.getShapes().get(0); + HSLFAutoShape as = (HSLFAutoShape)sm.getPlaceholder(Placeholder.TITLE); HSLFTextParagraph tp = as.getTextParagraphs().get(0); HSLFTextRun tr = tp.getTextRuns().get(0); tr.setFontFamily("Tahoma"); @@ -766,8 +775,9 @@ public final class TestBugs { tr.setFontSize(44.); tr.setFontColor(Color.red); tp.setTextAlign(TextAlign.RIGHT); - ppt1.createSlide().addTitle().setText("foobaa"); - + HSLFTextBox tb = ppt1.createSlide().addTitle(); + tb.setText("foobaa"); + HSLFSlideShow ppt2 = HSLFTestDataSamples.writeOutAndReadBack(ppt1); ppt1.close(); @@ -877,7 +887,7 @@ public final class TestBugs { StringBuffer sb = new StringBuffer(); for (char c = iterator.first(); - c != AttributedCharacterIterator.DONE; + c != CharacterIterator.DONE; c = iterator.next()) { sb.append(c); attributes = iterator.getAttributes(); diff --git a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java index a52cee4129..baed0386d3 100644 --- a/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java +++ b/src/scratchpad/testcases/org/apache/poi/hslf/usermodel/TestCounts.java @@ -18,29 +18,23 @@ package org.apache.poi.hslf.usermodel; -import java.util.List; +import static org.junit.Assert.assertEquals; -import junit.framework.TestCase; +import java.io.IOException; +import java.util.List; -import org.apache.poi.POIDataSamples; +import org.apache.poi.hslf.HSLFTestDataSamples; +import org.junit.Test; /** * Tests that SlideShow returns the right number of Sheets and MetaSheets - * - * @author Nick Burch (nick at torchbox dot com) */ -public final class TestCounts extends TestCase { - // SlideShow primed on the test data - private final HSLFSlideShow ss; - - public TestCounts() throws Exception { - POIDataSamples slTests = POIDataSamples.getSlideShowInstance(); - HSLFSlideShowImpl hss = new HSLFSlideShowImpl(slTests.openResourceAsStream("basic_test_ppt_file.ppt")); - ss = new HSLFSlideShow(hss); - } - - public void testSheetsCount() { - List slides = ss.getSlides(); +public final class TestCounts { + @Test + public void testSheetsCount() throws IOException { + HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("basic_test_ppt_file.ppt"); + + List slides = ppt.getSlides(); // Two sheets - master sheet is separate assertEquals(2, slides.size()); @@ -55,10 +49,15 @@ public final class TestCounts extends TestCase { // These are slides 1+2 -> 256+257 assertEquals(256, slides.get(0)._getSheetNumber()); assertEquals(257, slides.get(1)._getSheetNumber()); + + ppt.close(); } - public void testNotesCount() { - List notes = ss.getNotes(); + @Test + public void testNotesCount() throws IOException { + HSLFSlideShow ppt = HSLFTestDataSamples.getSlideShow("basic_test_ppt_file.ppt"); + + List notes = ppt.getNotes(); // Two sheets -> two notes // Note: there are also notes on the slide master //assertEquals(3, notes.length); // When we do slide masters @@ -74,5 +73,7 @@ public final class TestCounts extends TestCase { // They happen to go between the two slides in Ref terms assertEquals(5, notes.get(0)._getSheetRefId()); assertEquals(7, notes.get(1)._getSheetRefId()); + + ppt.close(); } }