diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java index 21ab67124..67af3a179 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java @@ -65,6 +65,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Comparator; +import java.util.List; /** * Encapsulates logic to translate DrawingML objects to Java2D @@ -125,6 +126,8 @@ class RenderableShape { CTPathShadeProperties ps = gradFill.getPath(); if(ps.getPath() == STPathShadeType.CIRCLE){ paint = createRadialGradientPaint(gradFill, anchor, theme, phClr); + } else if (ps.getPath() == STPathShadeType.SHAPE){ + paint = toRadialGradientPaint(gradFill, anchor, theme, phClr); } } } @@ -165,7 +168,7 @@ class RenderableShape { return paint; } - private static Paint createLinearGradientPaint( + private Paint createLinearGradientPaint( Graphics2D graphics, CTGradientFillProperties gradFill, Rectangle2D anchor, XSLFTheme theme, CTSchemeColor phClr) { @@ -205,15 +208,16 @@ class RenderableShape { fractions[i] = stop.getPos() / 100000.f; } - AffineTransform grAt; - if(gradFill.getRotWithShape()) grAt = new AffineTransform(); - else { - // gradient fill is not rotated with the shape - try { - grAt = graphics.getTransform().createInverse(); - } catch (Exception e){ - // should not happen. - grAt = new AffineTransform(); + AffineTransform grAt = new AffineTransform(); + if(gradFill.isSetRotWithShape() || !gradFill.getRotWithShape()) { + double rotation = _shape.getRotation(); + if (rotation != 0.) { + double centerX = anchor.getX() + anchor.getWidth() / 2; + double centerY = anchor.getY() + anchor.getHeight() / 2; + + grAt.translate(centerX, centerY); + grAt.rotate(Math.toRadians(-rotation)); + grAt.translate(-centerX, -centerY); } } @@ -237,6 +241,30 @@ class RenderableShape { return paint; } + /** + * gradients with type=shape are enot supported by Java graphics. + * We approximate it with a radial gradient. + */ + private static Paint toRadialGradientPaint( + CTGradientFillProperties gradFill, Rectangle2D anchor, + XSLFTheme theme, CTSchemeColor phClr) { + + CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); + Arrays.sort(gs, new Comparator() { + public int compare(CTGradientStop o1, CTGradientStop o2) { + Integer pos1 = o1.getPos(); + Integer pos2 = o2.getPos(); + return pos1.compareTo(pos2); + } + }); + gs[1].setPos(50000); + + CTGradientFillProperties g = CTGradientFillProperties.Factory.newInstance(); + g.set(gradFill); + g.getGsLst().setGsArray(new CTGradientStop[]{gs[0], gs[1]}); + return createRadialGradientPaint(g, anchor, theme, phClr); + } + private static Paint createRadialGradientPaint( CTGradientFillProperties gradFill, Rectangle2D anchor, XSLFTheme theme, CTSchemeColor phClr) { @@ -498,7 +526,7 @@ class RenderableShape { if(shadow != null) for(Outline o : elems){ if(o.getPath().isFilled()){ if(fill != null) shadow.fill(graphics, o.getOutline()); - if(line != null) shadow.draw(graphics, o.getOutline()); + else if(line != null) shadow.draw(graphics, o.getOutline()); } } // then fill the shape interior diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java index 9de0c7493..9ffcee684 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XMLSlideShow.java @@ -32,7 +32,9 @@ import org.apache.poi.util.PackageHelper; import org.apache.poi.util.Units; import org.apache.poi.xslf.XSLFSlideShow; import org.apache.xmlbeans.XmlException; +import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.XmlOptions; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.officeDocument.x2006.relationships.STRelationshipId; import org.openxmlformats.schemas.presentationml.x2006.main.CTPresentation; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideIdList; @@ -355,4 +357,15 @@ public class XMLSlideShow extends POIXMLDocument { return _tableStyles; } + CTTextParagraphProperties getDefaultParagraphStyle(int level) { + XmlObject[] o = _presentation.selectPath( + "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + + ".//p:defaultTextStyle/a:lvl" +(level+1)+ "pPr"); + if(o.length == 1){ + return (CTTextParagraphProperties)o[0]; + } + return null; + } + } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java index 7d8cb061e..0c60ec604 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFBackground.java @@ -20,7 +20,9 @@ package org.apache.poi.xslf.usermodel; import org.apache.xmlbeans.XmlObject; import org.openxmlformats.schemas.drawingml.x2006.main.CTBackgroundFillStyleList; import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; +import org.openxmlformats.schemas.drawingml.x2006.main.CTShapeProperties; import org.openxmlformats.schemas.drawingml.x2006.main.CTStyleMatrixReference; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; import java.awt.Color; @@ -91,4 +93,15 @@ public class XSLFBackground extends XSLFSimpleShape { } return null; } + + /** + * background does not have a associated transform. + * we return a dummy transform object to prevent exceptions in inherited methods. + * + * @return dummy CTTransform2D bean + */ + @Override + CTTransform2D getXfrm() { + return CTTransform2D.Factory.newInstance(); + } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java index a8bbe8b6a..3c3d49bfc 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFImageRendener.java @@ -24,6 +24,8 @@ import org.apache.poi.util.Beta; import javax.imageio.ImageIO; import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.geom.AffineTransform; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -78,9 +80,19 @@ public class XSLFImageRendener { Rectangle2D anchor) { try { BufferedImage img = ImageIO.read(data.getPackagePart().getInputStream()); - graphics.drawImage(img, - (int) anchor.getX(), (int) anchor.getY(), - (int) anchor.getWidth(), (int) anchor.getHeight(), null); + Number groupScale = (Number)graphics.getRenderingHint(XSLFRenderingHint.GROUP_SCALE); + if(groupScale != null) { + double sx = anchor.getWidth()/img.getWidth(); + double sy = anchor.getHeight()/img.getHeight(); + double tx = anchor.getX(); + double ty = anchor.getY(); + AffineTransform at = new AffineTransform(sx, 0, 0, sy, tx, ty) ; + graphics.drawRenderedImage(img, at); + } else { + graphics.drawImage(img, + (int) anchor.getX(), (int) anchor.getY(), + (int) anchor.getWidth(), (int) anchor.getHeight(), null); + } return true; } catch (Exception e) { return false; diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java index 7c2128e8f..42dc360cd 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java @@ -44,7 +44,11 @@ public class XSLFShadow extends XSLFSimpleShape { public void fill(Graphics2D graphics, Shape outline) { - double angle = getAngle(); + double shapeRotation = _parent.getRotation(); + if(_parent.getFlipVertical()){ + shapeRotation += 180; + } + double angle = getAngle() - shapeRotation; double dist = getDistance(); double dx = dist * Math.cos(Math.toRadians(angle)); double dy = dist * Math.sin(Math.toRadians(angle)); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java index 8a74e2d0f..fb8da22e1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java @@ -413,6 +413,14 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable fetcher = new PropertyFetcher() { public boolean fetch(XSLFSimpleShape shape) { CTShapeProperties pr = shape.getSpPr(); @@ -561,7 +561,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { * * The following order of inheritance is assumed: *

- * slide <-- slideLayout <-- slideMaster <-- default styles in the slideMaster + * slide <-- slideLayout <-- slideMaster *

* * @param visitor the object that collects the desired property @@ -571,24 +571,21 @@ public abstract class XSLFSimpleShape extends XSLFShape { boolean ok = visitor.fetch(this); XSLFSimpleShape masterShape; - XSLFSheet layout = getSheet().getMasterSheet(); - if (layout != null) { + XSLFSheet masterSheet = getSheet().getMasterSheet(); + CTPlaceholder ph = getCTPlaceholder(); + + if (masterSheet != null && ph != null) { if (!ok) { - // first try to fetch from the slide layout - CTPlaceholder ph = getCTPlaceholder(); - if (ph != null) { - masterShape = layout.getPlaceholder(ph); - if (masterShape != null) { - ok = visitor.fetch(masterShape); - } + masterShape = masterSheet.getPlaceholder(ph); + if (masterShape != null) { + ok = visitor.fetch(masterShape); } } // try slide master - if (!ok) { + if (!ok ) { int textType; - CTPlaceholder ph = getCTPlaceholder(); - if (ph == null || !ph.isSetType()) textType = STPlaceholderType.INT_BODY; + if ( !ph.isSetType()) textType = STPlaceholderType.INT_BODY; else { switch (ph.getType().intValue()) { case STPlaceholderType.INT_TITLE: @@ -605,7 +602,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { break; } } - XSLFSheet master = layout.getMasterSheet(); + XSLFSheet master = masterSheet.getMasterSheet(); if (master != null) { masterShape = master.getPlaceholderByType(textType); if (masterShape != null) { 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 aee9ef002..91566cb09 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlide.java @@ -184,23 +184,14 @@ public final class XSLFSlide extends XSLFSheet { * * @return the information about background appearance of this slide */ + @Override public XSLFBackground getBackground() { - - - if(_slide.getCSld().isSetBg()) { - return new XSLFBackground(_slide.getCSld().getBg(), this); + CTBackground bg = _slide.getCSld().getBg(); + if(bg != null) { + return new XSLFBackground(bg, this); + } else { + return getMasterSheet().getBackground(); } - - XSLFSlideLayout layout = getMasterSheet(); - if(layout.getXmlObject().getCSld().isSetBg()) { - return new XSLFBackground(layout.getXmlObject().getCSld().getBg(), this); - } - - XSLFSlideMaster master = layout.getMasterSheet(); - if(master.getXmlObject().getCSld().isSetBg()) { - return new XSLFBackground(master.getXmlObject().getCSld().getBg(), this); - } - return null; } /** 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 4a505972d..f8cd23ccd 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideLayout.java @@ -22,6 +22,7 @@ import org.apache.poi.openxml4j.opc.PackageRelationship; import org.apache.poi.util.Beta; import org.apache.poi.util.Internal; import org.apache.xmlbeans.XmlException; +import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideLayout; import org.openxmlformats.schemas.presentationml.x2006.main.SldLayoutDocument; @@ -115,6 +116,17 @@ public class XSLFSlideLayout extends XSLFSheet { return true; } + + @Override + public XSLFBackground getBackground() { + CTBackground bg = _layout.getCSld().getBg(); + if(bg != null) { + return new XSLFBackground(bg, this); + } else { + return getMasterSheet().getBackground(); + } + } + /** * Copy placeholders from this layout to the destination slide * diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java index e73ee7a4c..d4383c4d9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSlideMaster.java @@ -23,6 +23,7 @@ import org.apache.poi.util.Beta; import org.apache.xmlbeans.XmlException; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextListStyle; +import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMaster; import org.openxmlformats.schemas.presentationml.x2006.main.CTSlideMasterTextStyles; @@ -167,4 +168,14 @@ import java.util.Map; return true; } + @Override + public XSLFBackground getBackground() { + CTBackground bg = _slide.getCSld().getBg(); + if(bg != null) { + return new XSLFBackground(bg, this); + } else { + return null; + } + } + } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index dcca92fbf..67a5013c8 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -570,7 +570,7 @@ public class XSLFTextParagraph implements Iterable{ /** * - * @return the text level of this paragraph. Default is 0. + * @return the text level of this paragraph (0-based). Default is 0. */ public int getLevel(){ CTTextParagraphProperties pr = _p.getPPr(); @@ -823,7 +823,7 @@ public class XSLFTextParagraph implements Iterable{ AttributedString at = getAttributedString(graphics); AttributedCharacterIterator it = at.getIterator(); - LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()); + LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext()) ; for (;;) { int startIndex = measurer.getPosition(); @@ -898,29 +898,27 @@ public class XSLFTextParagraph implements Iterable{ return _lines; } - CTTextParagraphProperties getDefaultStyle(){ + CTTextParagraphProperties getDefaultMasterStyle(){ CTPlaceholder ph = _shape.getCTPlaceholder(); String defaultStyleSelector; - if(ph == null) defaultStyleSelector = "otherStyle"; - else { - switch(ph.getType().intValue()){ - case STPlaceholderType.INT_TITLE: - case STPlaceholderType.INT_CTR_TITLE: - defaultStyleSelector = "titleStyle"; - break; - case STPlaceholderType.INT_FTR: - case STPlaceholderType.INT_SLD_NUM: - case STPlaceholderType.INT_DT: - defaultStyleSelector = "otherStyle"; - break; - default: - defaultStyleSelector = "bodyStyle"; - break; - } + switch(ph.getType().intValue()){ + case STPlaceholderType.INT_TITLE: + case STPlaceholderType.INT_CTR_TITLE: + defaultStyleSelector = "titleStyle"; + break; + case STPlaceholderType.INT_FTR: + case STPlaceholderType.INT_SLD_NUM: + case STPlaceholderType.INT_DT: + defaultStyleSelector = "otherStyle"; + break; + default: + defaultStyleSelector = "bodyStyle"; + break; } int level = getLevel(); + // wind up and find the root master sheet which must be slide master XSLFSheet masterSheet = _shape.getSheet(); while (masterSheet.getMasterSheet() != null){ masterSheet = masterSheet.getMasterSheet(); @@ -946,9 +944,18 @@ public class XSLFTextParagraph implements Iterable{ if(!ok) { XSLFTextShape shape = getParentShape(); ok = shape.fetchShapeProperty(visitor); - if(!ok) { - CTTextParagraphProperties defaultProps = getDefaultStyle(); - if(defaultProps != null) ok = visitor.fetch(defaultProps); + if(!ok){ + CTPlaceholder ph = shape.getCTPlaceholder(); + if(ph == null){ + // if it is a plain text box then take defaults from presentation.xml + XMLSlideShow ppt = getParentShape().getSheet().getSlideShow(); + CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(getLevel()); + if(themeProps != null) ok = visitor.fetch(themeProps); + } else { + // defaults for placeholders are defined in the slide master + CTTextParagraphProperties defaultProps = getDefaultMasterStyle(); + if(defaultProps != null) ok = visitor.fetch(defaultProps); + } } } 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 f67fde8ae..cb7e5fc12 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java @@ -30,6 +30,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties import org.openxmlformats.schemas.drawingml.x2006.main.STTextStrikeType; import org.openxmlformats.schemas.drawingml.x2006.main.STTextUnderlineType; import org.openxmlformats.schemas.drawingml.x2006.main.STSchemeColorVal; +import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import java.awt.Color; import java.awt.font.FontRenderContext; @@ -467,11 +468,23 @@ public class XSLFTextRun { if(!ok) { XSLFTextShape shape = _p.getParentShape(); ok = shape.fetchShapeProperty(fetcher); - if(!ok) { - CTTextParagraphProperties defaultProps = _p.getDefaultStyle(); - if(defaultProps != null) { - fetcher.isFetchingFromMaster = true; - ok = fetcher.fetch(defaultProps); + if(!ok){ + CTPlaceholder ph = shape.getCTPlaceholder(); + if(ph == null){ + // if it is a plain text box then take defaults from presentation.xml + XMLSlideShow ppt = shape.getSheet().getSlideShow(); + CTTextParagraphProperties themeProps = ppt.getDefaultParagraphStyle(_p.getLevel()); + if(themeProps != null) { + fetcher.isFetchingFromMaster = true; + ok = fetcher.fetch(themeProps); + } + } else { + // defaults for placeholders are defined in the slide master + CTTextParagraphProperties defaultProps = _p.getDefaultMasterStyle(); + if(defaultProps != null) { + fetcher.isFetchingFromMaster = true; + ok = fetcher.fetch(defaultProps); + } } } } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java index 2aabbd2b1..c54d74e1f 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java @@ -51,6 +51,11 @@ import java.util.List; public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable{ private final List _paragraphs; + /** + * whether the text was broken into lines. + */ + private boolean _isTextBroken; + /*package*/ XSLFTextShape(XmlObject shape, XSLFSheet sheet) { super(shape, sheet); @@ -441,17 +446,23 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable< /** * Compute the cumulative height occupied by the text */ - private double getTextHeight(){ + public double getTextHeight(){ // dry-run in a 1x1 image and return the vertical advance BufferedImage img = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB); - return drawParagraphs(img.createGraphics(), 0, 0); + Graphics2D graphics = img.createGraphics(); + breakText(graphics); + return drawParagraphs(graphics, 0, 0); } /** * break the contained text into lines */ private void breakText(Graphics2D graphics){ - for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics); + if(!_isTextBroken) { + for(XSLFTextParagraph p : _paragraphs) p.breakText(graphics); + + _isTextBroken = true; + } } @Override 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 b996dd5fb..41f2109fc 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTheme.java @@ -29,6 +29,7 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorMapping; import org.openxmlformats.schemas.drawingml.x2006.main.CTColorScheme; import org.openxmlformats.schemas.drawingml.x2006.main.CTOfficeStyleSheet; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; import org.openxmlformats.schemas.drawingml.x2006.main.ThemeDocument; import javax.xml.namespace.QName; @@ -150,4 +151,17 @@ public class XSLFTheme extends POIXMLDocumentPart { public String getMinorFont(){ return _theme.getThemeElements().getFontScheme().getMinorFont().getLatin().getTypeface(); } + + + CTTextParagraphProperties getDefaultParagraphStyle(){ + XmlObject[] o = _theme.selectPath( + "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + + "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' " + + ".//a:objectDefaults/a:spDef/a:lstStyle/a:defPPr"); + if(o.length == 1){ + return (CTTextParagraphProperties)o[0]; + } + return null; + } + } diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java index 06b5f1b9a..4f921ac05 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextBox.java @@ -17,6 +17,7 @@ package org.apache.poi.xslf.usermodel; import junit.framework.TestCase; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; /** * @author Yegor Kozlov @@ -33,5 +34,33 @@ public class TestXSLFTextBox extends TestCase { assertEquals(Placeholder.TITLE, shape.getTextType()); shape.setPlaceholder(null); assertNull(shape.getTextType()); + shape.setText("Apache POI"); + } + + /** + * text box inherits default text proeprties from presentation.xml + */ + public void testDefaultTextStyle() { + XMLSlideShow ppt = new XMLSlideShow(); + XSLFSlide slide = ppt.createSlide(); + + // default character properties for paragraphs with level=1 + CTTextCharacterProperties pPr = ppt.getCTPresentation().getDefaultTextStyle().getLvl1PPr().getDefRPr(); + + XSLFTextBox shape = slide.createTextBox(); + shape.setText("Apache POI"); + assertEquals(1, shape.getTextParagraphs().size()); + assertEquals(1, shape.getTextParagraphs().get(0).getTextRuns().size()); + + XSLFTextRun r = shape.getTextParagraphs().get(0).getTextRuns().get(0); + + assertEquals(1800, pPr.getSz()); + assertEquals(18.0, r.getFontSize()); + assertEquals("Calibri", r.getFontFamily()); + + pPr.setSz(900); + pPr.getLatin().setTypeface("Arial"); + assertEquals(9.0, r.getFontSize()); + assertEquals("Arial", r.getFontFamily()); } } \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java index 2974dd69f..62d3c5239 100755 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java @@ -3,6 +3,7 @@ package org.apache.poi.xslf.usermodel; import junit.framework.TestCase; import org.apache.poi.util.POILogFactory; import org.apache.poi.util.POILogger; +import org.apache.poi.xslf.XSLFTestDataSamples; import java.awt.*; import java.awt.geom.Rectangle2D; @@ -105,22 +106,13 @@ public class TestXSLFTextParagraph extends TestCase { /** * test breaking test into lines. - * This test is platform-dependent and will run only if the test fonr (Arial) - * is present in local graphics environment + * This test requires that the Arial font is available and will run only on windows */ public void testBreakLines(){ - GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment(); - String testFont = "Arial"; - boolean testFontAvailable = false; - for(String family : env.getAvailableFontFamilyNames()) { - if(family.equals(testFont)) { - testFontAvailable = true; - break; - } - } - - if(!testFontAvailable) { - _logger.log(POILogger.WARN, "the Arial font family is not available in local graphics environment"); + String os = System.getProperty("os.name"); + if(os == null || !os.contains("Windows")) { + _logger.log(POILogger.WARN, "Skipping testBreakLines(), it is executed only on Windows machines"); + return; } XMLSlideShow ppt = new XMLSlideShow(); @@ -198,4 +190,20 @@ public class TestXSLFTextParagraph extends TestCase { assertTrue(lines.get(0).getHeight() > lines.get(1).getHeight()*2); } + + public void testThemeInheritance(){ + XMLSlideShow ppt = XSLFTestDataSamples.openSampleDocument("prProps.pptx"); + XSLFShape[] shapes = ppt.getSlides()[0].getShapes(); + XSLFTextShape sh1 = (XSLFTextShape)shapes[0]; + assertEquals("Apache", sh1.getText()); + assertEquals(TextAlign.CENTER, sh1.getTextParagraphs().get(0).getTextAlign()); + XSLFTextShape sh2 = (XSLFTextShape)shapes[1]; + assertEquals("Software", sh2.getText()); + assertEquals(TextAlign.CENTER, sh2.getTextParagraphs().get(0).getTextAlign()); + XSLFTextShape sh3 = (XSLFTextShape)shapes[2]; + assertEquals("Foundation", sh3.getText()); + assertEquals(TextAlign.CENTER, sh3.getTextParagraphs().get(0).getTextAlign()); + + + } } 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 07e64de1e..79063a308 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextShape.java @@ -19,6 +19,9 @@ package org.apache.poi.xslf.usermodel; import junit.framework.TestCase; import org.apache.poi.xslf.XSLFTestDataSamples; import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBodyProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharacterProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; +import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; @@ -606,4 +609,288 @@ public class TestXSLFTextShape extends TestCase { XSLFTextShape sldNum = (XSLFTextShape)slide.getPlaceholderByType(STPlaceholderType.INT_SLD_NUM); assertEquals("10", sldNum.getText()); } + + + public void testTitleStyles(){ + XMLSlideShow ppt = new XMLSlideShow(); + + XSLFSlideMaster master = ppt.getSlideMasters()[0]; + XSLFTheme theme = master.getTheme(); + XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE); + XSLFSlide slide = ppt.createSlide(layout) ; + assertSame(layout, slide.getSlideLayout()); + assertSame(master, slide.getSlideMaster()); + + XSLFTextShape titleShape = (XSLFTextShape)slide.getPlaceholder(0); + titleShape.setText("Apache POI"); + XSLFTextParagraph paragraph = titleShape.getTextParagraphs().get(0); + XSLFTextRun textRun = paragraph.getTextRuns().get(0); + + // level 1 : default title style on the master slide + // /p:sldMaster/p:txStyles/p:titleStyle/a:lvl1pPr + CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getTitleStyle().getLvl1PPr(); + CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); + assertEquals(4400, lv1CPr.getSz()); + assertEquals(44.0, textRun.getFontSize()); + assertEquals("+mj-lt", lv1CPr.getLatin().getTypeface()); + assertEquals("Calibri", theme.getMajorFont()); + assertEquals("Calibri", textRun.getFontFamily()); + lv1CPr.setSz(3200); + assertEquals(32.0, textRun.getFontSize()); + lv1CPr.getLatin().setTypeface("Arial"); + assertEquals("Arial", textRun.getFontFamily()); + assertEquals(STTextAlignType.CTR, lv1PPr.getAlgn()); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); + + // level 2: title placeholder on the master slide + // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="title"] + XSLFTextShape tx2 = master.getPlaceholder(0); + CTTextParagraphProperties lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv2CPr = lv2PPr.addNewDefRPr(); + lv2CPr.setSz(3300); + assertEquals(33.0, textRun.getFontSize()); + lv2CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", textRun.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, paragraph.getTextAlign()); + + + // level 3: title placeholder on the slide layout + // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] + XSLFTextShape tx3 = layout.getPlaceholder(0); + CTTextParagraphProperties lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv3CPr = lv3PPr.addNewDefRPr(); + lv3CPr.setSz(3400); + assertEquals(34.0, textRun.getFontSize()); + lv3CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", textRun.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + + // level 4: default text properties in the shape itself + // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr + CTTextParagraphProperties lv4PPr = titleShape.getTextBody(true).getLstStyle().addNewLvl1PPr(); + CTTextCharacterProperties lv4CPr = lv4PPr.addNewDefRPr(); + lv4CPr.setSz(3500); + assertEquals(35.0, textRun.getFontSize()); + lv4CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", textRun.getFontFamily()); + lv4PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, paragraph.getTextAlign()); + + // level 5: text properties are defined in the text run + CTTextParagraphProperties lv5PPr = paragraph.getXmlObject().addNewPPr(); + CTTextCharacterProperties lv5CPr = textRun.getXmlObject().getRPr(); + lv5CPr.setSz(3600); + assertEquals(36.0, textRun.getFontSize()); + lv5CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", textRun.getFontFamily()); + lv5PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, paragraph.getTextAlign()); + } + + public void testBodyStyles(){ + XMLSlideShow ppt = new XMLSlideShow(); + + XSLFSlideMaster master = ppt.getSlideMasters()[0]; + XSLFTheme theme = master.getTheme(); + XSLFSlideLayout layout = master.getLayout(SlideLayout.TITLE_AND_CONTENT); + XSLFSlide slide = ppt.createSlide(layout) ; + assertSame(layout, slide.getSlideLayout()); + assertSame(master, slide.getSlideMaster()); + + XSLFTextShape tx1 = (XSLFTextShape)slide.getPlaceholder(1); + tx1.clearText(); + + XSLFTextParagraph p1 = tx1.addNewTextParagraph(); + assertEquals(0, p1.getLevel()); + XSLFTextRun r1 = p1.addNewTextRun(); + r1.setText("Apache POI"); + + XSLFTextParagraph p2 = tx1.addNewTextParagraph(); + p2.setLevel(1); + assertEquals(1, p2.getLevel()); + XSLFTextRun r2 = p2.addNewTextRun(); + r2.setText("HSLF"); + + XSLFTextParagraph p3 = tx1.addNewTextParagraph(); + p3.setLevel(2); + assertEquals(2, p3.getLevel()); + XSLFTextRun r3 = p3.addNewTextRun(); + r3.setText("XSLF"); + + // level 1 : default title style on the master slide + // /p:sldMaster/p:txStyles/p:bodyStyle/a:lvl1pPr + CTTextParagraphProperties lv1PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl1PPr(); + CTTextCharacterProperties lv1CPr = lv1PPr.getDefRPr(); + CTTextParagraphProperties lv2PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl2PPr(); + CTTextCharacterProperties lv2CPr = lv2PPr.getDefRPr(); + CTTextParagraphProperties lv3PPr = master.getXmlObject().getTxStyles().getBodyStyle().getLvl3PPr(); + CTTextCharacterProperties lv3CPr = lv3PPr.getDefRPr(); + // lv1 + assertEquals(3200, lv1CPr.getSz()); + assertEquals(32.0, r1.getFontSize()); + assertEquals("+mn-lt", lv1CPr.getLatin().getTypeface()); + assertEquals("Calibri", theme.getMinorFont()); + assertEquals("Calibri", r1.getFontFamily()); + lv1CPr.setSz(3300); + assertEquals(33.0, r1.getFontSize()); + lv1CPr.getLatin().setTypeface("Arial"); + assertEquals("Arial", r1.getFontFamily()); + assertEquals(STTextAlignType.L, lv1PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + lv1PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p1.getTextAlign()); + //lv2 + assertEquals(2800, lv2CPr.getSz()); + assertEquals(28.0, r2.getFontSize()); + lv2CPr.setSz(3300); + assertEquals(33.0, r2.getFontSize()); + lv2CPr.getLatin().setTypeface("Times"); + assertEquals("Times", r2.getFontFamily()); + assertEquals(STTextAlignType.L, lv2PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + lv2PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p2.getTextAlign()); + //lv3 + assertEquals(2400, lv3CPr.getSz()); + assertEquals(24.0, r3.getFontSize()); + lv3CPr.setSz(2500); + assertEquals(25.0, r3.getFontSize()); + lv3CPr.getLatin().setTypeface("Courier New"); + assertEquals("Courier New", r3.getFontFamily()); + assertEquals(STTextAlignType.L, lv3PPr.getAlgn()); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + lv3PPr.setAlgn(STTextAlignType.R); + assertEquals(TextAlign.RIGHT, p3.getTextAlign()); + + + // level 2: body placeholder on the master slide + // /p:sldMaster/p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="body"] + XSLFTextShape tx2 = master.getPlaceholder(1); + assertEquals(Placeholder.BODY, tx2.getTextType()); + + lv1PPr = tx2.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx2.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx2.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3300); + assertEquals(33.0, r1.getFontSize()); + lv1CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + + lv2CPr.setSz(3300); + assertEquals(33.0, r2.getFontSize()); + lv2CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + + lv3CPr.setSz(3300); + assertEquals(33.0, r3.getFontSize()); + lv3CPr.addNewLatin().setTypeface("Times"); + assertEquals("Times", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + + // level 3: body placeholder on the slide layout + // /p:sldLayout /p:cSld/p:spTree/p:sp/p:nvPr/p:ph[@type="ctrTitle"] + XSLFTextShape tx3 = layout.getPlaceholder(1); + assertEquals(Placeholder.BODY, tx2.getTextType()); + lv1PPr = tx3.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx3.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx3.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3400); + assertEquals(34.0, r1.getFontSize()); + lv1CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p1.getTextAlign()); + + lv2CPr.setSz(3400); + assertEquals(34.0, r2.getFontSize()); + lv2CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p2.getTextAlign()); + + lv3CPr.setSz(3400); + assertEquals(34.0, r3.getFontSize()); + lv3CPr.addNewLatin().setTypeface("Courier New"); + assertEquals("Courier New", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p3.getTextAlign()); + + // level 4: default text properties in the shape itself + // ./p:sp/p:txBody/a:lstStyle/a:lvl1pPr + lv1PPr = tx1.getTextBody(true).getLstStyle().addNewLvl1PPr(); + lv1CPr = lv1PPr.addNewDefRPr(); + lv2PPr = tx1.getTextBody(true).getLstStyle().addNewLvl2PPr(); + lv2CPr = lv2PPr.addNewDefRPr(); + lv3PPr = tx1.getTextBody(true).getLstStyle().addNewLvl3PPr(); + lv3CPr = lv3PPr.addNewDefRPr(); + + lv1CPr.setSz(3500); + assertEquals(35.0, r1.getFontSize()); + lv1CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p1.getTextAlign()); + + lv2CPr.setSz(3500); + assertEquals(35.0, r2.getFontSize()); + lv2CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p2.getTextAlign()); + + lv3CPr.setSz(3500); + assertEquals(35.0, r3.getFontSize()); + lv3CPr.addNewLatin().setTypeface("Arial"); + assertEquals("Arial", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.L); + assertEquals(TextAlign.LEFT, p3.getTextAlign()); + + // level 5: text properties are defined in the text run + lv1PPr = p1.getXmlObject().isSetPPr() ? p1.getXmlObject().getPPr() : p1.getXmlObject().addNewPPr(); + lv1CPr = r1.getXmlObject().getRPr(); + lv2PPr = p2.getXmlObject().isSetPPr() ? p2.getXmlObject().getPPr() : p2.getXmlObject().addNewPPr(); + lv2CPr = r2.getXmlObject().getRPr(); + lv3PPr = p3.getXmlObject().isSetPPr() ? p3.getXmlObject().getPPr() : p3.getXmlObject().addNewPPr(); + lv3CPr = r3.getXmlObject().getRPr(); + + lv1CPr.setSz(3600); + assertEquals(36.0, r1.getFontSize()); + lv1CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r1.getFontFamily()); + lv1PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p1.getTextAlign()); + + lv2CPr.setSz(3600); + assertEquals(36.0, r2.getFontSize()); + lv2CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r2.getFontFamily()); + lv2PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p2.getTextAlign()); + + lv3CPr.setSz(3600); + assertEquals(36.0, r3.getFontSize()); + lv3CPr.addNewLatin().setTypeface("Calibri"); + assertEquals("Calibri", r3.getFontFamily()); + lv3PPr.setAlgn(STTextAlignType.CTR); + assertEquals(TextAlign.CENTER, p3.getTextAlign()); + + } + } \ No newline at end of file diff --git a/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx b/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx index eea1e064e..ababa9379 100755 Binary files a/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx and b/src/resources/ooxml/org/apache/poi/xslf/usermodel/empty.pptx differ diff --git a/test-data/slideshow/prProps.pptx b/test-data/slideshow/prProps.pptx new file mode 100644 index 000000000..2829fcaec Binary files /dev/null and b/test-data/slideshow/prProps.pptx differ