diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/LineCap.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/LineCap.java
index f2bfba99f..55b4c8492 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/LineCap.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/LineCap.java
@@ -17,11 +17,13 @@
package org.apache.poi.xslf.usermodel;
/**
+ *
+ *
* @author Yegor Kozlov
*/
public enum LineCap {
/**
- * Rounded ends - the default
+ * Rounded ends
*/
ROUND,
/**
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 bbd0de9ea..82a29756a 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/RenderableShape.java
@@ -469,10 +469,19 @@ class RenderableShape {
// first fill
Paint fill = getFillPaint(graphics);
+ Paint line = getLinePaint(graphics);
+ applyStroke(graphics); // the stroke applies both to the shadow and the shape
+
+ // first paint the shadow
+ 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());
+ }
+ }
+ // then fill the shape interior
if(fill != null) for(Outline o : elems){
if(o.getPath().isFilled()){
- if(shadow != null) shadow.fill(graphics, o.getOutline());
-
graphics.setPaint(fill);
graphics.fill(o.getOutline());
}
@@ -482,13 +491,8 @@ class RenderableShape {
_shape.drawContent(graphics);
// then stroke the shape outline
- Paint line = getLinePaint(graphics);
if(line != null) for(Outline o : elems){
if(o.getPath().isStroked()){
- applyStroke(graphics); // the stroke applies both to the shadow and the shape
-
- if(shadow != null) shadow.draw(graphics, o.getOutline());
-
graphics.setPaint(line);
graphics.draw(o.getOutline());
}
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java
index 647b414c5..b7f1f3d38 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFConnectorShape.java
@@ -331,4 +331,12 @@ public class XSLFConnectorShape extends XSLFSimpleShape {
return lst;
}
+ /**
+ * YK: dashing of lines is suppressed for now.
+ * @return
+ */
+ @Override
+ public XSLFShadow getShadow() {
+ return null;
+ }
}
\ No newline at end of file
diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
index 445c16e9b..1b5bbea2e 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFPictureShape.java
@@ -22,6 +22,7 @@ package org.apache.poi.xslf.usermodel;
import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
+import org.apache.poi.openxml4j.opc.TargetMode;
import org.apache.poi.util.Beta;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;
@@ -97,8 +98,7 @@ public class XSLFPictureShape extends XSLFSimpleShape {
public XSLFPictureData getPictureData() {
if(_data == null){
- CTPicture ct = (CTPicture)getXmlObject();
- String blipId = ct.getBlipFill().getBlip().getEmbed();
+ String blipId = getBlipId();
PackagePart p = getSheet().getPackagePart();
PackageRelationship rel = p.getRelationship(blipId);
@@ -115,6 +115,11 @@ public class XSLFPictureShape extends XSLFSimpleShape {
return _data;
}
+ private String getBlipId(){
+ CTPicture ct = (CTPicture)getXmlObject();
+ return ct.getBlipFill().getBlip().getEmbed();
+ }
+
@Override
public void drawContent(Graphics2D graphics) {
@@ -126,4 +131,19 @@ public class XSLFPictureShape extends XSLFSimpleShape {
renderer.drawImage(graphics, data, getAnchor());
}
+
+
+ @Override
+ void copy(XSLFShape sh){
+ super.copy(sh);
+
+ XSLFPictureShape p = (XSLFPictureShape)sh;
+ String blipId = p.getBlipId();
+ String relId = getSheet().importBlip(blipId, p.getSheet().getPackagePart());
+
+ CTPicture ct = (CTPicture)getXmlObject();
+ CTBlip blip = ct.getBlipFill().getBlip();
+ blip.setEmbed(relId);
+
+ }
}
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 70d42ce5c..7c2128e8f 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShadow.java
@@ -32,6 +32,7 @@ import java.awt.geom.Rectangle2D;
* @author Yegor Kozlov
*/
public class XSLFShadow extends XSLFSimpleShape {
+
private XSLFSimpleShape _parent;
/* package */XSLFShadow(CTOuterShadowEffect shape, XSLFSimpleShape parentShape) {
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 9ff619257..00191df6f 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java
@@ -20,6 +20,7 @@
package org.apache.poi.xslf.usermodel;
import org.apache.poi.util.Beta;
+import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlObject;
import java.awt.Graphics2D;
@@ -34,27 +35,23 @@ import java.awt.geom.Rectangle2D;
public abstract class XSLFShape {
/**
- *
* @return the position of this shape within the drawing canvas.
- * The coordinates are expressed in points
+ * The coordinates are expressed in points
*/
public abstract Rectangle2D getAnchor();
/**
- *
* @param anchor the position of this shape within the drawing canvas.
- * The coordinates are expressed in points
+ * The coordinates are expressed in points
*/
public abstract void setAnchor(Rectangle2D anchor);
/**
- *
* @return the xml bean holding this shape's data
*/
public abstract XmlObject getXmlObject();
/**
- *
* @return human-readable name of this shape, e.g. "Rectange 3"
*/
public abstract String getShapeName();
@@ -64,8 +61,8 @@ public abstract class XSLFShape {
* This ID may be used to assist in uniquely identifying this object so that it can
* be referred to by other parts of the document.
*
- * If multiple objects within the same document share the same id attribute value,
- * then the document shall be considered non-conformant.
+ * If multiple objects within the same document share the same id attribute value,
+ * then the document shall be considered non-conformant.
*
*
* @return unique id of this shape
@@ -82,7 +79,7 @@ public abstract class XSLFShape {
* @param theta the rotation angle in degrees.
*/
public abstract void setRotation(double theta);
-
+
/**
* Rotation angle in degrees
*
@@ -105,7 +102,7 @@ public abstract class XSLFShape {
* @param flip whether the shape is vertically flipped
*/
public abstract void setFlipVertical(boolean flip);
-
+
/**
* Whether the shape is horizontally flipped
*
@@ -132,15 +129,15 @@ public abstract class XSLFShape {
*
* @param graphics the graphics whos transform matrix will be modified
*/
- protected void applyTransform(Graphics2D graphics){
+ protected void applyTransform(Graphics2D graphics) {
Rectangle2D anchor = getAnchor();
// rotation
double rotation = getRotation();
- if(rotation != 0.) {
- // PowerPoint rotates shapes relative to the geometric center
- double centerX = anchor.getX() + anchor.getWidth()/2;
- double centerY = anchor.getY() + anchor.getHeight()/2;
+ if (rotation != 0.) {
+ // PowerPoint rotates shapes relative to the geometric center
+ double centerX = anchor.getX() + anchor.getWidth() / 2;
+ double centerY = anchor.getY() + anchor.getHeight() / 2;
graphics.translate(centerX, centerY);
graphics.rotate(Math.toRadians(rotation));
@@ -148,18 +145,34 @@ public abstract class XSLFShape {
}
//flip horizontal
- if(getFlipHorizontal()){
+ if (getFlipHorizontal()) {
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());
graphics.scale(-1, 1);
- graphics.translate(-anchor.getX() , -anchor.getY());
+ graphics.translate(-anchor.getX(), -anchor.getY());
}
//flip vertical
- if(getFlipVertical()){
+ if (getFlipVertical()) {
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());
graphics.scale(1, -1);
graphics.translate(-anchor.getX(), -anchor.getY());
}
}
+ /**
+ * Set the contents of this shape to be a copy of the source shape.
+ * This method is called recursively for each shape when merging slides
+ *
+ * @param sh the source shape
+ * @see org.apache.poi.xslf.usermodel.XSLFSlide#importContent(XSLFSheet)
+ */
+ @Internal
+ void copy(XSLFShape sh) {
+ if (!getClass().isInstance(sh)) {
+ throw new IllegalArgumentException(
+ "Can't copy " + sh.getClass().getSimpleName() + " into " + getClass().getSimpleName());
+ }
+
+ setAnchor(sh.getAnchor());
+ }
}
\ No newline at end of file
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 964df2df9..a5baed531 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSheet.java
@@ -17,9 +17,11 @@
package org.apache.poi.xslf.usermodel;
import org.apache.poi.POIXMLDocumentPart;
+import org.apache.poi.POIXMLException;
import org.apache.poi.openxml4j.opc.PackagePart;
import org.apache.poi.openxml4j.opc.PackageRelationship;
import org.apache.poi.openxml4j.opc.TargetMode;
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;
import org.apache.poi.util.Beta;
import org.apache.poi.util.Internal;
import org.apache.xmlbeans.XmlObject;
@@ -270,14 +272,29 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable tgtShapes = getShapeList();
+ List srcShapes = src.getShapeList();
+ for(int i = 0; i < tgtShapes.size(); i++){
+ XSLFShape s1 = srcShapes.get(i);
+ XSLFShape s2 = tgtShapes.get(i);
+
+ s2.copy(s1);
+ }
+ return this;
}
/**
@@ -423,4 +440,32 @@ public abstract class XSLFSheet extends POIXMLDocumentPart implements Iterable{
private final XSLFTextShape _shape;
private List _lines;
private TextFragment _bullet;
+ /**
+ * the highest line in this paragraph. Used for line spacing.
+ */
+ private double _maxLineHeight;
XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){
_p = p;
_runs = new ArrayList();
_shape = shape;
- for (CTRegularTextRun r : _p.getRList()) {
- _runs.add(new XSLFTextRun(r, this));
- }
-
- for (CTTextField f : _p.getFldList()) {
- CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
- r.setT(f.getT());
- _runs.add(new XSLFTextRun(r, this));
+ for(XmlObject ch : _p.selectPath("*")){
+ if(ch instanceof CTRegularTextRun){
+ CTRegularTextRun r = (CTRegularTextRun)ch;
+ _runs.add(new XSLFTextRun(r, this));
+ } else if (ch instanceof CTTextLineBreak){
+ CTTextLineBreak br = (CTTextLineBreak)ch;
+ CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
+ r.setRPr(br.getRPr());
+ r.setT("\n");
+ _runs.add(new XSLFTextRun(r, this));
+ } else if (ch instanceof CTTextField){
+ CTTextField f = (CTTextField)ch;
+ CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();
+ r.setRPr(f.getRPr());
+ r.setT(f.getT());
+ _runs.add(new XSLFTextRun(r, this));
+ }
}
}
@@ -81,19 +100,10 @@ public class XSLFTextParagraph implements Iterable{
return out.toString();
}
- private String getVisibleText(){
+ String getRenderableText(){
StringBuilder out = new StringBuilder();
for (XSLFTextRun r : _runs) {
- String txt = r.getText();
- switch (r.getTextCap()){
- case ALL:
- txt = txt.toUpperCase();
- break;
- case SMALL:
- txt = txt.toLowerCase();
- break;
- }
- out.append(txt);
+ out.append(r.getRenderableText());
}
return out.toString();
}
@@ -183,6 +193,12 @@ public class XSLFTextParagraph implements Iterable{
return fetcher.getValue();
}
+ public void setBulletFont(String typeface){
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
+ CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont();
+ font.setTypeface(typeface);
+ }
+
/**
* @return the character to be used in place of the standard bullet point
*/
@@ -200,6 +216,12 @@ public class XSLFTextParagraph implements Iterable{
return fetcher.getValue();
}
+ public void setBulletCharacter(String str){
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
+ CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar();
+ c.setChar(str);
+ }
+
public Color getBulletFontColor(){
final XSLFTheme theme = getParentShape().getSheet().getTheme();
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
@@ -216,6 +238,13 @@ public class XSLFTextParagraph implements Iterable{
return fetcher.getValue();
}
+ public void setBulletFontColor(Color color){
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
+ CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr();
+ CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr();
+ clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});
+ }
+
public double getBulletFontSize(){
ParagraphPropertyFetcher fetcher = new ParagraphPropertyFetcher(getLevel()){
public boolean fetch(CTTextParagraphProperties props){
@@ -234,11 +263,17 @@ public class XSLFTextParagraph implements Iterable{
return fetcher.getValue() == null ? 100 : fetcher.getValue();
}
+ public void setBulletFontSize(double size){
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
+ CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts();
+ pt.setVal((int)(size*1000));
+ if(pr.isSetBuSzPct()) pr.unsetBuSzPct();
+ }
+
/**
* Specifies the indent size that will be applied to the first line of text in the paragraph.
*
- * @param value the indent in points. The value of -1 unsets the indent attribute
- * from the underlying xml bean.
+ * @param value the indent in points.
*/
public void setIndent(double value){
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();
@@ -297,7 +332,8 @@ public class XSLFTextParagraph implements Iterable{
}
};
fetchParagraphProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ // if the marL attribute is omitted, then a value of 347663 is implied
+ return fetcher.getValue() == null ? Units.toPoints(347663) : fetcher.getValue();
}
/**
@@ -512,7 +548,7 @@ public class XSLFTextParagraph implements Iterable{
/**
*
- * @param isBullet whether text in this paragraph has bullets
+ * @param flag whether text in this paragraph has bullets
*/
public void setBullet(boolean flag) {
if(isBullet() == flag) return;
@@ -535,38 +571,117 @@ public class XSLFTextParagraph implements Iterable{
return _lines;
}
+ /**
+ * Returns wrapping width to break lines in this paragraph
+ *
+ * @param firstLine whether the first line is breaking
+ *
+ * @return wrapping width in points
+ */
+ double getWrappingWidth(boolean firstLine){
+ // internal margins for the text box
+ double leftInset = _shape.getLeftInset();
+ double rightInset = _shape.getRightInset();
+
+ Rectangle2D anchor = _shape.getAnchor();
+
+ double leftMargin = getLeftMargin();
+ double indent = getIndent();
+
+ double width;
+ if(!_shape.getWordWrap()) {
+ // if wordWrap == false then we return the advance to the right border of the sheet
+ width = _shape.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX();
+ } else {
+ width = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ if(firstLine) {
+ if(isBullet()){
+ width -= Math.abs(indent);
+ } else {
+ if(indent > 0) width -= indent; // first line indentation
+ else if (indent < 0) { // hanging indentation: the first line start at the left margin
+ width += leftMargin;
+ }
+ }
+ }
+ }
+
+ return width;
+ }
+
public double draw(Graphics2D graphics, double x, double y){
- double marginLeft = _shape.getLeftInset();
- double marginRight = _shape.getRightInset();
+ double leftInset = _shape.getLeftInset();
+ double rightInset = _shape.getRightInset();
Rectangle2D anchor = _shape.getAnchor();
double penY = y;
- double textOffset = getLeftMargin();
+ double leftMargin = getLeftMargin();
boolean firstLine = true;
+ double indent = getIndent();
for(TextFragment line : _lines){
double penX = x;
+
+ if(firstLine) {
+
+ if(_bullet != null){
+ if(indent < 0) {
+ // a negative value means "Hanging" indentation and
+ // indicates the position of the actual bullet character.
+ // (the bullet is shifted to right relative to the text)
+ _bullet.draw(graphics, penX, penY);
+ penX -= indent;
+ } else if(indent > 0){
+ penX += leftMargin;
+ // a positive value means the "First Line" indentation:
+ // the first line is indented and other lines start at the bullet ofset
+ _bullet.draw(graphics, penX, penY);
+ penX += indent;
+ } else {
+ // no special indent. The first line behaves like all others
+ penX += leftMargin;
+
+ // a zero indent means that the bullet and text have the same offset
+ _bullet.draw(graphics, penX, penY);
+
+ // don't let text overlay the bullet and advance by the bullet width
+ penX += _bullet._layout.getAdvance() + 1;
+ }
+ } else {
+ if(indent < 0) {
+ // if bullet=false and indentation=hanging then the first line
+ // starts at the left offset (penX is not incremented)
+ } else if(indent > 0) {
+ // first line indent shifts penX
+ penX += indent + leftMargin;
+ } else {
+ // no special indent. The first line behaves like all others
+ penX += leftMargin;
+ }
+ }
+ } else {
+ penX += leftMargin;
+ }
+
+
switch (getTextAlign()) {
case CENTER:
- penX += textOffset + (anchor.getWidth() - textOffset - line.getWidth() - marginLeft - marginRight) / 2;
+ penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;
break;
case RIGHT:
- penX += (anchor.getWidth() - line.getWidth() - marginLeft - marginRight);
+ penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);
break;
default:
- penX = x + textOffset;
+ //penX += leftInset;
break;
}
- if(_bullet != null && firstLine){
- _bullet.draw(graphics, penX + getIndent(), penY);
- }
line.draw(graphics, penX, penY);
//The vertical line spacing
double spacing = getLineSpacing();
if(spacing > 0) {
// If linespacing >= 0, then linespacing is a percentage of normal line height.
- penY += spacing*0.01*line.getHeight();
+ penY += spacing*0.01* _maxLineHeight;
} else {
// positive value means absolute spacing in points
penY += -spacing;
@@ -607,13 +722,13 @@ public class XSLFTextParagraph implements Iterable{
}
AttributedString getAttributedString(Graphics2D graphics){
- String text = getVisibleText();
+ String text = getRenderableText();
AttributedString string = new AttributedString(text);
int startIndex = 0;
for (XSLFTextRun run : _runs){
- int length = run.getText().length();
+ int length = run.getRenderableText().length();
if(length == 0) {
// skip empty runs
continue;
@@ -656,6 +771,7 @@ public class XSLFTextParagraph implements Iterable{
void breakText(Graphics2D graphics){
_lines = new ArrayList();
+ String text = getRenderableText();
AttributedString at = getAttributedString(graphics);
AttributedCharacterIterator it = at.getIterator();
if(it.getBeginIndex() == it.getEndIndex()) {
@@ -664,12 +780,17 @@ public class XSLFTextParagraph implements Iterable{
LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());
for (;;) {
int startIndex = measurer.getPosition();
- double wrappingWidth = getWrappingWidth() + 1; // add a pixel to compensate rounding errors
- TextLayout layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), true);
+ double wrappingWidth = getWrappingWidth(_lines.size() == 0) + 1; // add a pixel to compensate rounding errors
+
+
+ int nextBreak = text.indexOf('\n', startIndex + 1);
+ if(nextBreak == -1) nextBreak = it.getEndIndex();
+
+ TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);
if (layout == null) {
// layout can be null if the entire word at the current position
// does not fit within the wrapping width. Try with requireNextWord=false.
- layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), false);
+ layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);
}
int endIndex = measurer.getPosition();
@@ -683,6 +804,8 @@ public class XSLFTextParagraph implements Iterable{
TextFragment line = new TextFragment(layout, str);
_lines.add(line);
+ _maxLineHeight = Math.max(_maxLineHeight, line.getHeight());
+
if(endIndex == it.getEndIndex()) break;
}
@@ -714,17 +837,6 @@ public class XSLFTextParagraph implements Iterable{
}
- double getWrappingWidth(){
- double width;
- if(!_shape.getWordWrap()) {
- width = _shape.getSheet().getSlideShow().getPageSize().getWidth();
- } else {
- width = _shape.getAnchor().getWidth() -
- _shape.getLeftInset() - _shape.getRightInset() - getLeftMargin();
- }
- return width;
- }
-
CTTextParagraphProperties getDefaultStyle(){
CTPlaceholder ph = _shape.getCTPlaceholder();
String defaultStyleSelector;
@@ -782,4 +894,64 @@ public class XSLFTextParagraph implements Iterable{
return ok;
}
+ void copy(XSLFTextParagraph p){
+ TextAlign srcAlign = p.getTextAlign();
+ if(srcAlign != getTextAlign()){
+ setTextAlign(srcAlign);
+ }
+
+ boolean isBullet = p.isBullet();
+ if(isBullet != isBullet()){
+ setBullet(isBullet);
+ if(isBullet) {
+ String buFont = p.getBulletFont();
+ if(buFont != null && !buFont.equals(getBulletFont())){
+ setBulletFont(buFont);
+ }
+ String buChar = p.getBulletCharacter();
+ if(buChar != null && !buChar.equals(getBulletCharacter())){
+ setBulletCharacter(buChar);
+ }
+ Color buColor = p.getBulletFontColor();
+ if(buColor != null && !buColor.equals(getBulletFontColor())){
+ setBulletFontColor(buColor);
+ }
+ double buSize = p.getBulletFontSize();
+ if(buSize != getBulletFontSize()){
+ setBulletFontSize(buSize);
+ }
+ }
+ }
+
+ double leftMargin = p.getLeftMargin();
+ if(leftMargin != getLeftMargin()){
+ setLeftMargin(leftMargin);
+ }
+
+ double indent = p.getIndent();
+ if(indent != getIndent()){
+ setIndent(indent);
+ }
+
+ double spaceAfter = p.getSpaceAfter();
+ if(spaceAfter != getSpaceAfter()){
+ setSpaceAfter(spaceAfter);
+ }
+ double spaceBefore = p.getSpaceBefore();
+ if(spaceBefore != getSpaceBefore()){
+ setSpaceBefore(spaceBefore);
+ }
+ double lineSpacing = p.getLineSpacing();
+ if(lineSpacing != getLineSpacing()){
+ setLineSpacing(lineSpacing);
+ }
+
+ List srcR = p.getTextRuns();
+ List tgtR = getTextRuns();
+ for(int i = 0; i < srcR.size(); i++){
+ XSLFTextRun r1 = srcR.get(i);
+ XSLFTextRun r2 = tgtR.get(i);
+ r2.copy(r1);
+ }
+ }
}
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 b4aa3ca8b..dc79e73c6 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextRun.java
@@ -56,6 +56,23 @@ public class XSLFTextRun {
return _r.getT();
}
+ String getRenderableText(){
+ String txt = _r.getT();
+ switch (getTextCap()){
+ case ALL:
+ txt = txt.toUpperCase();
+ break;
+ case SMALL:
+ txt = txt.toLowerCase();
+ break;
+ }
+ // TODO-1 is is the place to convert wingdings to unicode
+
+ // TODO-2 this is a temporary hack. Rendering text with tabs is not yet supported.
+ // for now tabs are replaced with some number of spaces.
+ return txt.replace("\t", " ");
+ }
+
public void setText(String text){
_r.setT(text);
}
@@ -69,6 +86,13 @@ public class XSLFTextRun {
CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill();
CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();
clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});
+
+ if(fill.isSetHslClr()) fill.unsetHslClr();
+ if(fill.isSetPrstClr()) fill.unsetPrstClr();
+ if(fill.isSetSchemeClr()) fill.unsetSchemeClr();
+ if(fill.isSetScrgbClr()) fill.unsetScrgbClr();
+ if(fill.isSetSysClr()) fill.unsetSysClr();
+
}
public Color getFontColor(){
@@ -393,4 +417,32 @@ public class XSLFTextRun {
return ok;
}
+ void copy(XSLFTextRun r){
+ String srcFontFamily = r.getFontFamily();
+ if(srcFontFamily != null && !srcFontFamily.equals(getFontFamily())){
+ setFontFamily(srcFontFamily);
+ }
+
+ Color srcFontColor = r.getFontColor();
+ if(srcFontColor != null && !srcFontColor.equals(getFontColor())){
+ setFontColor(srcFontColor);
+ }
+
+ double srcFontSize = r.getFontSize();
+ if(srcFontSize != getFontSize()){
+ setFontSize(srcFontSize);
+ }
+
+ boolean bold = r.isBold();
+ if(bold != isBold()) setBold(bold);
+
+ boolean italic = r.isItalic();
+ if(italic != isItalic()) setItalic(italic);
+
+ boolean underline = r.isUnderline();
+ if(underline != isUnderline()) setUnderline(underline);
+
+ boolean strike = r.isStrikethrough();
+ if(strike != isStrikethrough()) setStrikethrough(strike);
+ }
}
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 dd49c316a..cce5a61e8 100644
--- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
+++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextShape.java
@@ -202,7 +202,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
}
};
fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ // If this attribute is omitted, then a value of 0.05 inches is implied
+ return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
}
/**
@@ -224,7 +225,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
}
};
fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ // If this attribute is omitted, then a value of 0.1 inches is implied
+ return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
}
/**
@@ -246,7 +248,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
}
};
fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ // If this attribute is omitted, then a value of 0.1 inches is implied
+ return fetcher.getValue() == null ? 7.2 : fetcher.getValue();
}
/**
@@ -267,7 +270,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
}
};
fetchShapeProperty(fetcher);
- return fetcher.getValue() == null ? 0 : fetcher.getValue();
+ // If this attribute is omitted, then a value of 0.05 inches is implied
+ return fetcher.getValue() == null ? 3.6 : fetcher.getValue();
}
/**
@@ -521,4 +525,46 @@ public abstract class XSLFTextShape extends XSLFSimpleShape implements Iterable<
return y - y0;
}
+ @Override
+ void copy(XSLFShape sh){
+ super.copy(sh);
+
+ XSLFTextShape tsh = (XSLFTextShape)sh;
+
+ boolean srcWordWrap = tsh.getWordWrap();
+ if(srcWordWrap != getWordWrap()){
+ setWordWrap(srcWordWrap);
+ }
+
+ double leftInset = tsh.getLeftInset();
+ if(leftInset != getLeftInset()) {
+ setLeftInset(leftInset);
+ }
+ double rightInset = tsh.getRightInset();
+ if(rightInset != getRightInset()) {
+ setRightInset(rightInset);
+ }
+ double topInset = tsh.getTopInset();
+ if(topInset != getTopInset()) {
+ setTopInset(topInset);
+ }
+ double bottomInset = tsh.getBottomInset();
+ if(bottomInset != getBottomInset()) {
+ setBottomInset(bottomInset);
+ }
+
+ VerticalAlignment vAlign = tsh.getVerticalAlignment();
+ if(vAlign != getVerticalAlignment()) {
+ setVerticalAlignment(vAlign);
+ }
+
+ List srcP = tsh.getTextParagraphs();
+ List tgtP = getTextParagraphs();
+ for(int i = 0; i < srcP.size(); i++){
+ XSLFTextParagraph p1 = srcP.get(i);
+ XSLFTextParagraph p2 = tgtP.get(i);
+ p2.copy(p1);
+ }
+
+ }
}
\ No newline at end of file
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 d8cb5783e..d5e7c61c2 100755
--- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFSlide.java
@@ -19,6 +19,12 @@ package org.apache.poi.xslf.usermodel;
import junit.framework.TestCase;
import org.apache.poi.xslf.XSLFTestDataSamples;
+import org.apache.poi.openxml4j.opc.PackagePart;
+
+import java.awt.Color;
+import java.util.List;
+import java.util.Arrays;
+import java.util.regex.Pattern;
/**
* @author Yegor Kozlov
@@ -103,4 +109,58 @@ public class TestXSLFSlide extends TestCase {
assertTrue(slide.getFollowMasterGraphics());
}
+ public void testImportContent(){
+ XMLSlideShow ppt = new XMLSlideShow();
+
+ XMLSlideShow src = XSLFTestDataSamples.openSampleDocument("themes.pptx");
+
+ // create a blank slide and import content from the 4th slide of themes.pptx
+ XSLFSlide slide1 = ppt.createSlide().importContent(src.getSlides()[3]);
+ XSLFShape[] shapes1 = slide1.getShapes();
+ assertEquals(2, shapes1.length);
+
+ XSLFTextShape sh1 = (XSLFTextShape)shapes1[0];
+ assertEquals("Austin Theme", sh1.getText());
+ XSLFTextRun r1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals("Century Gothic", r1.getFontFamily());
+ assertEquals(40.0, r1.getFontSize());
+ assertTrue(r1.isBold());
+ assertTrue(r1.isItalic());
+ assertEquals(new Color(148, 198, 0), r1.getFontColor());
+ assertNull(sh1.getFillColor());
+ assertNull(sh1.getLineColor());
+
+ XSLFTextShape sh2 = (XSLFTextShape)shapes1[1];
+ assertEquals(
+ "Text in a autoshape is white\n" +
+ "Fill: RGB(148, 198,0)", sh2.getText());
+ XSLFTextRun r2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals("Century Gothic", r2.getFontFamily());
+ assertEquals(18.0, r2.getFontSize());
+ assertFalse(r2.isBold());
+ assertFalse(r2.isItalic());
+ assertEquals(Color.white, r2.getFontColor());
+ assertEquals(new Color(148, 198, 0), sh2.getFillColor());
+ assertEquals(new Color(74, 99, 0), sh2.getLineColor()); // slightly different from PowerPoint!
+
+ // the 5th slide has a picture and a texture fill
+ XSLFSlide slide2 = ppt.createSlide().importContent(src.getSlides()[4]);
+ XSLFShape[] shapes2 = slide2.getShapes();
+ assertEquals(2, shapes2.length);
+
+ XSLFTextShape sh3 = (XSLFTextShape)shapes2[0];
+ assertEquals("This slide overrides master background with a texture fill", sh3.getText());
+ XSLFTextRun r3 = sh3.getTextParagraphs().get(0).getTextRuns().get(0);
+ assertEquals("Century Gothic", r3.getFontFamily());
+ //assertEquals(32.4.0, r3.getFontSize());
+ assertTrue(r3.isBold());
+ assertTrue(r3.isItalic());
+ assertEquals(new Color(148, 198, 0), r3.getFontColor());
+ assertNull(sh3.getFillColor());
+ assertNull(sh3.getLineColor());
+
+ XSLFPictureShape sh4 = (XSLFPictureShape)shapes2[1];
+ XSLFPictureShape srcPic = (XSLFPictureShape)src.getSlides()[4].getShapes()[1];
+ assertTrue(Arrays.equals(sh4.getPictureData().getData(), srcPic.getPictureData().getData()));
+ }
}
\ 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
new file mode 100755
index 000000000..c426fd15c
--- /dev/null
+++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTextParagraph.java
@@ -0,0 +1,105 @@
+package org.apache.poi.xslf.usermodel;
+
+import junit.framework.TestCase;
+
+import java.awt.Rectangle;
+import java.awt.Color;
+import java.awt.geom.Rectangle2D;
+import java.io.FileOutputStream;
+
+import org.apache.poi.xssf.dev.XSSFDump;
+import org.apache.poi.xslf.util.PPTX2PNG;
+
+/**
+ * Created by IntelliJ IDEA.
+ * User: yegor
+ * Date: Nov 10, 2011
+ * Time: 1:43:25 PM
+ * To change this template use File | Settings | File Templates.
+ */
+public class TestXSLFTextParagraph extends TestCase {
+
+ public void testWrappingWidth() throws Exception {
+ XMLSlideShow ppt = new XMLSlideShow();
+ XSLFSlide slide = ppt.createSlide();
+ XSLFTextShape sh = slide.createAutoShape();
+ sh.setLineColor(Color.black);
+
+ XSLFTextParagraph p = sh.addNewTextParagraph();
+ p.addNewTextRun().setText(
+ "Paragraph formatting allows for more granular control " +
+ "of text within a shape. Properties here apply to all text " +
+ "residing within the corresponding paragraph.");
+
+ Rectangle2D anchor = new Rectangle(50, 50, 300, 200);
+ sh.setAnchor(anchor);
+
+ double leftInset = sh.getLeftInset();
+ double rightInset = sh.getRightInset();
+ assertEquals(7.2, leftInset);
+ assertEquals(7.2, rightInset);
+
+ double leftMargin = p.getLeftMargin();
+ assertEquals(0.0, leftMargin);
+
+ double indent = p.getIndent();
+ assertEquals(0.0, indent); // default
+
+ double expectedWidth;
+
+ // Case 1: bullet=false, leftMargin=0, indent=0.
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ assertEquals(285.6, expectedWidth); // 300 - 7.2 - 7.2 - 0
+ assertEquals(expectedWidth, p.getWrappingWidth(true));
+ assertEquals(expectedWidth, p.getWrappingWidth(false));
+
+ p.setLeftMargin(36); // 0.5"
+ leftMargin = p.getLeftMargin();
+ assertEquals(36.0, leftMargin);
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ assertEquals(249.6, expectedWidth, 1E-5); // 300 - 7.2 - 7.2 - 36
+ assertEquals(expectedWidth, p.getWrappingWidth(true));
+ assertEquals(expectedWidth, p.getWrappingWidth(false));
+
+ // increase insets, the wrapping width should get smaller
+ sh.setLeftInset(10);
+ sh.setRightInset(10);
+ leftInset = sh.getLeftInset();
+ rightInset = sh.getRightInset();
+ assertEquals(10.0, leftInset);
+ assertEquals(10.0, rightInset);
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
+ assertEquals(expectedWidth, p.getWrappingWidth(true));
+ assertEquals(expectedWidth, p.getWrappingWidth(false));
+
+ // set a positive indent of a 0.5 inch. This means "First Line" indentation:
+ // |<--- indent -->|Here goes first line of the text
+ // Here go other lines (second and subsequent)
+
+ p.setIndent(36.0); // 0.5"
+ indent = p.getIndent();
+ assertEquals(36.0, indent);
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin - indent;
+ assertEquals(208.0, expectedWidth); // 300 - 10 - 10 - 36 - 6.4
+ assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is indented
+ // other lines are not indented
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
+ assertEquals(expectedWidth, p.getWrappingWidth(false));
+
+ // set a negative indent of a 1 inch. This means "Hanging" indentation:
+ // Here goes first line of the text
+ // |<--- indent -->|Here go other lines (second and subsequent)
+ p.setIndent(-72.0); // 1"
+ indent = p.getIndent();
+ assertEquals(-72.0, indent);
+ expectedWidth = anchor.getWidth() - leftInset - rightInset;
+ assertEquals(280.0, expectedWidth); // 300 - 10 - 10
+ assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is NOT indented
+ // other lines are indented by leftMargin (the value of indent is not used)
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36
+ assertEquals(expectedWidth, p.getWrappingWidth(false));
+ }
+}
diff --git a/test-data/slideshow/themes.pptx b/test-data/slideshow/themes.pptx
index ff39de3ed..5f2e751a7 100755
Binary files a/test-data/slideshow/themes.pptx and b/test-data/slideshow/themes.pptx differ