package org.apache.poi.xslf.usermodel;\r
\r
/**\r
+ * \r
+ *\r
* @author Yegor Kozlov\r
*/\r
public enum LineCap {\r
/**\r
- * Rounded ends - the default\r
+ * Rounded ends\r
*/\r
ROUND,\r
/**\r
\r
// first fill\r
Paint fill = getFillPaint(graphics);\r
+ Paint line = getLinePaint(graphics);\r
+ applyStroke(graphics); // the stroke applies both to the shadow and the shape\r
+\r
+ // first paint the shadow\r
+ if(shadow != null) for(Outline o : elems){\r
+ if(o.getPath().isFilled()){\r
+ if(fill != null) shadow.fill(graphics, o.getOutline());\r
+ if(line != null) shadow.draw(graphics, o.getOutline());\r
+ }\r
+ }\r
+ // then fill the shape interior\r
if(fill != null) for(Outline o : elems){\r
if(o.getPath().isFilled()){\r
- if(shadow != null) shadow.fill(graphics, o.getOutline());\r
-\r
graphics.setPaint(fill);\r
graphics.fill(o.getOutline());\r
}\r
_shape.drawContent(graphics);\r
\r
// then stroke the shape outline\r
- Paint line = getLinePaint(graphics);\r
if(line != null) for(Outline o : elems){\r
if(o.getPath().isStroked()){\r
- applyStroke(graphics); // the stroke applies both to the shadow and the shape\r
-\r
- if(shadow != null) shadow.draw(graphics, o.getOutline());\r
-\r
graphics.setPaint(line);\r
graphics.draw(o.getOutline());\r
}\r
return lst;\r
}\r
\r
+ /**\r
+ * YK: dashing of lines is suppressed for now.\r
+ * @return\r
+ */\r
+ @Override\r
+ public XSLFShadow getShadow() {\r
+ return null;\r
+ }\r
}
\ No newline at end of file
import org.apache.poi.POIXMLException;\r
import org.apache.poi.openxml4j.opc.PackagePart;\r
import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
import org.apache.poi.util.Beta;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTBlipFillProperties;\r
\r
public XSLFPictureData getPictureData() {\r
if(_data == null){\r
- CTPicture ct = (CTPicture)getXmlObject();\r
- String blipId = ct.getBlipFill().getBlip().getEmbed();\r
+ String blipId = getBlipId();\r
\r
PackagePart p = getSheet().getPackagePart();\r
PackageRelationship rel = p.getRelationship(blipId);\r
return _data;\r
}\r
\r
+ private String getBlipId(){\r
+ CTPicture ct = (CTPicture)getXmlObject();\r
+ return ct.getBlipFill().getBlip().getEmbed();\r
+ }\r
+\r
@Override\r
public void drawContent(Graphics2D graphics) {\r
\r
\r
renderer.drawImage(graphics, data, getAnchor());\r
}\r
+\r
+\r
+ @Override\r
+ void copy(XSLFShape sh){\r
+ super.copy(sh);\r
+\r
+ XSLFPictureShape p = (XSLFPictureShape)sh;\r
+ String blipId = p.getBlipId();\r
+ String relId = getSheet().importBlip(blipId, p.getSheet().getPackagePart());\r
+\r
+ CTPicture ct = (CTPicture)getXmlObject();\r
+ CTBlip blip = ct.getBlipFill().getBlip();\r
+ blip.setEmbed(relId);\r
+\r
+ }\r
}\r
* @author Yegor Kozlov\r
*/\r
public class XSLFShadow extends XSLFSimpleShape {\r
+\r
private XSLFSimpleShape _parent;\r
\r
/* package */XSLFShadow(CTOuterShadowEffect shape, XSLFSimpleShape parentShape) {\r
package org.apache.poi.xslf.usermodel;\r
\r
import org.apache.poi.util.Beta;\r
+import org.apache.poi.util.Internal;\r
import org.apache.xmlbeans.XmlObject;\r
\r
import java.awt.Graphics2D;\r
public abstract class XSLFShape {\r
\r
/**\r
- *\r
* @return the position of this shape within the drawing canvas.\r
- * The coordinates are expressed in points\r
+ * The coordinates are expressed in points\r
*/\r
public abstract Rectangle2D getAnchor();\r
\r
/**\r
- *\r
* @param anchor the position of this shape within the drawing canvas.\r
- * The coordinates are expressed in points\r
+ * The coordinates are expressed in points\r
*/\r
public abstract void setAnchor(Rectangle2D anchor);\r
\r
/**\r
- *\r
* @return the xml bean holding this shape's data\r
*/\r
public abstract XmlObject getXmlObject();\r
\r
/**\r
- *\r
* @return human-readable name of this shape, e.g. "Rectange 3"\r
*/\r
public abstract String getShapeName();\r
* This ID may be used to assist in uniquely identifying this object so that it can\r
* be referred to by other parts of the document.\r
* <p>\r
- * If multiple objects within the same document share the same id attribute value,\r
- * then the document shall be considered non-conformant.\r
+ * If multiple objects within the same document share the same id attribute value,\r
+ * then the document shall be considered non-conformant.\r
* </p>\r
*\r
* @return unique id of this shape\r
* @param theta the rotation angle in degrees.\r
*/\r
public abstract void setRotation(double theta);\r
- \r
+\r
/**\r
* Rotation angle in degrees\r
* <p>\r
* @param flip whether the shape is vertically flipped\r
*/\r
public abstract void setFlipVertical(boolean flip);\r
- \r
+\r
/**\r
* Whether the shape is horizontally flipped\r
*\r
*\r
* @param graphics the graphics whos transform matrix will be modified\r
*/\r
- protected void applyTransform(Graphics2D graphics){\r
+ protected void applyTransform(Graphics2D graphics) {\r
Rectangle2D anchor = getAnchor();\r
\r
// rotation\r
double rotation = getRotation();\r
- if(rotation != 0.) {\r
- // PowerPoint rotates shapes relative to the geometric center\r
- double centerX = anchor.getX() + anchor.getWidth()/2;\r
- double centerY = anchor.getY() + anchor.getHeight()/2;\r
+ if (rotation != 0.) {\r
+ // PowerPoint rotates shapes relative to the geometric center\r
+ double centerX = anchor.getX() + anchor.getWidth() / 2;\r
+ double centerY = anchor.getY() + anchor.getHeight() / 2;\r
\r
graphics.translate(centerX, centerY);\r
graphics.rotate(Math.toRadians(rotation));\r
}\r
\r
//flip horizontal\r
- if(getFlipHorizontal()){\r
+ if (getFlipHorizontal()) {\r
graphics.translate(anchor.getX() + anchor.getWidth(), anchor.getY());\r
graphics.scale(-1, 1);\r
- graphics.translate(-anchor.getX() , -anchor.getY());\r
+ graphics.translate(-anchor.getX(), -anchor.getY());\r
}\r
\r
//flip vertical\r
- if(getFlipVertical()){\r
+ if (getFlipVertical()) {\r
graphics.translate(anchor.getX(), anchor.getY() + anchor.getHeight());\r
graphics.scale(1, -1);\r
graphics.translate(-anchor.getX(), -anchor.getY());\r
}\r
}\r
\r
+ /**\r
+ * Set the contents of this shape to be a copy of the source shape.\r
+ * This method is called recursively for each shape when merging slides\r
+ *\r
+ * @param sh the source shape\r
+ * @see org.apache.poi.xslf.usermodel.XSLFSlide#importContent(XSLFSheet)\r
+ */\r
+ @Internal\r
+ void copy(XSLFShape sh) {\r
+ if (!getClass().isInstance(sh)) {\r
+ throw new IllegalArgumentException(\r
+ "Can't copy " + sh.getClass().getSimpleName() + " into " + getClass().getSimpleName());\r
+ }\r
+\r
+ setAnchor(sh.getAnchor());\r
+ }\r
}
\ No newline at end of file
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;
/**
* 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;
}
/**
}
}
+
+ /**
+ * 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();
+ }
}
\ No newline at end of file
import org.apache.poi.xslf.model.geom.Outline;\r
import org.apache.poi.xslf.model.geom.Path;\r
import org.apache.poi.xslf.model.geom.PresetGeometries;\r
+import org.apache.poi.openxml4j.opc.PackageRelationship;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+import org.apache.poi.openxml4j.opc.TargetMode;\r
+import org.apache.poi.openxml4j.exceptions.InvalidFormatException;\r
+import org.apache.poi.POIXMLException;\r
import org.apache.xmlbeans.XmlObject;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTEffectStyleItem;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTGeomGuide;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STShapeType;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTBlip;\r
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;\r
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;\r
+import org.openxmlformats.schemas.presentationml.x2006.main.CTPicture;\r
\r
import java.awt.Color;\r
import java.awt.Graphics2D;\r
*/\r
@Beta\r
public abstract class XSLFSimpleShape extends XSLFShape {\r
+ private static CTOuterShadowEffect NO_SHADOW = CTOuterShadowEffect.Factory.newInstance();\r
\r
private final XmlObject _shape;\r
private final XSLFSheet _sheet;\r
CTSolidColorFillProperties fill = ln.isSetSolidFill() ? ln\r
.getSolidFill() : ln.addNewSolidFill();\r
fill.setSrgbClr(rgb);\r
+ if(fill.isSetHslClr()) fill.unsetHslClr();\r
+ if(fill.isSetPrstClr()) fill.unsetPrstClr();\r
+ if(fill.isSetSchemeClr()) fill.unsetSchemeClr();\r
+ if(fill.isSetScrgbClr()) fill.unsetScrgbClr();\r
+ if(fill.isSetSysClr()) fill.unsetSysClr();\r
}\r
}\r
\r
(byte) color.getGreen(), (byte) color.getBlue()});\r
\r
fill.setSrgbClr(rgb);\r
+ if(fill.isSetHslClr()) fill.unsetHslClr();\r
+ if(fill.isSetPrstClr()) fill.unsetPrstClr();\r
+ if(fill.isSetSchemeClr()) fill.unsetSchemeClr();\r
+ if(fill.isSetScrgbClr()) fill.unsetScrgbClr();\r
+ if(fill.isSetSysClr()) fill.unsetSysClr();\r
}\r
}\r
\r
CTShapeProperties spPr = shape.getSpPr();\r
if (spPr.isSetEffectLst()) {\r
CTOuterShadowEffect obj = spPr.getEffectLst().getOuterShdw();\r
- setValue(obj);\r
+ setValue(obj == null ? NO_SHADOW : obj);\r
return true;\r
}\r
return false;\r
}\r
}\r
}\r
- return obj == null ? null : new XSLFShadow(obj, this);\r
+ return (obj == null || obj == NO_SHADOW) ? null : new XSLFShadow(obj, this);\r
}\r
\r
@Override\r
public void drawContent(Graphics2D graphics){\r
\r
}\r
+\r
+ @Override\r
+ void copy(XSLFShape sh){\r
+ super.copy(sh);\r
+\r
+ XSLFSimpleShape s = (XSLFSimpleShape)sh;\r
+\r
+ Color srsSolidFill = s.getFillColor();\r
+ Color tgtSoliFill = getFillColor();\r
+ if(srsSolidFill != null && !srsSolidFill.equals(tgtSoliFill)){\r
+ setFillColor(srsSolidFill);\r
+ }\r
+\r
+ if(getSpPr().isSetBlipFill()){\r
+ CTBlip blip = getSpPr().getBlipFill().getBlip();\r
+ String blipId = blip.getEmbed();\r
+\r
+ String relId = getSheet().importBlip(blipId, s.getSheet().getPackagePart());\r
+ blip.setEmbed(relId);\r
+ }\r
+ \r
+ Color srcLineColor = s.getLineColor();\r
+ Color tgtLineColor = getLineColor();\r
+ if(srcLineColor != null && !srcLineColor.equals(tgtLineColor)) {\r
+ setLineColor(srcLineColor);\r
+ }\r
+\r
+ double srcLineWidth = s.getLineWidth();\r
+ double tgtLineWidth = getLineWidth();\r
+ if(srcLineWidth != tgtLineWidth) {\r
+ setLineWidth(srcLineWidth);\r
+ }\r
+\r
+ LineDash srcLineDash = s.getLineDash();\r
+ LineDash tgtLineDash = getLineDash();\r
+ if(srcLineDash != null && srcLineDash != tgtLineDash) {\r
+ setLineDash(srcLineDash);\r
+ }\r
+\r
+ LineCap srcLineCap = s.getLineCap();\r
+ LineCap tgtLineCap = getLineCap();\r
+ if(srcLineCap != null && srcLineCap != tgtLineCap) {\r
+ setLineCap(srcLineCap);\r
+ }\r
+\r
+ }\r
}\r
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;
}
+ @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;
+ }
+
}
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraphProperties;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextSpacing;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAlignType;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextFont;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextCharBullet;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBulletSizePoint;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextLineBreak;\r
import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder;\r
import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType;\r
\r
private final XSLFTextShape _shape;\r
private List<TextFragment> _lines;\r
private TextFragment _bullet;\r
+ /**\r
+ * the highest line in this paragraph. Used for line spacing.\r
+ */\r
+ private double _maxLineHeight;\r
\r
XSLFTextParagraph(CTTextParagraph p, XSLFTextShape shape){\r
_p = p;\r
_runs = new ArrayList<XSLFTextRun>();\r
_shape = shape;\r
\r
- for (CTRegularTextRun r : _p.getRList()) {\r
- _runs.add(new XSLFTextRun(r, this));\r
- }\r
-\r
- for (CTTextField f : _p.getFldList()) {\r
- CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();\r
- r.setT(f.getT());\r
- _runs.add(new XSLFTextRun(r, this));\r
+ for(XmlObject ch : _p.selectPath("*")){\r
+ if(ch instanceof CTRegularTextRun){\r
+ CTRegularTextRun r = (CTRegularTextRun)ch;\r
+ _runs.add(new XSLFTextRun(r, this));\r
+ } else if (ch instanceof CTTextLineBreak){\r
+ CTTextLineBreak br = (CTTextLineBreak)ch;\r
+ CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();\r
+ r.setRPr(br.getRPr());\r
+ r.setT("\n");\r
+ _runs.add(new XSLFTextRun(r, this));\r
+ } else if (ch instanceof CTTextField){\r
+ CTTextField f = (CTTextField)ch;\r
+ CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();\r
+ r.setRPr(f.getRPr());\r
+ r.setT(f.getT());\r
+ _runs.add(new XSLFTextRun(r, this));\r
+ }\r
}\r
}\r
\r
return out.toString();\r
}\r
\r
- private String getVisibleText(){\r
+ String getRenderableText(){\r
StringBuilder out = new StringBuilder();\r
for (XSLFTextRun r : _runs) {\r
- String txt = r.getText();\r
- switch (r.getTextCap()){\r
- case ALL:\r
- txt = txt.toUpperCase();\r
- break;\r
- case SMALL:\r
- txt = txt.toLowerCase();\r
- break;\r
- }\r
- out.append(txt);\r
+ out.append(r.getRenderableText());\r
}\r
return out.toString();\r
}\r
return fetcher.getValue();\r
}\r
\r
+ public void setBulletFont(String typeface){\r
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
+ CTTextFont font = pr.isSetBuFont() ? pr.getBuFont() : pr.addNewBuFont();\r
+ font.setTypeface(typeface);\r
+ }\r
+\r
/**\r
* @return the character to be used in place of the standard bullet point\r
*/\r
return fetcher.getValue();\r
}\r
\r
+ public void setBulletCharacter(String str){\r
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
+ CTTextCharBullet c = pr.isSetBuChar() ? pr.getBuChar() : pr.addNewBuChar();\r
+ c.setChar(str);\r
+ }\r
+\r
public Color getBulletFontColor(){\r
final XSLFTheme theme = getParentShape().getSheet().getTheme();\r
ParagraphPropertyFetcher<Color> fetcher = new ParagraphPropertyFetcher<Color>(getLevel()){\r
return fetcher.getValue();\r
}\r
\r
+ public void setBulletFontColor(Color color){\r
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
+ CTColor c = pr.isSetBuClr() ? pr.getBuClr() : pr.addNewBuClr();\r
+ CTSRgbColor clr = c.isSetSrgbClr() ? c.getSrgbClr() : c.addNewSrgbClr();\r
+ clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});\r
+ }\r
+\r
public double getBulletFontSize(){\r
ParagraphPropertyFetcher<Double> fetcher = new ParagraphPropertyFetcher<Double>(getLevel()){\r
public boolean fetch(CTTextParagraphProperties props){\r
return fetcher.getValue() == null ? 100 : fetcher.getValue();\r
}\r
\r
+ public void setBulletFontSize(double size){\r
+ CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
+ CTTextBulletSizePoint pt = pr.isSetBuSzPts() ? pr.getBuSzPts() : pr.addNewBuSzPts();\r
+ pt.setVal((int)(size*1000));\r
+ if(pr.isSetBuSzPct()) pr.unsetBuSzPct();\r
+ }\r
+\r
/**\r
* Specifies the indent size that will be applied to the first line of text in the paragraph.\r
*\r
- * @param value the indent in points. The value of -1 unsets the indent attribute\r
- * from the underlying xml bean.\r
+ * @param value the indent in points. \r
*/\r
public void setIndent(double value){\r
CTTextParagraphProperties pr = _p.isSetPPr() ? _p.getPPr() : _p.addNewPPr();\r
}\r
};\r
fetchParagraphProperty(fetcher);\r
- return fetcher.getValue() == null ? 0 : fetcher.getValue();\r
+ // if the marL attribute is omitted, then a value of 347663 is implied\r
+ return fetcher.getValue() == null ? Units.toPoints(347663) : fetcher.getValue();\r
}\r
\r
/**\r
\r
/**\r
*\r
- * @param isBullet whether text in this paragraph has bullets\r
+ * @param flag whether text in this paragraph has bullets\r
*/\r
public void setBullet(boolean flag) {\r
if(isBullet() == flag) return;\r
return _lines;\r
}\r
\r
+ /**\r
+ * Returns wrapping width to break lines in this paragraph\r
+ *\r
+ * @param firstLine whether the first line is breaking\r
+ *\r
+ * @return wrapping width in points\r
+ */\r
+ double getWrappingWidth(boolean firstLine){\r
+ // internal margins for the text box\r
+ double leftInset = _shape.getLeftInset();\r
+ double rightInset = _shape.getRightInset();\r
+\r
+ Rectangle2D anchor = _shape.getAnchor();\r
+\r
+ double leftMargin = getLeftMargin();\r
+ double indent = getIndent();\r
+\r
+ double width;\r
+ if(!_shape.getWordWrap()) {\r
+ // if wordWrap == false then we return the advance to the right border of the sheet\r
+ width = _shape.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX();\r
+ } else {\r
+ width = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ if(firstLine) {\r
+ if(isBullet()){\r
+ width -= Math.abs(indent);\r
+ } else {\r
+ if(indent > 0) width -= indent; // first line indentation\r
+ else if (indent < 0) { // hanging indentation: the first line start at the left margin\r
+ width += leftMargin;\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ return width;\r
+ }\r
+\r
public double draw(Graphics2D graphics, double x, double y){\r
- double marginLeft = _shape.getLeftInset();\r
- double marginRight = _shape.getRightInset();\r
+ double leftInset = _shape.getLeftInset();\r
+ double rightInset = _shape.getRightInset();\r
Rectangle2D anchor = _shape.getAnchor();\r
double penY = y;\r
\r
- double textOffset = getLeftMargin();\r
+ double leftMargin = getLeftMargin();\r
boolean firstLine = true;\r
+ double indent = getIndent();\r
for(TextFragment line : _lines){\r
double penX = x;\r
+\r
+ if(firstLine) {\r
+\r
+ if(_bullet != null){\r
+ if(indent < 0) {\r
+ // a negative value means "Hanging" indentation and\r
+ // indicates the position of the actual bullet character.\r
+ // (the bullet is shifted to right relative to the text)\r
+ _bullet.draw(graphics, penX, penY);\r
+ penX -= indent;\r
+ } else if(indent > 0){\r
+ penX += leftMargin;\r
+ // a positive value means the "First Line" indentation:\r
+ // the first line is indented and other lines start at the bullet ofset\r
+ _bullet.draw(graphics, penX, penY);\r
+ penX += indent;\r
+ } else {\r
+ // no special indent. The first line behaves like all others\r
+ penX += leftMargin;\r
+\r
+ // a zero indent means that the bullet and text have the same offset\r
+ _bullet.draw(graphics, penX, penY);\r
+\r
+ // don't let text overlay the bullet and advance by the bullet width\r
+ penX += _bullet._layout.getAdvance() + 1;\r
+ }\r
+ } else {\r
+ if(indent < 0) {\r
+ // if bullet=false and indentation=hanging then the first line\r
+ // starts at the left offset (penX is not incremented)\r
+ } else if(indent > 0) {\r
+ // first line indent shifts penX\r
+ penX += indent + leftMargin;\r
+ } else {\r
+ // no special indent. The first line behaves like all others\r
+ penX += leftMargin;\r
+ }\r
+ }\r
+ } else {\r
+ penX += leftMargin;\r
+ }\r
+\r
+\r
switch (getTextAlign()) {\r
case CENTER:\r
- penX += textOffset + (anchor.getWidth() - textOffset - line.getWidth() - marginLeft - marginRight) / 2;\r
+ penX += (anchor.getWidth() - leftMargin - line.getWidth() - leftInset - rightInset) / 2;\r
break;\r
case RIGHT:\r
- penX += (anchor.getWidth() - line.getWidth() - marginLeft - marginRight);\r
+ penX += (anchor.getWidth() - line.getWidth() - leftInset - rightInset);\r
break;\r
default:\r
- penX = x + textOffset;\r
+ //penX += leftInset;\r
break;\r
}\r
\r
- if(_bullet != null && firstLine){\r
- _bullet.draw(graphics, penX + getIndent(), penY);\r
- }\r
line.draw(graphics, penX, penY);\r
\r
//The vertical line spacing\r
double spacing = getLineSpacing();\r
if(spacing > 0) {\r
// If linespacing >= 0, then linespacing is a percentage of normal line height.\r
- penY += spacing*0.01*line.getHeight();\r
+ penY += spacing*0.01* _maxLineHeight;\r
} else {\r
// positive value means absolute spacing in points\r
penY += -spacing;\r
}\r
\r
AttributedString getAttributedString(Graphics2D graphics){\r
- String text = getVisibleText();\r
+ String text = getRenderableText();\r
\r
AttributedString string = new AttributedString(text);\r
\r
int startIndex = 0;\r
for (XSLFTextRun run : _runs){\r
- int length = run.getText().length();\r
+ int length = run.getRenderableText().length();\r
if(length == 0) {\r
// skip empty runs\r
continue;\r
void breakText(Graphics2D graphics){\r
_lines = new ArrayList<TextFragment>();\r
\r
+ String text = getRenderableText();\r
AttributedString at = getAttributedString(graphics);\r
AttributedCharacterIterator it = at.getIterator();\r
if(it.getBeginIndex() == it.getEndIndex()) {\r
LineBreakMeasurer measurer = new LineBreakMeasurer(it, graphics.getFontRenderContext());\r
for (;;) {\r
int startIndex = measurer.getPosition();\r
- double wrappingWidth = getWrappingWidth() + 1; // add a pixel to compensate rounding errors\r
- TextLayout layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), true);\r
+ double wrappingWidth = getWrappingWidth(_lines.size() == 0) + 1; // add a pixel to compensate rounding errors\r
+\r
+\r
+ int nextBreak = text.indexOf('\n', startIndex + 1);\r
+ if(nextBreak == -1) nextBreak = it.getEndIndex();\r
+\r
+ TextLayout layout = measurer.nextLayout((float)wrappingWidth, nextBreak, true);\r
if (layout == null) {\r
// layout can be null if the entire word at the current position\r
// does not fit within the wrapping width. Try with requireNextWord=false.\r
- layout = measurer.nextLayout((float)wrappingWidth, it.getEndIndex(), false);\r
+ layout = measurer.nextLayout((float)wrappingWidth, nextBreak, false);\r
}\r
\r
int endIndex = measurer.getPosition();\r
TextFragment line = new TextFragment(layout, str);\r
_lines.add(line);\r
\r
+ _maxLineHeight = Math.max(_maxLineHeight, line.getHeight());\r
+\r
if(endIndex == it.getEndIndex()) break;\r
}\r
\r
\r
}\r
\r
- double getWrappingWidth(){\r
- double width;\r
- if(!_shape.getWordWrap()) {\r
- width = _shape.getSheet().getSlideShow().getPageSize().getWidth();\r
- } else {\r
- width = _shape.getAnchor().getWidth() -\r
- _shape.getLeftInset() - _shape.getRightInset() - getLeftMargin();\r
- }\r
- return width;\r
- }\r
-\r
CTTextParagraphProperties getDefaultStyle(){\r
CTPlaceholder ph = _shape.getCTPlaceholder();\r
String defaultStyleSelector;\r
return ok;\r
}\r
\r
+ void copy(XSLFTextParagraph p){\r
+ TextAlign srcAlign = p.getTextAlign();\r
+ if(srcAlign != getTextAlign()){\r
+ setTextAlign(srcAlign);\r
+ }\r
+\r
+ boolean isBullet = p.isBullet();\r
+ if(isBullet != isBullet()){\r
+ setBullet(isBullet);\r
+ if(isBullet) {\r
+ String buFont = p.getBulletFont();\r
+ if(buFont != null && !buFont.equals(getBulletFont())){\r
+ setBulletFont(buFont);\r
+ }\r
+ String buChar = p.getBulletCharacter();\r
+ if(buChar != null && !buChar.equals(getBulletCharacter())){\r
+ setBulletCharacter(buChar);\r
+ }\r
+ Color buColor = p.getBulletFontColor();\r
+ if(buColor != null && !buColor.equals(getBulletFontColor())){\r
+ setBulletFontColor(buColor);\r
+ }\r
+ double buSize = p.getBulletFontSize();\r
+ if(buSize != getBulletFontSize()){\r
+ setBulletFontSize(buSize);\r
+ }\r
+ }\r
+ }\r
+\r
+ double leftMargin = p.getLeftMargin();\r
+ if(leftMargin != getLeftMargin()){\r
+ setLeftMargin(leftMargin);\r
+ }\r
+\r
+ double indent = p.getIndent();\r
+ if(indent != getIndent()){\r
+ setIndent(indent);\r
+ }\r
+\r
+ double spaceAfter = p.getSpaceAfter();\r
+ if(spaceAfter != getSpaceAfter()){\r
+ setSpaceAfter(spaceAfter);\r
+ }\r
+ double spaceBefore = p.getSpaceBefore();\r
+ if(spaceBefore != getSpaceBefore()){\r
+ setSpaceBefore(spaceBefore);\r
+ }\r
+ double lineSpacing = p.getLineSpacing();\r
+ if(lineSpacing != getLineSpacing()){\r
+ setLineSpacing(lineSpacing);\r
+ }\r
+\r
+ List<XSLFTextRun> srcR = p.getTextRuns();\r
+ List<XSLFTextRun> tgtR = getTextRuns();\r
+ for(int i = 0; i < srcR.size(); i++){\r
+ XSLFTextRun r1 = srcR.get(i);\r
+ XSLFTextRun r2 = tgtR.get(i);\r
+ r2.copy(r1);\r
+ }\r
+ }\r
}\r
return _r.getT();\r
}\r
\r
+ String getRenderableText(){\r
+ String txt = _r.getT();\r
+ switch (getTextCap()){\r
+ case ALL:\r
+ txt = txt.toUpperCase();\r
+ break;\r
+ case SMALL:\r
+ txt = txt.toLowerCase();\r
+ break;\r
+ }\r
+ // TODO-1 is is the place to convert wingdings to unicode\r
+ \r
+ // TODO-2 this is a temporary hack. Rendering text with tabs is not yet supported.\r
+ // for now tabs are replaced with some number of spaces.\r
+ return txt.replace("\t", " ");\r
+ }\r
+\r
public void setText(String text){\r
_r.setT(text);\r
}\r
CTSolidColorFillProperties fill = rPr.isSetSolidFill() ? rPr.getSolidFill() : rPr.addNewSolidFill();\r
CTSRgbColor clr = fill.isSetSrgbClr() ? fill.getSrgbClr() : fill.addNewSrgbClr();\r
clr.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});\r
+\r
+ if(fill.isSetHslClr()) fill.unsetHslClr();\r
+ if(fill.isSetPrstClr()) fill.unsetPrstClr();\r
+ if(fill.isSetSchemeClr()) fill.unsetSchemeClr();\r
+ if(fill.isSetScrgbClr()) fill.unsetScrgbClr();\r
+ if(fill.isSetSysClr()) fill.unsetSysClr();\r
+\r
}\r
\r
public Color getFontColor(){\r
return ok;\r
}\r
\r
+ void copy(XSLFTextRun r){\r
+ String srcFontFamily = r.getFontFamily();\r
+ if(srcFontFamily != null && !srcFontFamily.equals(getFontFamily())){\r
+ setFontFamily(srcFontFamily);\r
+ }\r
+\r
+ Color srcFontColor = r.getFontColor();\r
+ if(srcFontColor != null && !srcFontColor.equals(getFontColor())){\r
+ setFontColor(srcFontColor);\r
+ }\r
+\r
+ double srcFontSize = r.getFontSize();\r
+ if(srcFontSize != getFontSize()){\r
+ setFontSize(srcFontSize);\r
+ }\r
+\r
+ boolean bold = r.isBold();\r
+ if(bold != isBold()) setBold(bold);\r
+\r
+ boolean italic = r.isItalic();\r
+ if(italic != isItalic()) setItalic(italic);\r
+\r
+ boolean underline = r.isUnderline();\r
+ if(underline != isUnderline()) setUnderline(underline);\r
+\r
+ boolean strike = r.isStrikethrough();\r
+ if(strike != isStrikethrough()) setStrikethrough(strike);\r
+ }\r
}\r
}
};
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();
}
/**
}
};
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();
}
/**
}
};
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();
}
/**
}
};
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();
}
/**
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);
+ }
+
+ }
}
\ No newline at end of file
import junit.framework.TestCase;\r
\r
import org.apache.poi.xslf.XSLFTestDataSamples;\r
+import org.apache.poi.openxml4j.opc.PackagePart;\r
+\r
+import java.awt.Color;\r
+import java.util.List;\r
+import java.util.Arrays;\r
+import java.util.regex.Pattern;\r
\r
/**\r
* @author Yegor Kozlov\r
assertTrue(slide.getFollowMasterGraphics());\r
}\r
\r
+ public void testImportContent(){\r
+ XMLSlideShow ppt = new XMLSlideShow();\r
+\r
+ XMLSlideShow src = XSLFTestDataSamples.openSampleDocument("themes.pptx");\r
+\r
+ // create a blank slide and import content from the 4th slide of themes.pptx\r
+ XSLFSlide slide1 = ppt.createSlide().importContent(src.getSlides()[3]);\r
+ XSLFShape[] shapes1 = slide1.getShapes();\r
+ assertEquals(2, shapes1.length);\r
+\r
+ XSLFTextShape sh1 = (XSLFTextShape)shapes1[0];\r
+ assertEquals("Austin Theme", sh1.getText());\r
+ XSLFTextRun r1 = sh1.getTextParagraphs().get(0).getTextRuns().get(0);\r
+ assertEquals("Century Gothic", r1.getFontFamily());\r
+ assertEquals(40.0, r1.getFontSize());\r
+ assertTrue(r1.isBold());\r
+ assertTrue(r1.isItalic());\r
+ assertEquals(new Color(148, 198, 0), r1.getFontColor());\r
+ assertNull(sh1.getFillColor());\r
+ assertNull(sh1.getLineColor());\r
+\r
+ XSLFTextShape sh2 = (XSLFTextShape)shapes1[1];\r
+ assertEquals(\r
+ "Text in a autoshape is white\n" +\r
+ "Fill: RGB(148, 198,0)", sh2.getText());\r
+ XSLFTextRun r2 = sh2.getTextParagraphs().get(0).getTextRuns().get(0);\r
+ assertEquals("Century Gothic", r2.getFontFamily());\r
+ assertEquals(18.0, r2.getFontSize());\r
+ assertFalse(r2.isBold());\r
+ assertFalse(r2.isItalic());\r
+ assertEquals(Color.white, r2.getFontColor());\r
+ assertEquals(new Color(148, 198, 0), sh2.getFillColor());\r
+ assertEquals(new Color(74, 99, 0), sh2.getLineColor()); // slightly different from PowerPoint!\r
+\r
+ // the 5th slide has a picture and a texture fill\r
+ XSLFSlide slide2 = ppt.createSlide().importContent(src.getSlides()[4]);\r
+ XSLFShape[] shapes2 = slide2.getShapes();\r
+ assertEquals(2, shapes2.length);\r
+\r
+ XSLFTextShape sh3 = (XSLFTextShape)shapes2[0];\r
+ assertEquals("This slide overrides master background with a texture fill", sh3.getText());\r
+ XSLFTextRun r3 = sh3.getTextParagraphs().get(0).getTextRuns().get(0);\r
+ assertEquals("Century Gothic", r3.getFontFamily());\r
+ //assertEquals(32.4.0, r3.getFontSize());\r
+ assertTrue(r3.isBold());\r
+ assertTrue(r3.isItalic());\r
+ assertEquals(new Color(148, 198, 0), r3.getFontColor());\r
+ assertNull(sh3.getFillColor());\r
+ assertNull(sh3.getLineColor());\r
+\r
+ XSLFPictureShape sh4 = (XSLFPictureShape)shapes2[1];\r
+ XSLFPictureShape srcPic = (XSLFPictureShape)src.getSlides()[4].getShapes()[1];\r
+ assertTrue(Arrays.equals(sh4.getPictureData().getData(), srcPic.getPictureData().getData()));\r
+ }\r
}
\ No newline at end of file
--- /dev/null
+package org.apache.poi.xslf.usermodel;\r
+\r
+import junit.framework.TestCase;\r
+\r
+import java.awt.Rectangle;\r
+import java.awt.Color;\r
+import java.awt.geom.Rectangle2D;\r
+import java.io.FileOutputStream;\r
+\r
+import org.apache.poi.xssf.dev.XSSFDump;\r
+import org.apache.poi.xslf.util.PPTX2PNG;\r
+\r
+/**\r
+ * Created by IntelliJ IDEA.\r
+ * User: yegor\r
+ * Date: Nov 10, 2011\r
+ * Time: 1:43:25 PM\r
+ * To change this template use File | Settings | File Templates.\r
+ */\r
+public class TestXSLFTextParagraph extends TestCase {\r
+\r
+ public void testWrappingWidth() throws Exception {\r
+ XMLSlideShow ppt = new XMLSlideShow();\r
+ XSLFSlide slide = ppt.createSlide();\r
+ XSLFTextShape sh = slide.createAutoShape();\r
+ sh.setLineColor(Color.black);\r
+\r
+ XSLFTextParagraph p = sh.addNewTextParagraph();\r
+ p.addNewTextRun().setText(\r
+ "Paragraph formatting allows for more granular control " +\r
+ "of text within a shape. Properties here apply to all text " +\r
+ "residing within the corresponding paragraph.");\r
+\r
+ Rectangle2D anchor = new Rectangle(50, 50, 300, 200);\r
+ sh.setAnchor(anchor);\r
+\r
+ double leftInset = sh.getLeftInset();\r
+ double rightInset = sh.getRightInset();\r
+ assertEquals(7.2, leftInset);\r
+ assertEquals(7.2, rightInset);\r
+\r
+ double leftMargin = p.getLeftMargin();\r
+ assertEquals(0.0, leftMargin);\r
+\r
+ double indent = p.getIndent();\r
+ assertEquals(0.0, indent); // default\r
+\r
+ double expectedWidth;\r
+\r
+ // Case 1: bullet=false, leftMargin=0, indent=0.\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ assertEquals(285.6, expectedWidth); // 300 - 7.2 - 7.2 - 0\r
+ assertEquals(expectedWidth, p.getWrappingWidth(true));\r
+ assertEquals(expectedWidth, p.getWrappingWidth(false));\r
+\r
+ p.setLeftMargin(36); // 0.5"\r
+ leftMargin = p.getLeftMargin();\r
+ assertEquals(36.0, leftMargin);\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ assertEquals(249.6, expectedWidth, 1E-5); // 300 - 7.2 - 7.2 - 36\r
+ assertEquals(expectedWidth, p.getWrappingWidth(true));\r
+ assertEquals(expectedWidth, p.getWrappingWidth(false));\r
+\r
+ // increase insets, the wrapping width should get smaller\r
+ sh.setLeftInset(10);\r
+ sh.setRightInset(10);\r
+ leftInset = sh.getLeftInset();\r
+ rightInset = sh.getRightInset();\r
+ assertEquals(10.0, leftInset);\r
+ assertEquals(10.0, rightInset);\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36\r
+ assertEquals(expectedWidth, p.getWrappingWidth(true));\r
+ assertEquals(expectedWidth, p.getWrappingWidth(false));\r
+\r
+ // set a positive indent of a 0.5 inch. This means "First Line" indentation:\r
+ // |<--- indent -->|Here goes first line of the text\r
+ // Here go other lines (second and subsequent)\r
+\r
+ p.setIndent(36.0); // 0.5"\r
+ indent = p.getIndent();\r
+ assertEquals(36.0, indent);\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin - indent;\r
+ assertEquals(208.0, expectedWidth); // 300 - 10 - 10 - 36 - 6.4\r
+ assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is indented\r
+ // other lines are not indented\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36\r
+ assertEquals(expectedWidth, p.getWrappingWidth(false));\r
+\r
+ // set a negative indent of a 1 inch. This means "Hanging" indentation:\r
+ // Here goes first line of the text\r
+ // |<--- indent -->|Here go other lines (second and subsequent)\r
+ p.setIndent(-72.0); // 1"\r
+ indent = p.getIndent();\r
+ assertEquals(-72.0, indent);\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset;\r
+ assertEquals(280.0, expectedWidth); // 300 - 10 - 10 \r
+ assertEquals(expectedWidth, p.getWrappingWidth(true)); // first line is NOT indented\r
+ // other lines are indented by leftMargin (the value of indent is not used)\r
+ expectedWidth = anchor.getWidth() - leftInset - rightInset - leftMargin;\r
+ assertEquals(244.0, expectedWidth); // 300 - 10 - 10 - 36 \r
+ assertEquals(expectedWidth, p.getWrappingWidth(false));\r
+ }\r
+}\r