improved rendering of indented text in XSLF, also improved import content from external slides

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1200330 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yegor Kozlov 2011-11-10 13:59:16 +00:00
parent b0a7faba08
commit dd654d9870
15 changed files with 695 additions and 84 deletions

View File

@ -17,11 +17,13 @@
package org.apache.poi.xslf.usermodel;
/**
*
*
* @author Yegor Kozlov
*/
public enum LineCap {
/**
* Rounded ends - the default
* Rounded ends
*/
ROUND,
/**

View File

@ -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());
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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());
}
}

View File

@ -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();
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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()));
}
}

View File

@ -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));
}
}

Binary file not shown.