git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1200330 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_8_BETA5
@@ -17,11 +17,13 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
/** | |||
* | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public enum LineCap { | |||
/** | |||
* Rounded ends - the default | |||
* Rounded ends | |||
*/ | |||
ROUND, | |||
/** |
@@ -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()); | |||
} |
@@ -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; | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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) { |
@@ -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. | |||
* <p> | |||
* 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. | |||
* </p> | |||
* | |||
* @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 | |||
* <p> | |||
@@ -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()); | |||
} | |||
} |
@@ -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<X | |||
/** | |||
* Set the contents of this sheet to be a copy of the source sheet. | |||
* This method erases any existing shapes and replaces them with | |||
* object from the source sheet. | |||
* | |||
* @param src the source sheet to copy data from | |||
* @return modified 'this' | |||
*/ | |||
public void copy(XSLFSheet src){ | |||
public XSLFSheet importContent(XSLFSheet src){ | |||
_shapes = null; | |||
_spTree = null; | |||
_drawing = null; | |||
// first copy the source xml | |||
getXmlObject().set(src.getXmlObject()); | |||
// recursively update each shape | |||
List<XSLFShape> tgtShapes = getShapeList(); | |||
List<XSLFShape> 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<X | |||
} | |||
} | |||
/** | |||
* Import a picture data from another document. | |||
* | |||
* @param blipId ID of the package relationship to retrieve. | |||
* @param packagePart package part containing the data to import | |||
* @return ID of the created relationship | |||
*/ | |||
String importBlip(String blipId, PackagePart packagePart) { | |||
PackageRelationship blipRel = packagePart.getRelationship(blipId); | |||
PackagePart blipPart; | |||
try { | |||
blipPart = packagePart.getRelatedPart(blipRel); | |||
} catch (InvalidFormatException e){ | |||
throw new POIXMLException(e); | |||
} | |||
XSLFPictureData data = new XSLFPictureData(blipPart, null); | |||
XMLSlideShow ppt = getSlideShow(); | |||
int pictureIdx = ppt.addPicture(data.getData(), data.getPictureType()); | |||
PackagePart pic = ppt.getAllPictures().get(pictureIdx).getPackagePart(); | |||
PackageRelationship rel = getPackagePart().addRelationship( | |||
pic.getPartName(), TargetMode.INTERNAL, blipRel.getRelationshipType()); | |||
addRelation(rel.getId(), new XSLFPictureData(pic, rel)); | |||
return rel.getId(); | |||
} | |||
} |
@@ -29,6 +29,11 @@ import org.apache.poi.xslf.model.geom.IAdjustableShape; | |||
import org.apache.poi.xslf.model.geom.Outline; | |||
import org.apache.poi.xslf.model.geom.Path; | |||
import org.apache.poi.xslf.model.geom.PresetGeometries; | |||
import org.apache.poi.openxml4j.opc.PackageRelationship; | |||
import org.apache.poi.openxml4j.opc.PackagePart; | |||
import org.apache.poi.openxml4j.opc.TargetMode; | |||
import org.apache.poi.openxml4j.exceptions.InvalidFormatException; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTEffectStyleItem; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide; | |||
@@ -48,8 +53,10 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture; | |||
import java.awt.Color; | |||
import java.awt.Graphics2D; | |||
@@ -69,6 +76,7 @@ import java.util.List; | |||
*/ | |||
@Beta | |||
public abstract class XSLFSimpleShape extends XSLFShape { | |||
private static CTOuterShadowEffect NO_SHADOW = CTOuterShadowEffect.Factory.newInstance(); | |||
private final XmlObject _shape; | |||
private final XSLFSheet _sheet; | |||
@@ -285,6 +293,11 @@ public abstract class XSLFSimpleShape extends XSLFShape { | |||
CTSolidColorFillProperties fill = ln.isSetSolidFill() ? ln | |||
.getSolidFill() : ln.addNewSolidFill(); | |||
fill.setSrgbClr(rgb); | |||
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(); | |||
} | |||
} | |||
@@ -483,6 +496,11 @@ public abstract class XSLFSimpleShape extends XSLFShape { | |||
(byte) color.getGreen(), (byte) color.getBlue()}); | |||
fill.setSrgbClr(rgb); | |||
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(); | |||
} | |||
} | |||
@@ -508,7 +526,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { | |||
CTShapeProperties spPr = shape.getSpPr(); | |||
if (spPr.isSetEffectLst()) { | |||
CTOuterShadowEffect obj = spPr.getEffectLst().getOuterShdw(); | |||
setValue(obj); | |||
setValue(obj == null ? NO_SHADOW : obj); | |||
return true; | |||
} | |||
return false; | |||
@@ -530,7 +548,7 @@ public abstract class XSLFSimpleShape extends XSLFShape { | |||
} | |||
} | |||
} | |||
return obj == null ? null : new XSLFShadow(obj, this); | |||
return (obj == null || obj == NO_SHADOW) ? null : new XSLFShadow(obj, this); | |||
} | |||
@Override | |||
@@ -640,4 +658,50 @@ public abstract class XSLFSimpleShape extends XSLFShape { | |||
public void drawContent(Graphics2D graphics){ | |||
} | |||
@Override | |||
void copy(XSLFShape sh){ | |||
super.copy(sh); | |||
XSLFSimpleShape s = (XSLFSimpleShape)sh; | |||
Color srsSolidFill = s.getFillColor(); | |||
Color tgtSoliFill = getFillColor(); | |||
if(srsSolidFill != null && !srsSolidFill.equals(tgtSoliFill)){ | |||
setFillColor(srsSolidFill); | |||
} | |||
if(getSpPr().isSetBlipFill()){ | |||
CTBlip blip = getSpPr().getBlipFill().getBlip(); | |||
String blipId = blip.getEmbed(); | |||
String relId = getSheet().importBlip(blipId, s.getSheet().getPackagePart()); | |||
blip.setEmbed(relId); | |||
} | |||
Color srcLineColor = s.getLineColor(); | |||
Color tgtLineColor = getLineColor(); | |||
if(srcLineColor != null && !srcLineColor.equals(tgtLineColor)) { | |||
setLineColor(srcLineColor); | |||
} | |||
double srcLineWidth = s.getLineWidth(); | |||
double tgtLineWidth = getLineWidth(); | |||
if(srcLineWidth != tgtLineWidth) { | |||
setLineWidth(srcLineWidth); | |||
} | |||
LineDash srcLineDash = s.getLineDash(); | |||
LineDash tgtLineDash = getLineDash(); | |||
if(srcLineDash != null && srcLineDash != tgtLineDash) { | |||
setLineDash(srcLineDash); | |||
} | |||
LineCap srcLineCap = s.getLineCap(); | |||
LineCap tgtLineCap = getLineCap(); | |||
if(srcLineCap != null && srcLineCap != tgtLineCap) { | |||
setLineCap(srcLineCap); | |||
} | |||
} | |||
} |
@@ -26,11 +26,13 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTGroupTransform2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTCommonSlideData; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShape; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTGroupShapeNonVisual; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTSlide; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.SldDocument; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTBackground; | |||
import java.awt.Graphics2D; | |||
import java.io.IOException; | |||
@@ -228,4 +230,21 @@ public final class XSLFSlide extends XSLFSheet { | |||
} | |||
@Override | |||
public XSLFSlide importContent(XSLFSheet src){ | |||
super.importContent(src); | |||
CTBackground bg = ((CTSlide)src.getXmlObject()).getCSld().getBg(); | |||
if(bg != null) { | |||
if(bg.isSetBgPr() && bg.getBgPr().isSetBlipFill()){ | |||
CTBlip blip = bg.getBgPr().getBlipFill().getBlip(); | |||
String blipId = blip.getEmbed(); | |||
String relId = importBlip(blipId, src.getPackagePart()); | |||
blip.setEmbed(relId); | |||
} | |||
} | |||
return this; | |||
} | |||
} |
@@ -27,6 +27,12 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
@@ -56,20 +62,33 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ | |||
private final XSLFTextShape _shape; | |||
private List<TextFragment> _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<XSLFTextRun>(); | |||
_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<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
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<Color> fetcher = new ParagraphPropertyFetcher<Color>(getLevel()){ | |||
@@ -216,6 +238,13 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ | |||
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<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){ | |||
public boolean fetch(CTTextParagraphProperties props){ | |||
@@ -234,11 +263,17 @@ public class XSLFTextParagraph implements Iterable<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
} | |||
}; | |||
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<XSLFTextRun>{ | |||
/** | |||
* | |||
* @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<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
} | |||
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<XSLFTextRun>{ | |||
void breakText(Graphics2D graphics){ | |||
_lines = new ArrayList<TextFragment>(); | |||
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<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
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<XSLFTextRun>{ | |||
} | |||
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<XSLFTextRun>{ | |||
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<XSLFTextRun> srcR = p.getTextRuns(); | |||
List<XSLFTextRun> tgtR = getTextRuns(); | |||
for(int i = 0; i < srcR.size(); i++){ | |||
XSLFTextRun r1 = srcR.get(i); | |||
XSLFTextRun r2 = tgtR.get(i); | |||
r2.copy(r1); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |
@@ -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<XSLFTextParagraph> srcP = tsh.getTextParagraphs(); | |||
List<XSLFTextParagraph> tgtP = getTextParagraphs(); | |||
for(int i = 0; i < srcP.size(); i++){ | |||
XSLFTextParagraph p1 = srcP.get(i); | |||
XSLFTextParagraph p2 = tgtP.get(i); | |||
p2.copy(p1); | |||
} | |||
} | |||
} |
@@ -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())); | |||
} | |||
} |
@@ -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)); | |||
} | |||
} |