git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1746856 13f79535-47bb-0310-9956-ffa450edef68tags/REL_3_15_BETA2
@@ -56,6 +56,14 @@ public class DrawFactory { | |||
defaultFactory.set(factory); | |||
} | |||
/** | |||
* Returns the DrawFactory, preferably via a graphics instance. | |||
* If graphics is null, the current thread local is checked or | |||
* if it is not set, a new factory is created. | |||
* | |||
* @param graphics the current graphics context or null | |||
* @return the draw factory | |||
*/ | |||
public static DrawFactory getInstance(Graphics2D graphics) { | |||
// first try to find the factory over the rendering hint | |||
DrawFactory factory = null; |
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.sl.draw; | |||
import java.awt.BasicStroke; | |||
import java.awt.Graphics2D; | |||
import java.awt.geom.AffineTransform; | |||
import java.awt.geom.Rectangle2D; | |||
@@ -24,6 +25,9 @@ import java.util.Locale; | |||
import org.apache.poi.sl.usermodel.PlaceableShape; | |||
import org.apache.poi.sl.usermodel.Shape; | |||
import org.apache.poi.sl.usermodel.StrokeStyle; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineCap; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineDash; | |||
public class DrawShape implements Drawable { | |||
@@ -157,4 +161,44 @@ public class DrawShape implements Drawable { | |||
protected Shape<?,?> getShape() { | |||
return shape; | |||
} | |||
protected static BasicStroke getStroke(StrokeStyle strokeStyle) { | |||
float lineWidth = (float) strokeStyle.getLineWidth(); | |||
if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt | |||
LineDash lineDash = strokeStyle.getLineDash(); | |||
if (lineDash == null) { | |||
lineDash = LineDash.SOLID; | |||
} | |||
int dashPatI[] = lineDash.pattern; | |||
final float dash_phase = 0; | |||
float[] dashPatF = null; | |||
if (dashPatI != null) { | |||
dashPatF = new float[dashPatI.length]; | |||
for (int i=0; i<dashPatI.length; i++) { | |||
dashPatF[i] = dashPatI[i]*Math.max(1, lineWidth); | |||
} | |||
} | |||
LineCap lineCapE = strokeStyle.getLineCap(); | |||
if (lineCapE == null) lineCapE = LineCap.FLAT; | |||
int lineCap; | |||
switch (lineCapE) { | |||
case ROUND: | |||
lineCap = BasicStroke.CAP_ROUND; | |||
break; | |||
case SQUARE: | |||
lineCap = BasicStroke.CAP_SQUARE; | |||
break; | |||
default: | |||
case FLAT: | |||
lineCap = BasicStroke.CAP_BUTT; | |||
break; | |||
} | |||
int lineJoin = BasicStroke.JOIN_ROUND; | |||
return new BasicStroke(lineWidth, lineCap, lineJoin, lineWidth, dashPatF, dash_phase); | |||
} | |||
} |
@@ -53,9 +53,6 @@ import org.apache.poi.sl.usermodel.LineDecoration.DecorationSize; | |||
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint; | |||
import org.apache.poi.sl.usermodel.Shadow; | |||
import org.apache.poi.sl.usermodel.SimpleShape; | |||
import org.apache.poi.sl.usermodel.StrokeStyle; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineCap; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineDash; | |||
import org.apache.poi.util.Units; | |||
@@ -282,45 +279,7 @@ public class DrawSimpleShape extends DrawShape { | |||
} | |||
public BasicStroke getStroke() { | |||
StrokeStyle strokeStyle = getShape().getStrokeStyle(); | |||
float lineWidth = (float) strokeStyle.getLineWidth(); | |||
if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt | |||
LineDash lineDash = strokeStyle.getLineDash(); | |||
if (lineDash == null) { | |||
lineDash = LineDash.SOLID; | |||
} | |||
int dashPatI[] = lineDash.pattern; | |||
final float dash_phase = 0; | |||
float[] dashPatF = null; | |||
if (dashPatI != null) { | |||
dashPatF = new float[dashPatI.length]; | |||
for (int i=0; i<dashPatI.length; i++) { | |||
dashPatF[i] = dashPatI[i]*Math.max(1, lineWidth); | |||
} | |||
} | |||
LineCap lineCapE = strokeStyle.getLineCap(); | |||
if (lineCapE == null) lineCapE = LineCap.FLAT; | |||
int lineCap; | |||
switch (lineCapE) { | |||
case ROUND: | |||
lineCap = BasicStroke.CAP_ROUND; | |||
break; | |||
case SQUARE: | |||
lineCap = BasicStroke.CAP_SQUARE; | |||
break; | |||
default: | |||
case FLAT: | |||
lineCap = BasicStroke.CAP_BUTT; | |||
break; | |||
} | |||
int lineJoin = BasicStroke.JOIN_ROUND; | |||
return new BasicStroke(lineWidth, lineCap, lineJoin, lineWidth, dashPatF, dash_phase); | |||
return getStroke(getShape().getStrokeStyle()); | |||
} | |||
protected void drawShadow( |
@@ -19,21 +19,31 @@ package org.apache.poi.sl.draw; | |||
import java.awt.Color; | |||
import java.awt.Graphics2D; | |||
import java.awt.Paint; | |||
import java.awt.geom.Line2D; | |||
import java.awt.geom.Rectangle2D; | |||
import org.apache.poi.sl.usermodel.GroupShape; | |||
import org.apache.poi.sl.usermodel.StrokeStyle; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound; | |||
import org.apache.poi.sl.usermodel.StrokeStyle.LineDash; | |||
import org.apache.poi.sl.usermodel.TableCell; | |||
import org.apache.poi.sl.usermodel.TableCell.BorderEdge; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.sl.usermodel.TableShape; | |||
public class DrawTableShape extends DrawShape { | |||
// to be implemented ... | |||
/** | |||
* Additional spacing between cells | |||
*/ | |||
@Internal | |||
public static final int borderSize = 2; | |||
public DrawTableShape(TableShape<?,?> shape) { | |||
super(shape); | |||
} | |||
protected Drawable getDrawable(Graphics2D graphics) { | |||
protected Drawable getGroupShape(Graphics2D graphics) { | |||
if (shape instanceof GroupShape) { | |||
DrawFactory df = DrawFactory.getInstance(graphics); | |||
return df.getDrawable((GroupShape<?,?>)shape); | |||
@@ -42,31 +52,102 @@ public class DrawTableShape extends DrawShape { | |||
} | |||
public void applyTransform(Graphics2D graphics) { | |||
Drawable d = getDrawable(graphics); | |||
Drawable d = getGroupShape(graphics); | |||
if (d != null) { | |||
d.applyTransform(graphics); | |||
} else { | |||
super.applyTransform(graphics); | |||
} | |||
} | |||
public void draw(Graphics2D graphics) { | |||
Drawable d = getDrawable(graphics); | |||
Drawable d = getGroupShape(graphics); | |||
if (d != null) { | |||
d.draw(graphics); | |||
return; | |||
} | |||
TableShape<?,?> ts = getShape(); | |||
DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(ts); | |||
final int rows = ts.getNumberOfRows(); | |||
final int cols = ts.getNumberOfColumns(); | |||
// draw background boxes | |||
for (int row=0; row<rows; row++) { | |||
for (int col=0; col<cols; col++) { | |||
TableCell<?,?> tc = ts.getCell(row, col); | |||
if (tc == null || tc.isMerged()) { | |||
continue; | |||
} | |||
Paint fillPaint = drawPaint.getPaint(graphics, tc.getFillStyle().getPaint()); | |||
graphics.setPaint(fillPaint); | |||
Rectangle2D cellAnc = tc.getAnchor(); | |||
graphics.fill(cellAnc); | |||
for (BorderEdge edge : BorderEdge.values()) { | |||
StrokeStyle stroke = tc.getBorderStyle(edge); | |||
if (stroke == null) { | |||
continue; | |||
} | |||
graphics.setStroke(getStroke(stroke)); | |||
Paint linePaint = drawPaint.getPaint(graphics, stroke.getPaint()); | |||
graphics.setPaint(linePaint); | |||
double x=cellAnc.getX(), y=cellAnc.getY(), w=cellAnc.getWidth(), h=cellAnc.getHeight(); | |||
Line2D line; | |||
switch (edge) { | |||
default: | |||
case bottom: | |||
line = new Line2D.Double(x-borderSize, y+h, x+w+borderSize, y+h); | |||
break; | |||
case left: | |||
line = new Line2D.Double(x, y, x, y+h+borderSize); | |||
break; | |||
case right: | |||
line = new Line2D.Double(x+w, y, x+w, y+h+borderSize); | |||
break; | |||
case top: | |||
line = new Line2D.Double(x-borderSize, y, x+w+borderSize, y); | |||
break; | |||
} | |||
graphics.draw(line); | |||
} | |||
} | |||
} | |||
// draw text | |||
drawContent(graphics); | |||
} | |||
public void drawContent(Graphics2D graphics) { | |||
Drawable d = getDrawable(graphics); | |||
Drawable d = getGroupShape(graphics); | |||
if (d != null) { | |||
d.drawContent(graphics); | |||
return; | |||
} | |||
TableShape<?,?> ts = getShape(); | |||
DrawFactory df = DrawFactory.getInstance(graphics); | |||
final int rows = ts.getNumberOfRows(); | |||
final int cols = ts.getNumberOfColumns(); | |||
for (int row=0; row<rows; row++) { | |||
for (int col=0; col<cols; col++) { | |||
TableCell<?,?> tc = ts.getCell(row, col); | |||
DrawTextShape dts = df.getDrawable(tc); | |||
dts.drawContent(graphics); | |||
} | |||
} | |||
} | |||
@Override | |||
protected TableShape<?,?> getShape() { | |||
return (TableShape<?,?>)shape; | |||
} | |||
} | |||
/** | |||
* Format the table and apply the specified Line to all cell boundaries, | |||
* both outside and inside. | |||
@@ -79,7 +160,7 @@ public class DrawTableShape extends DrawShape { | |||
TableShape<?,?> table = getShape(); | |||
final int rows = table.getNumberOfRows(); | |||
final int cols = table.getNumberOfColumns(); | |||
BorderEdge edges[] = { BorderEdge.top, BorderEdge.left, null, null }; | |||
for (int row = 0; row < rows; row++) { | |||
for (int col = 0; col < cols; col++) { | |||
@@ -99,11 +180,11 @@ public class DrawTableShape extends DrawShape { | |||
*/ | |||
public void setOutsideBorders(Object... args){ | |||
if (args.length == 0) return; | |||
TableShape<?,?> table = getShape(); | |||
final int rows = table.getNumberOfRows(); | |||
final int cols = table.getNumberOfColumns(); | |||
BorderEdge edges[] = new BorderEdge[4]; | |||
for (int row = 0; row < rows; row++) { | |||
for (int col = 0; col < cols; col++) { | |||
@@ -125,11 +206,11 @@ public class DrawTableShape extends DrawShape { | |||
*/ | |||
public void setInsideBorders(Object... args) { | |||
if (args.length == 0) return; | |||
TableShape<?,?> table = getShape(); | |||
final int rows = table.getNumberOfRows(); | |||
final int cols = table.getNumberOfColumns(); | |||
BorderEdge edges[] = new BorderEdge[2]; | |||
for (int row = 0; row < rows; row++) { | |||
for (int col = 0; col < cols; col++) { | |||
@@ -139,7 +220,7 @@ public class DrawTableShape extends DrawShape { | |||
} | |||
} | |||
} | |||
/** | |||
* Apply the border attributes (args) to the given cell and edges | |||
* | |||
@@ -168,5 +249,4 @@ public class DrawTableShape extends DrawShape { | |||
} | |||
} | |||
} | |||
} |
@@ -17,6 +17,7 @@ | |||
package org.apache.poi.sl.draw; | |||
import java.awt.Dimension; | |||
import java.awt.Graphics2D; | |||
import java.awt.Paint; | |||
import java.awt.font.FontRenderContext; | |||
@@ -45,9 +46,11 @@ import org.apache.poi.sl.usermodel.TextParagraph.TextAlign; | |||
import org.apache.poi.sl.usermodel.TextRun; | |||
import org.apache.poi.sl.usermodel.TextRun.TextCap; | |||
import org.apache.poi.sl.usermodel.TextShape; | |||
import org.apache.poi.sl.usermodel.TextShape.TextDirection; | |||
import org.apache.poi.util.StringUtil; | |||
import org.apache.poi.util.Units; | |||
public class DrawTextParagraph implements Drawable { | |||
/** Keys for passing hyperlinks to the graphics context */ | |||
public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href"); | |||
@@ -390,14 +393,13 @@ public class DrawTextParagraph implements Drawable { | |||
* @return wrapping width in points | |||
*/ | |||
protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){ | |||
// internal margins for the text box | |||
TextShape<?,?> ts = paragraph.getParentShape(); | |||
Insets2D insets = paragraph.getParentShape().getInsets(); | |||
// internal margins for the text box | |||
Insets2D insets = ts.getInsets(); | |||
double leftInset = insets.left; | |||
double rightInset = insets.right; | |||
Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape()); | |||
int indentLevel = paragraph.getIndentLevel(); | |||
if (indentLevel == -1) { | |||
// default to 0, if indentLevel is not set | |||
@@ -417,13 +419,33 @@ public class DrawTextParagraph implements Drawable { | |||
rightMargin = 0d; | |||
} | |||
Rectangle2D anchor = DrawShape.getAnchor(graphics, ts); | |||
TextDirection textDir = ts.getTextDirection(); | |||
double width; | |||
TextShape<?,?> ts = paragraph.getParentShape(); | |||
if (!ts.getWordWrap()) { | |||
// if wordWrap == false then we return the advance to the right border of the sheet | |||
width = ts.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX(); | |||
Dimension pageDim = ts.getSheet().getSlideShow().getPageSize(); | |||
// if wordWrap == false then we return the advance to the (right) border of the sheet | |||
switch (textDir) { | |||
default: | |||
width = pageDim.getWidth() - anchor.getX(); | |||
break; | |||
case VERTICAL: | |||
width = pageDim.getHeight() - anchor.getX(); | |||
break; | |||
case VERTICAL_270: | |||
width = anchor.getX(); | |||
break; | |||
} | |||
} else { | |||
width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin; | |||
switch (textDir) { | |||
default: | |||
width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin; | |||
break; | |||
case VERTICAL: | |||
case VERTICAL_270: | |||
width = anchor.getHeight() - leftInset - rightInset - leftMargin - rightMargin; | |||
break; | |||
} | |||
if (firstLine && !isHSLF()) { | |||
if (bullet != null){ | |||
if (indent > 0) width -= indent; |
@@ -30,6 +30,7 @@ import org.apache.poi.sl.usermodel.TextParagraph; | |||
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle; | |||
import org.apache.poi.sl.usermodel.TextRun; | |||
import org.apache.poi.sl.usermodel.TextShape; | |||
import org.apache.poi.sl.usermodel.TextShape.TextDirection; | |||
public class DrawTextShape extends DrawSimpleShape { | |||
@@ -50,7 +51,7 @@ public class DrawTextShape extends DrawSimpleShape { | |||
// remember the initial transform | |||
AffineTransform tx = graphics.getTransform(); | |||
// Transform of text in flipped shapes is special. | |||
// At this point the flip and rotation transform is already applied | |||
// (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down". | |||
@@ -73,7 +74,7 @@ public class DrawTextShape extends DrawSimpleShape { | |||
graphics.scale(-1, 1); | |||
graphics.translate(-anchor.getX(), -anchor.getY()); | |||
} | |||
Double textRot = s.getTextRotation(); | |||
if (textRot != null && textRot != 0) { | |||
graphics.translate(anchor.getCenterX(), anchor.getCenterY()); | |||
@@ -98,6 +99,20 @@ public class DrawTextShape extends DrawSimpleShape { | |||
break; | |||
} | |||
TextDirection textDir = s.getTextDirection(); | |||
if (textDir == TextDirection.VERTICAL || textDir == TextDirection.VERTICAL_270) { | |||
double deg = (textDir == TextDirection.VERTICAL) ? 90 : 270; | |||
graphics.translate(anchor.getCenterX(), anchor.getCenterY()); | |||
graphics.rotate(Math.toRadians(deg)); | |||
graphics.translate(-anchor.getCenterX(), -anchor.getCenterY()); | |||
// old top/left edge is now bottom/left or top/right - as we operate on the already | |||
// rotated drawing context, both verticals can be moved in the same direction | |||
double w = anchor.getWidth(); | |||
double h = anchor.getHeight(); | |||
graphics.translate((w-h)/2d,(h-w)/2d); | |||
} | |||
drawParagraphs(graphics, x, y); | |||
// restore the transform |
@@ -83,4 +83,32 @@ public interface TableCell< | |||
* @param edge the border edge to be cleared | |||
*/ | |||
void removeBorder(BorderEdge edge); | |||
/** | |||
* Get the number of columns to be spanned/merged | |||
* | |||
* @return the grid span | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
int getGridSpan(); | |||
/** | |||
* Get the number of rows to be spanned/merged | |||
* | |||
* @return the row span | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
int getRowSpan(); | |||
/** | |||
* Return if this cell is part of a merged cell. The top/left cell of a merged region is not regarded as merged - | |||
* its grid and/or row span is greater than one. | |||
* | |||
* @return true if this a merged cell | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
boolean isMerged(); | |||
} |
@@ -29,6 +29,7 @@ import org.apache.poi.util.POILogFactory; | |||
import org.apache.poi.util.POILogger; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTFontReference; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTHslColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveFixedPercentage; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor; | |||
@@ -165,6 +166,8 @@ public class XSLFColor { | |||
color = Color.black; | |||
} | |||
} | |||
} else if (ch instanceof CTFontReference) { | |||
// try next ... | |||
} else { | |||
throw new IllegalArgumentException("Unexpected color choice: " + ch.getClass()); | |||
} |
@@ -65,8 +65,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
/** | |||
* Base super-class class for all shapes in PresentationML | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
@Beta | |||
public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
@@ -150,6 +148,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
} | |||
protected PaintStyle getFillPaint() { | |||
final XSLFTheme theme = getSheet().getTheme(); | |||
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() { | |||
public boolean fetch(XSLFShape shape) { | |||
XmlObject pr = null; | |||
@@ -178,7 +177,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
PaintStyle paint = null; | |||
PackagePart pp = getSheet().getPackagePart(); | |||
for (XmlObject obj : pr.selectPath("*")) { | |||
paint = selectPaint(obj, null, pp); | |||
paint = selectPaint(obj, null, pp, theme); | |||
if (paint != null) { | |||
setValue(paint); | |||
return true; | |||
@@ -203,7 +202,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
if (fillRef == null) { | |||
fillRef = getBgRef(); | |||
} | |||
paint = selectPaint(fillRef); | |||
paint = selectPaint(fillRef, theme); | |||
return paint; | |||
} | |||
@@ -365,9 +364,11 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
protected PaintStyle getPaint(XmlObject spPr, CTSchemeColor phClr) { | |||
PaintStyle paint = null; | |||
PackagePart pp = getSheet().getPackagePart(); | |||
XSLFSheet sheet = getSheet(); | |||
PackagePart pp = sheet.getPackagePart(); | |||
XSLFTheme theme = sheet.getTheme(); | |||
for (XmlObject obj : spPr.selectPath("*")) { | |||
paint = selectPaint(obj, phClr, pp); | |||
paint = selectPaint(obj, phClr, pp, theme); | |||
if(paint != null) break; | |||
} | |||
return paint; | |||
@@ -392,24 +393,23 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
* | |||
* @return the applied Paint or null if none was applied | |||
*/ | |||
protected PaintStyle selectPaint(XmlObject obj, final CTSchemeColor phClr, final PackagePart parentPart) { | |||
protected static PaintStyle selectPaint(XmlObject obj, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme) { | |||
if (obj instanceof CTNoFillProperties) { | |||
return null; | |||
} else if (obj instanceof CTSolidColorFillProperties) { | |||
return selectPaint((CTSolidColorFillProperties)obj, phClr); | |||
return selectPaint((CTSolidColorFillProperties)obj, phClr, theme); | |||
} else if (obj instanceof CTBlipFillProperties) { | |||
return selectPaint((CTBlipFillProperties)obj, parentPart); | |||
} else if (obj instanceof CTGradientFillProperties) { | |||
return selectPaint((CTGradientFillProperties) obj, phClr); | |||
return selectPaint((CTGradientFillProperties) obj, phClr, theme); | |||
} else if (obj instanceof CTStyleMatrixReference) { | |||
return selectPaint((CTStyleMatrixReference)obj); | |||
return selectPaint((CTStyleMatrixReference)obj, theme); | |||
} else { | |||
return null; | |||
} | |||
} | |||
protected PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr) { | |||
final XSLFTheme theme = getSheet().getTheme(); | |||
protected static PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr, final XSLFTheme theme) { | |||
if (phClr == null && solidFill.isSetSchemeClr()) { | |||
phClr = solidFill.getSchemeClr(); | |||
} | |||
@@ -417,7 +417,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
return DrawPaint.createSolidPaint(c.getColorStyle()); | |||
} | |||
protected PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) { | |||
protected static PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) { | |||
final CTBlip blip = blipFill.getBlip(); | |||
return new TexturePaint() { | |||
private PackagePart getPart() { | |||
@@ -451,7 +451,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
}; | |||
} | |||
protected PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr) { | |||
protected static PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) { | |||
final CTGradientStop[] gs = gradFill.getGsLst().getGsArray(); | |||
@@ -465,7 +465,6 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
final ColorStyle cs[] = new ColorStyle[gs.length]; | |||
final float fractions[] = new float[gs.length]; | |||
XSLFTheme theme = getSheet().getTheme(); | |||
int i=0; | |||
for (CTGradientStop cgs : gs) { | |||
@@ -519,7 +518,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
}; | |||
} | |||
protected PaintStyle selectPaint(CTStyleMatrixReference fillRef) { | |||
protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme) { | |||
if (fillRef == null) return null; | |||
// The idx attribute refers to the index of a fill style or | |||
@@ -529,8 +528,6 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
// values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element. | |||
int idx = (int)fillRef.getIdx(); | |||
CTSchemeColor phClr = fillRef.getSchemeClr(); | |||
XSLFSheet sheet = getSheet(); | |||
XSLFTheme theme = sheet.getTheme(); | |||
XmlObject fillProps = null; | |||
CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme(); | |||
if (idx >= 1 && idx <= 999) { | |||
@@ -538,7 +535,7 @@ public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> { | |||
} else if (idx >= 1001 ){ | |||
fillProps = matrix.getBgFillStyleLst().selectPath("*")[idx - 1001]; | |||
} | |||
return (fillProps == null) ? null : selectPaint(fillProps, phClr, theme.getPackagePart()); | |||
return (fillProps == null) ? null : selectPaint(fillProps, phClr, theme.getPackagePart(), theme); | |||
} | |||
@Override |
@@ -77,8 +77,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
/** | |||
* Represents a single (non-group) shape in a .pptx slide show | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
@Beta | |||
public abstract class XSLFSimpleShape extends XSLFShape | |||
@@ -259,6 +257,7 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
} | |||
protected PaintStyle getLinePaint() { | |||
final XSLFTheme theme = getSheet().getTheme(); | |||
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() { | |||
public boolean fetch(XSLFShape shape) { | |||
CTLineProperties spPr = shape.getSpPr().getLn(); | |||
@@ -271,7 +270,7 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
PaintStyle paint = null; | |||
PackagePart pp = getSheet().getPackagePart(); | |||
for (XmlObject obj : spPr.selectPath("*")) { | |||
paint = selectPaint(obj, null, pp); | |||
paint = selectPaint(obj, null, pp, theme); | |||
if (paint != null) { | |||
setValue(paint); | |||
return true; | |||
@@ -280,7 +279,7 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
CTShapeStyle style = shape.getSpStyle(); | |||
if (style != null) { | |||
paint = selectPaint(style.getLnRef()); | |||
paint = selectPaint(style.getLnRef(), theme); | |||
if (paint != null) { | |||
setValue(paint); | |||
return true; | |||
@@ -305,7 +304,6 @@ public abstract class XSLFSimpleShape extends XSLFShape | |||
int idx = (int)lnRef.getIdx(); | |||
CTSchemeColor phClr = lnRef.getSchemeClr(); | |||
if(idx > 0){ | |||
XSLFTheme theme = getSheet().getTheme(); | |||
XmlObject lnProps = theme.getXmlObject(). | |||
getThemeElements().getFmtScheme().getLnStyleLst().selectPath("*")[idx - 1]; | |||
paint = getPaint(lnProps, phClr); |
@@ -21,6 +21,7 @@ package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import java.awt.geom.Rectangle2D; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Iterator; | |||
@@ -29,6 +30,9 @@ import java.util.List; | |||
import javax.xml.namespace.QName; | |||
import org.apache.poi.POIXMLException; | |||
import org.apache.poi.sl.draw.DrawFactory; | |||
import org.apache.poi.sl.draw.DrawTableShape; | |||
import org.apache.poi.sl.draw.DrawTextShape; | |||
import org.apache.poi.sl.usermodel.TableShape; | |||
import org.apache.poi.util.Internal; | |||
import org.apache.poi.util.Units; | |||
@@ -45,12 +49,10 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFra | |||
/** | |||
* Represents a table in a .pptx presentation | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>, | |||
TableShape<XSLFShape,XSLFTextParagraph> { | |||
static String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table"; | |||
/* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table"; | |||
private CTTable _table; | |||
private List<XSLFTableRow> _rows; | |||
@@ -77,7 +79,11 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow | |||
_table = (CTTable) rs[0]; | |||
CTTableRow[] trArray = _table.getTrArray(); | |||
_rows = new ArrayList<XSLFTableRow>(trArray.length); | |||
for(CTTableRow row : trArray) _rows.add(new XSLFTableRow(row, this)); | |||
for(CTTableRow row : trArray) { | |||
XSLFTableRow xr = new XSLFTableRow(row, this); | |||
_rows.add(xr); | |||
} | |||
updateRowColIndexes(); | |||
} | |||
@Override | |||
@@ -146,8 +152,10 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow | |||
public XSLFTableRow addRow(){ | |||
CTTableRow tr = _table.addNewTr(); | |||
XSLFTableRow row = new XSLFTableRow(tr, this); | |||
row.setHeight(20.0); // default height is 20 points | |||
// default height is 20 points | |||
row.setHeight(20.0); | |||
_rows.add(row); | |||
updateRowColIndexes(); | |||
return row; | |||
} | |||
@@ -224,4 +232,110 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow | |||
} | |||
} | |||
} | |||
/** | |||
* Get assigned TableStyle | |||
* | |||
* @return the assigned TableStyle | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected XSLFTableStyle getTableStyle() { | |||
CTTable tab = getCTTable(); | |||
// TODO: support inline table style | |||
if (!tab.isSetTblPr() || !tab.getTblPr().isSetTableStyleId()) { | |||
return null; | |||
} | |||
String styleId = tab.getTblPr().getTableStyleId(); | |||
XSLFTableStyles styles = getSheet().getSlideShow().getTableStyles(); | |||
for (XSLFTableStyle style : styles.getStyles()) { | |||
if (style.getStyleId().equals(styleId)) { | |||
return style; | |||
} | |||
} | |||
return null; | |||
} | |||
/* package */ void updateRowColIndexes() { | |||
int rowIdx = 0; | |||
for (XSLFTableRow xr : this) { | |||
int colIdx = 0; | |||
for (XSLFTableCell tc : xr) { | |||
tc.setRowColIndex(rowIdx, colIdx); | |||
colIdx++; | |||
} | |||
rowIdx++; | |||
} | |||
} | |||
/* package */ void updateCellAnchor() { | |||
int rows = getNumberOfRows(); | |||
int cols = getNumberOfColumns(); | |||
double colWidths[] = new double[cols]; | |||
double rowHeights[] = new double[rows]; | |||
for (int row=0; row<rows; row++) { | |||
rowHeights[row] = getRowHeight(row); | |||
} | |||
for (int col=0; col<cols; col++) { | |||
colWidths[col] = getColumnWidth(col); | |||
} | |||
Rectangle2D tblAnc = getAnchor(); | |||
DrawFactory df = DrawFactory.getInstance(null); | |||
double newY = tblAnc.getY(); | |||
// #1 pass - determine row heights, the height values might be too low or 0 ... | |||
for (int row=0; row<rows; row++) { | |||
double maxHeight = 0; | |||
for (int col=0; col<cols; col++) { | |||
XSLFTableCell tc = getCell(row, col); | |||
if (tc.getGridSpan() != 1 || tc.getRowSpan() != 1) { | |||
continue; | |||
} | |||
// need to set the anchor before height calculation | |||
tc.setAnchor(new Rectangle2D.Double(0,0,colWidths[col],0)); | |||
DrawTextShape dts = df.getDrawable(tc); | |||
maxHeight = Math.max(maxHeight, dts.getTextHeight()); | |||
} | |||
rowHeights[row] = Math.max(rowHeights[row],maxHeight); | |||
} | |||
// #2 pass - init properties | |||
for (int row=0; row<rows; row++) { | |||
double newX = tblAnc.getX(); | |||
for (int col=0; col<cols; col++) { | |||
Rectangle2D bounds = new Rectangle2D.Double(newX, newY, colWidths[col], rowHeights[row]); | |||
XSLFTableCell tc = getCell(row, col); | |||
tc.setAnchor(bounds); | |||
newX += colWidths[col]+DrawTableShape.borderSize; | |||
} | |||
newY += rowHeights[row]+DrawTableShape.borderSize; | |||
} | |||
// #3 pass - update merge info | |||
for (int row=0; row<rows; row++) { | |||
for (int col=0; col<cols; col++) { | |||
XSLFTableCell tc = getCell(row, col); | |||
Rectangle2D mergedBounds = tc.getAnchor(); | |||
for (int col2=col+1; col2<col+tc.getGridSpan(); col2++) { | |||
assert(col2 < cols); | |||
XSLFTableCell tc2 = getCell(row, col2); | |||
assert(tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1); | |||
mergedBounds.add(tc2.getAnchor()); | |||
} | |||
for (int row2=row+1; row2<row+tc.getRowSpan(); row2++) { | |||
assert(row2 < rows); | |||
XSLFTableCell tc2 = getCell(row2, col); | |||
assert(tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1); | |||
mergedBounds.add(tc2.getAnchor()); | |||
} | |||
tc.setAnchor(mergedBounds); | |||
} | |||
} | |||
} | |||
} |
@@ -20,6 +20,7 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import java.awt.Color; | |||
import java.awt.geom.Rectangle2D; | |||
import org.apache.poi.sl.draw.DrawPaint; | |||
import org.apache.poi.sl.usermodel.PaintStyle; | |||
@@ -30,18 +31,32 @@ import org.apache.poi.sl.usermodel.StrokeStyle.LineDash; | |||
import org.apache.poi.sl.usermodel.TableCell; | |||
import org.apache.poi.sl.usermodel.VerticalAlignment; | |||
import org.apache.poi.util.Units; | |||
import org.apache.poi.xslf.usermodel.XSLFTableStyle.TablePartStyle; | |||
import org.apache.xmlbeans.XmlObject; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTFillProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTFontReference; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTable; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCell; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCellProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTablePartStyle; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableProperties; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyleTextStyle; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STCompoundLine; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndLength; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndType; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndWidth; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STOnOffStyleType; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STPenAlignment; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType; | |||
@@ -52,14 +67,22 @@ import org.openxmlformats.schemas.drawingml.x2006.main.STTextVerticalType; | |||
*/ | |||
public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,XSLFTextParagraph> { | |||
private CTTableCellProperties _tcPr = null; | |||
private final XSLFTable table; | |||
private int row = 0, col = 0; | |||
/*package*/ XSLFTableCell(CTTableCell cell, XSLFSheet sheet){ | |||
super(cell, sheet); | |||
/** | |||
* Volatile/temporary anchor - e.g. for rendering | |||
*/ | |||
private Rectangle2D anchor = null; | |||
/*package*/ XSLFTableCell(CTTableCell cell, XSLFTable table){ | |||
super(cell, table.getSheet()); | |||
this.table = table; | |||
} | |||
@Override | |||
protected CTTextBody getTextBody(boolean create){ | |||
CTTableCell cell = (CTTableCell)getXmlObject(); | |||
CTTableCell cell = getCell(); | |||
CTTextBody txBody = cell.getTxBody(); | |||
if (txBody == null && create) { | |||
txBody = cell.addNewTxBody(); | |||
@@ -80,7 +103,7 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
protected CTTableCellProperties getCellProperties(boolean create) { | |||
if (_tcPr == null) { | |||
CTTableCell cell = (CTTableCell)getXmlObject(); | |||
CTTableCell cell = getCell(); | |||
_tcPr = cell.getTcPr(); | |||
if (_tcPr == null && create) { | |||
_tcPr = cell.addNewTcPr(); | |||
@@ -190,28 +213,28 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
} | |||
}; | |||
} | |||
@Override | |||
public void setBorderStyle(BorderEdge edge, StrokeStyle style) { | |||
if (style == null) { | |||
throw new IllegalArgumentException("StrokeStyle needs to be specified."); | |||
} | |||
LineCap cap = style.getLineCap(); | |||
if (cap != null) { | |||
setBorderCap(edge, cap); | |||
} | |||
LineCompound compound = style.getLineCompound(); | |||
if (compound != null) { | |||
setBorderCompound(edge, compound); | |||
} | |||
LineDash dash = style.getLineDash(); | |||
if (dash != null) { | |||
setBorderDash(edge, dash); | |||
} | |||
double width = style.getLineWidth(); | |||
setBorderWidth(edge, width); | |||
} | |||
@@ -273,10 +296,9 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
} | |||
CTLineProperties ln = setBorderDefaults(edge); | |||
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance(); | |||
rgb.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()}); | |||
ln.addNewSolidFill().setSrgbClr(rgb); | |||
CTSolidColorFillProperties fill = ln.addNewSolidFill(); | |||
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr()); | |||
c.setColor(color); | |||
} | |||
public Color getBorderColor(BorderEdge edge) { | |||
@@ -284,12 +306,8 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill()) return null; | |||
CTSolidColorFillProperties fill = ln.getSolidFill(); | |||
if (!fill.isSetSrgbClr()) { | |||
// TODO for now return null for all colors except explicit RGB | |||
return null; | |||
} | |||
byte[] val = fill.getSrgbClr().getVal(); | |||
return new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]); | |||
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr()); | |||
return c.getColor(); | |||
} | |||
public LineCompound getBorderCompound(BorderEdge edge) { | |||
@@ -335,7 +353,7 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill() || !ln.isSetCap()) { | |||
return null; | |||
} | |||
return LineCap.fromOoxmlId(ln.getCap().intValue()); | |||
} | |||
@@ -361,14 +379,10 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
CTTableCellProperties spPr = getCellProperties(true); | |||
if (color == null) { | |||
if(spPr.isSetSolidFill()) spPr.unsetSolidFill(); | |||
} | |||
else { | |||
} else { | |||
CTSolidColorFillProperties fill = spPr.isSetSolidFill() ? spPr.getSolidFill() : spPr.addNewSolidFill(); | |||
CTSRgbColor rgb = CTSRgbColor.Factory.newInstance(); | |||
rgb.setVal(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()}); | |||
fill.setSrgbClr(rgb); | |||
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr()); | |||
c.setColor(color); | |||
} | |||
} | |||
@@ -379,31 +393,126 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
@Override | |||
public Color getFillColor(){ | |||
CTTableCellProperties spPr = getCellProperties(false); | |||
if (spPr == null || !spPr.isSetSolidFill()) return null; | |||
if (spPr == null || !spPr.isSetSolidFill()) { | |||
return null; | |||
} | |||
CTSolidColorFillProperties fill = spPr.getSolidFill(); | |||
if (!fill.isSetSrgbClr()) { | |||
// TODO for now return null for all colors except explicit RGB | |||
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr()); | |||
return c.getColor(); | |||
} | |||
@SuppressWarnings("resource") | |||
@Override | |||
public PaintStyle getFillPaint() { | |||
Color c = getFillColor(); | |||
if (c != null) { | |||
return DrawPaint.createSolidPaint(c); | |||
} | |||
CTTablePartStyle tps = getTablePartStyle(null); | |||
if (tps == null || !tps.isSetTcStyle()) { | |||
tps = getTablePartStyle(TablePartStyle.wholeTbl); | |||
if (tps == null || !tps.isSetTcStyle()) { | |||
return null; | |||
} | |||
} | |||
XMLSlideShow slideShow = table.getSheet().getSlideShow(); | |||
assert(slideShow != null); | |||
XSLFTheme theme = slideShow.getSlides().get(0).getTheme(); | |||
CTFillProperties pr = tps.getTcStyle().getFill(); | |||
for (XmlObject obj : pr.selectPath("*")) { | |||
PaintStyle paint = XSLFShape.selectPaint(obj, null, slideShow.getPackagePart(), theme); | |||
if (paint != null) { | |||
return paint; | |||
} | |||
} | |||
return null; | |||
} | |||
/** | |||
* Retrieves the part style depending on the location of this cell | |||
* | |||
* @param tablePartStyle the part to be returned, usually this is null | |||
* and only set when used as a helper method | |||
* @return the table part style | |||
*/ | |||
private CTTablePartStyle getTablePartStyle(TablePartStyle tablePartStyle) { | |||
CTTable ct = table.getCTTable(); | |||
if (!ct.isSetTblPr()) { | |||
return null; | |||
} | |||
CTTableProperties pr = ct.getTblPr(); | |||
boolean bandRow = (pr.isSetBandRow() && pr.getBandRow()); | |||
boolean firstRow = (pr.isSetFirstRow() && pr.getFirstRow()); | |||
boolean lastRow = (pr.isSetLastRow() && pr.getLastRow()); | |||
boolean bandCol = (pr.isSetBandCol() && pr.getBandCol()); | |||
boolean firstCol = (pr.isSetFirstCol() && pr.getFirstCol()); | |||
boolean lastCol = (pr.isSetLastCol() && pr.getLastCol()); | |||
TablePartStyle tps; | |||
if (tablePartStyle != null) { | |||
tps = tablePartStyle; | |||
} else if (row == 0 && firstRow) { | |||
tps = TablePartStyle.firstRow; | |||
} else if (row == table.getNumberOfRows()-1 && lastRow) { | |||
tps = TablePartStyle.lastRow; | |||
} else if (col == 0 && firstCol) { | |||
tps = TablePartStyle.firstCol; | |||
} else if (col == table.getNumberOfColumns()-1 && lastCol) { | |||
tps = TablePartStyle.lastCol; | |||
} else { | |||
tps = TablePartStyle.wholeTbl; | |||
int br = row + (firstRow ? 1 : 0); | |||
int bc = col + (firstCol ? 1 : 0); | |||
if (bandRow && (br & 1) == 0) { | |||
tps = TablePartStyle.band1H; | |||
} else if (bandCol && (bc & 1) == 0) { | |||
tps = TablePartStyle.band1V; | |||
} | |||
} | |||
XSLFTableStyle tabStyle = table.getTableStyle(); | |||
if (tabStyle == null) { | |||
return null; | |||
} | |||
byte[] val = fill.getSrgbClr().getVal(); | |||
return new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]); | |||
CTTablePartStyle part = tabStyle.getTablePartStyle(tps); | |||
return (part == null) ? tabStyle.getTablePartStyle(TablePartStyle.wholeTbl) : part; | |||
} | |||
void setGridSpan(int gridSpan_) { | |||
((CTTableCell)getXmlObject()).setGridSpan(gridSpan_); | |||
getCell().setGridSpan(gridSpan_); | |||
} | |||
@Override | |||
public int getGridSpan() { | |||
CTTableCell c = getCell(); | |||
return (c.isSetGridSpan()) ? c.getGridSpan() : 1; | |||
} | |||
void setRowSpan(int rowSpan_) { | |||
((CTTableCell)getXmlObject()).setRowSpan(rowSpan_); | |||
getCell().setRowSpan(rowSpan_); | |||
} | |||
@Override | |||
public int getRowSpan() { | |||
CTTableCell c = getCell(); | |||
return (c.isSetRowSpan()) ? c.getRowSpan() : 1; | |||
} | |||
void setHMerge(boolean merge_) { | |||
((CTTableCell)getXmlObject()).setHMerge(merge_); | |||
getCell().setHMerge(merge_); | |||
} | |||
void setVMerge(boolean merge_) { | |||
((CTTableCell)getXmlObject()).setVMerge(merge_); | |||
getCell().setVMerge(merge_); | |||
} | |||
@Override | |||
@@ -457,7 +566,7 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
vt = STTextVerticalType.WORD_ART_VERT; | |||
break; | |||
} | |||
cellProps.setVert(vt); | |||
} | |||
} | |||
@@ -475,7 +584,7 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
} else { | |||
orientation = STTextVerticalType.HORZ; | |||
} | |||
switch (orientation.intValue()) { | |||
default: | |||
case STTextVerticalType.INT_HORZ: | |||
@@ -491,4 +600,142 @@ public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, | |||
return TextDirection.STACKED; | |||
} | |||
} | |||
} | |||
private CTTableCell getCell() { | |||
return (CTTableCell)getXmlObject(); | |||
} | |||
/* package */ void setRowColIndex(int row, int col) { | |||
this.row = row; | |||
this.col = col; | |||
} | |||
/** | |||
* Return a fake-xfrm which is used for calculating the text height | |||
*/ | |||
protected CTTransform2D getXfrm() { | |||
Rectangle2D anc = getAnchor(); | |||
CTTransform2D xfrm = CTTransform2D.Factory.newInstance(); | |||
CTPoint2D off = xfrm.addNewOff(); | |||
off.setX(Units.toEMU(anc.getX())); | |||
off.setY(Units.toEMU(anc.getY())); | |||
CTPositiveSize2D size = xfrm.addNewExt(); | |||
size.setCx(Units.toEMU(anc.getWidth())); | |||
size.setCy(Units.toEMU(anc.getHeight())); | |||
return xfrm; | |||
} | |||
/** | |||
* There's no real anchor for table cells - this method is used to temporarily store the location | |||
* of the cell for a later retrieval, e.g. for rendering | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
@Override | |||
public void setAnchor(Rectangle2D anchor) { | |||
if (this.anchor == null) { | |||
this.anchor = (Rectangle2D)anchor.clone(); | |||
} else { | |||
this.anchor.setRect(anchor); | |||
} | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
@Override | |||
public Rectangle2D getAnchor() { | |||
if (anchor == null) { | |||
table.updateCellAnchor(); | |||
} | |||
// anchor should be set, after updateCellAnchor is through | |||
assert(anchor != null); | |||
return anchor; | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
@Override | |||
public boolean isMerged() { | |||
CTTableCell c = getCell(); | |||
return (c.isSetHMerge() && c.getHMerge()) || (c.isSetVMerge() && c.getVMerge()); | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
@Override | |||
protected XSLFCellTextParagraph newTextParagraph(CTTextParagraph p) { | |||
return new XSLFCellTextParagraph(p, this); | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
private class XSLFCellTextParagraph extends XSLFTextParagraph { | |||
protected XSLFCellTextParagraph(CTTextParagraph p, XSLFTextShape shape) { | |||
super(p, shape); | |||
} | |||
@Override | |||
protected XSLFCellTextRun newTextRun(CTRegularTextRun r) { | |||
return new XSLFCellTextRun(r, this); | |||
} | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
private class XSLFCellTextRun extends XSLFTextRun { | |||
protected XSLFCellTextRun(CTRegularTextRun r, XSLFTextParagraph p) { | |||
super(r, p); | |||
} | |||
@Override | |||
public PaintStyle getFontColor(){ | |||
CTTableStyleTextStyle txStyle = getTextStyle(); | |||
if (txStyle == null) { | |||
return super.getFontColor(); | |||
} | |||
CTSchemeColor phClr = null; | |||
CTFontReference fontRef = txStyle.getFontRef(); | |||
if (fontRef != null) { | |||
phClr = fontRef.getSchemeClr(); | |||
} | |||
XSLFTheme theme = getSheet().getTheme(); | |||
final XSLFColor c = new XSLFColor(txStyle, theme, phClr); | |||
return DrawPaint.createSolidPaint(c.getColorStyle()); | |||
} | |||
@Override | |||
public boolean isBold() { | |||
CTTableStyleTextStyle txStyle = getTextStyle(); | |||
if (txStyle == null) { | |||
return super.isBold(); | |||
} else { | |||
return txStyle.isSetB() && txStyle.getB().intValue() == STOnOffStyleType.INT_ON; | |||
} | |||
} | |||
@Override | |||
public boolean isItalic() { | |||
CTTableStyleTextStyle txStyle = getTextStyle(); | |||
if (txStyle == null) { | |||
return super.isItalic(); | |||
} else { | |||
return txStyle.isSetI() && txStyle.getI().intValue() == STOnOffStyleType.INT_ON; | |||
} | |||
} | |||
private CTTableStyleTextStyle getTextStyle() { | |||
CTTablePartStyle tps = getTablePartStyle(null); | |||
if (tps == null || !tps.isSetTcTxStyle()) { | |||
tps = getTablePartStyle(TablePartStyle.wholeTbl); | |||
} | |||
return (tps == null) ? null : tps.getTcTxStyle(); | |||
} | |||
} | |||
} |
@@ -30,8 +30,6 @@ import org.openxmlformats.schemas.drawingml.x2006.main.CTTableRow; | |||
/** | |||
* Represents a table in a .pptx presentation | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public class XSLFTableRow implements Iterable<XSLFTableCell> { | |||
private CTTableRow _row; | |||
@@ -44,7 +42,7 @@ public class XSLFTableRow implements Iterable<XSLFTableCell> { | |||
CTTableCell[] tcArray = _row.getTcArray(); | |||
_cells = new ArrayList<XSLFTableCell>(tcArray.length); | |||
for(CTTableCell cell : tcArray) { | |||
_cells.add(new XSLFTableCell(cell, table.getSheet())); | |||
_cells.add(new XSLFTableCell(cell, table)); | |||
} | |||
} | |||
@@ -71,12 +69,13 @@ public class XSLFTableRow implements Iterable<XSLFTableCell> { | |||
public XSLFTableCell addCell(){ | |||
CTTableCell c = _row.addNewTc(); | |||
c.set(XSLFTableCell.prototype()); | |||
XSLFTableCell cell = new XSLFTableCell(c, _table.getSheet()); | |||
XSLFTableCell cell = new XSLFTableCell(c, _table); | |||
_cells.add(cell); | |||
if(_table.getNumberOfColumns() < _row.sizeOfTcArray()) { | |||
_table.getCTTable().getTblGrid().addNewGridCol().setW(Units.toEMU(100.0)); | |||
} | |||
_table.updateRowColIndexes(); | |||
return cell; | |||
} | |||
@@ -19,16 +19,19 @@ | |||
package org.apache.poi.xslf.usermodel; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTablePartStyle; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle; | |||
/** | |||
* Represents a table in a .pptx presentation | |||
* | |||
* @author Yegor Kozlov | |||
* Represents a table style in a .pptx presentation | |||
*/ | |||
public class XSLFTableStyle { | |||
private CTTableStyle _tblStyle; | |||
public enum TablePartStyle { | |||
wholeTbl, band1H, band2H, band1V, band2V, firstCol, lastCol, firstRow, lastRow, seCell, swCell, neCell, nwCell; | |||
} | |||
/*package*/ XSLFTableStyle(CTTableStyle style){ | |||
_tblStyle = style; | |||
} | |||
@@ -44,4 +47,39 @@ public class XSLFTableStyle { | |||
public String getStyleId(){ | |||
return _tblStyle.getStyleId(); | |||
} | |||
/** | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected CTTablePartStyle getTablePartStyle(TablePartStyle tps) { | |||
switch (tps) { | |||
default: | |||
case wholeTbl: | |||
return _tblStyle.getWholeTbl(); | |||
case band1H: | |||
return _tblStyle.getBand1H(); | |||
case band2H: | |||
return _tblStyle.getBand2H(); | |||
case band1V: | |||
return _tblStyle.getBand1V(); | |||
case band2V: | |||
return _tblStyle.getBand2V(); | |||
case firstCol: | |||
return _tblStyle.getFirstCol(); | |||
case lastCol: | |||
return _tblStyle.getLastCol(); | |||
case firstRow: | |||
return _tblStyle.getFirstRow(); | |||
case lastRow: | |||
return _tblStyle.getLastRow(); | |||
case seCell: | |||
return _tblStyle.getSeCell(); | |||
case swCell: | |||
return _tblStyle.getSwCell(); | |||
case neCell: | |||
return _tblStyle.getNeCell(); | |||
case nwCell: | |||
return _tblStyle.getNwCell(); | |||
} | |||
} | |||
} |
@@ -16,9 +16,8 @@ | |||
==================================================================== */ | |||
package org.apache.poi.xslf.usermodel; | |||
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Iterator; | |||
@@ -31,6 +30,7 @@ import org.apache.poi.util.Beta; | |||
import org.apache.xmlbeans.XmlException; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyleList; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.TblStyleLstDocument; | |||
@Beta | |||
public class XSLFTableStyles extends POIXMLDocumentPart implements Iterable<XSLFTableStyle>{ | |||
@@ -47,7 +47,10 @@ public class XSLFTableStyles extends POIXMLDocumentPart implements Iterable<XSLF | |||
public XSLFTableStyles(PackagePart part) throws IOException, XmlException { | |||
super(part); | |||
_tblStyleLst = CTTableStyleList.Factory.parse(getPackagePart().getInputStream(), DEFAULT_XML_OPTIONS); | |||
InputStream is = getPackagePart().getInputStream(); | |||
TblStyleLstDocument styleDoc = TblStyleLstDocument.Factory.parse(is); | |||
is.close(); | |||
_tblStyleLst = styleDoc.getTblStyleLst(); | |||
CTTableStyle[] tblStyleArray = _tblStyleLst.getTblStyleArray(); | |||
_styles = new ArrayList<XSLFTableStyle>(tblStyleArray.length); | |||
for(CTTableStyle c : tblStyleArray){ |
@@ -59,7 +59,6 @@ import org.openxmlformats.schemas.presentationml.x2006.main.STPlaceholderType; | |||
* Represents a paragraph of text within the containing text body. | |||
* The paragraph is the highest level text separation mechanism. | |||
* | |||
* @author Yegor Kozlov | |||
* @since POI-3.8 | |||
*/ | |||
@Beta | |||
@@ -76,19 +75,19 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
for(XmlObject ch : _p.selectPath("*")){ | |||
if(ch instanceof CTRegularTextRun){ | |||
CTRegularTextRun r = (CTRegularTextRun)ch; | |||
_runs.add(new XSLFTextRun(r, this)); | |||
_runs.add(newTextRun(r)); | |||
} 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)); | |||
_runs.add(newTextRun(r)); | |||
} 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)); | |||
_runs.add(newTextRun(r)); | |||
} | |||
} | |||
} | |||
@@ -137,7 +136,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
CTRegularTextRun r = _p.addNewR(); | |||
CTTextCharacterProperties rPr = r.addNewRPr(); | |||
rPr.setLang("en-US"); | |||
XSLFTextRun run = new XSLFTextRun(r, this); | |||
XSLFTextRun run = newTextRun(r); | |||
_runs.add(run); | |||
return run; | |||
} | |||
@@ -774,22 +773,23 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
int level = getIndentLevel(); | |||
// wind up and find the root master sheet which must be slide master | |||
final String nsDecl = | |||
"declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " + | |||
"declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' "; | |||
final String xpaths[] = { | |||
nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr", | |||
nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr" | |||
}; | |||
final String nsPML = "http://schemas.openxmlformats.org/presentationml/2006/main"; | |||
final String nsDML = "http://schemas.openxmlformats.org/drawingml/2006/main"; | |||
XSLFSheet masterSheet = _shape.getSheet(); | |||
for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) { | |||
masterSheet = m; | |||
XmlObject xo = masterSheet.getXmlObject(); | |||
for (String xpath : xpaths) { | |||
XmlObject[] o = xo.selectPath(xpath); | |||
if (o.length == 1) { | |||
return (CTTextParagraphProperties)o[0]; | |||
XmlCursor cur = xo.newCursor(); | |||
try { | |||
cur.push(); | |||
if ((cur.toChild(nsPML, "txStyles") && cur.toChild(nsPML, defaultStyleSelector)) || | |||
(cur.pop() && cur.toChild(nsPML, "notesStyle"))) { | |||
if (cur.toChild(nsDML, "lvl" +(level+1)+ "pPr")) { | |||
return (CTTextParagraphProperties)cur.getObject(); | |||
} | |||
} | |||
} finally { | |||
cur.dispose(); | |||
} | |||
} | |||
@@ -880,7 +880,7 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
List<XSLFTextRun> otherRs = other.getTextRuns(); | |||
int i=0; | |||
for(CTRegularTextRun rtr : thisP.getRArray()) { | |||
XSLFTextRun run = new XSLFTextRun(rtr, this); | |||
XSLFTextRun run = newTextRun(rtr); | |||
run.copy(otherRs.get(i++)); | |||
_runs.add(run); | |||
} | |||
@@ -949,6 +949,9 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
@Override | |||
public Double getDefaultFontSize() { | |||
CTTextCharacterProperties endPr = _p.getEndParaRPr(); | |||
if (endPr == null || !endPr.isSetSz()) { | |||
endPr = getDefaultMasterStyle().getDefRPr(); | |||
} | |||
return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.); | |||
} | |||
@@ -1062,4 +1065,17 @@ public class XSLFTextParagraph implements TextParagraph<XSLFShape,XSLFTextParagr | |||
return false; | |||
} | |||
} | |||
/** | |||
* Helper method to allow subclasses to provide their own text run | |||
* | |||
* @param r the xml reference | |||
* | |||
* @return a new text paragraph | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected XSLFTextRun newTextRun(CTRegularTextRun r) { | |||
return new XSLFTextRun(r, this); | |||
} | |||
} |
@@ -30,15 +30,13 @@ import org.openxmlformats.schemas.presentationml.x2006.main.CTPlaceholder; | |||
/** | |||
* Represents a run of text within the containing text body. The run element is the | |||
* lowest level text separation mechanism within a text body. | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
@Beta | |||
public class XSLFTextRun implements TextRun { | |||
private final CTRegularTextRun _r; | |||
private final XSLFTextParagraph _p; | |||
XSLFTextRun(CTRegularTextRun r, XSLFTextParagraph p){ | |||
protected XSLFTextRun(CTRegularTextRun r, XSLFTextParagraph p){ | |||
_r = r; | |||
_p = p; | |||
} |
@@ -62,7 +62,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
CTTextBody txBody = getTextBody(false); | |||
if (txBody != null) { | |||
for (CTTextParagraph p : txBody.getPArray()) { | |||
_paragraphs.add(new XSLFTextParagraph(p, this)); | |||
_paragraphs.add(newTextParagraph(p)); | |||
} | |||
} | |||
} | |||
@@ -100,13 +100,13 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
txBody.removeP(i-1); | |||
_paragraphs.remove(i-1); | |||
} | |||
_paragraphs.get(0).clearButKeepProperties(); | |||
} | |||
return appendText(text, false); | |||
} | |||
@Override | |||
public XSLFTextRun appendText(String text, boolean newParagraph) { | |||
if (text == null) return null; | |||
@@ -114,7 +114,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
// copy properties from last paragraph / textrun or paragraph end marker | |||
CTTextParagraphProperties pPr = null; | |||
CTTextCharacterProperties rPr = null; | |||
boolean firstPara; | |||
XSLFTextParagraph para; | |||
if (_paragraphs.isEmpty()) { | |||
@@ -133,7 +133,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
rPr = ctp.getEndParaRPr(); | |||
} | |||
} | |||
XSLFTextRun run = null; | |||
for (String lineTxt : text.split("\\r\\n?|\\n")) { | |||
if (!firstPara) { | |||
@@ -159,7 +159,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
} | |||
firstPara = false; | |||
} | |||
assert(run != null); | |||
return run; | |||
} | |||
@@ -184,7 +184,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
} else { | |||
p = txBody.addNewP(); | |||
} | |||
XSLFTextParagraph paragraph = new XSLFTextParagraph(p, this); | |||
XSLFTextParagraph paragraph = newTextParagraph(p); | |||
_paragraphs.add(paragraph); | |||
return paragraph; | |||
} | |||
@@ -243,7 +243,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
fetchShapeProperty(fetcher); | |||
return fetcher.getValue() == null ? false : fetcher.getValue(); | |||
} | |||
@Override | |||
public void setTextDirection(TextDirection orientation){ | |||
CTTextBodyProperties bodyPr = getTextBodyPr(true); | |||
@@ -274,9 +274,9 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
if (bodyPr != null && bodyPr.isSetRot()) { | |||
return bodyPr.getRot() / 60000.; | |||
} | |||
return null; | |||
return null; | |||
} | |||
@Override | |||
public void setTextRotation(Double rotation) { | |||
CTTextBodyProperties bodyPr = getTextBodyPr(true); | |||
@@ -284,8 +284,8 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
bodyPr.setRot((int)(rotation * 60000.)); | |||
} | |||
} | |||
/** | |||
* Returns the distance (in points) between the bottom of the text frame | |||
* and the bottom of the inscribed rectangle of the shape that contains the text. | |||
@@ -437,7 +437,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
Insets2D insets = new Insets2D(getTopInset(), getLeftInset(), getBottomInset(), getRightInset()); | |||
return insets; | |||
} | |||
@Override | |||
public void setInsets(Insets2D insets) { | |||
setTopInset(insets.top); | |||
@@ -445,7 +445,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
setBottomInset(insets.bottom); | |||
setRightInset(insets.right); | |||
} | |||
@Override | |||
public boolean getWordWrap(){ | |||
PropertyFetcher<Boolean> fetcher = new TextBodyPropertyFetcher<Boolean>(){ | |||
@@ -520,14 +520,14 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
} | |||
return textBodyPr; | |||
} | |||
protected abstract CTTextBody getTextBody(boolean create); | |||
@Override | |||
public void setPlaceholder(Placeholder placeholder) { | |||
super.setPlaceholder(placeholder); | |||
} | |||
public Placeholder getTextType(){ | |||
CTPlaceholder ph = getCTPlaceholder(); | |||
if (ph == null) return null; | |||
@@ -552,15 +552,15 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
Rectangle2D anchor = getAnchor(); | |||
if(anchor.getWidth() == 0.) throw new POIXMLException( | |||
"Anchor of the shape was not set."); | |||
double height = getTextHeight(); | |||
double height = getTextHeight(); | |||
height += 1; // add a pixel to compensate rounding errors | |||
anchor.setRect(anchor.getX(), anchor.getY(), anchor.getWidth(), height); | |||
setAnchor(anchor); | |||
return anchor; | |||
} | |||
} | |||
@Override | |||
void copy(XSLFShape other){ | |||
@@ -572,14 +572,14 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
if (otherTB == null) { | |||
return; | |||
} | |||
thisTB.setBodyPr((CTTextBodyProperties)otherTB.getBodyPr().copy()); | |||
if (thisTB.isSetLstStyle()) thisTB.unsetLstStyle(); | |||
if (otherTB.isSetLstStyle()) { | |||
thisTB.setLstStyle((CTTextListStyle)otherTB.getLstStyle().copy()); | |||
} | |||
boolean srcWordWrap = otherTS.getWordWrap(); | |||
if(srcWordWrap != getWordWrap()){ | |||
setWordWrap(srcWordWrap); | |||
@@ -608,7 +608,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
} | |||
clearText(); | |||
for (XSLFTextParagraph srcP : otherTS.getTextParagraphs()) { | |||
XSLFTextParagraph tgtP = addNewTextParagraph(); | |||
tgtP.copy(srcP); | |||
@@ -621,7 +621,7 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
default: | |||
case NOTES: | |||
case HALF_BODY: | |||
case QUARTER_BODY: | |||
case QUARTER_BODY: | |||
case BODY: | |||
setPlaceholder(Placeholder.BODY); | |||
break; | |||
@@ -651,8 +651,19 @@ public abstract class XSLFTextShape extends XSLFSimpleShape | |||
case CENTERED_TITLE: return TextPlaceholder.CENTER_TITLE; | |||
default: | |||
case CONTENT: return TextPlaceholder.OTHER; | |||
} | |||
} | |||
} | |||
/** | |||
* Helper method to allow subclasses to provide their own text paragraph | |||
* | |||
* @param p the xml reference | |||
* | |||
* @return a new text paragraph | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected XSLFTextParagraph newTextParagraph(CTTextParagraph p) { | |||
return new XSLFTextParagraph(p, this); | |||
} | |||
} |
@@ -19,27 +19,20 @@ package org.apache.poi.xslf.usermodel; | |||
import static org.junit.Assert.assertEquals; | |||
import static org.junit.Assert.assertNotNull; | |||
import java.io.IOException; | |||
import org.junit.Test; | |||
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle; | |||
/** | |||
* @author Yegor Kozlov | |||
*/ | |||
public class TestXSLFTableStyles { | |||
@Test | |||
public void testRead(){ | |||
public void testRead() throws IOException { | |||
XMLSlideShow ppt = new XMLSlideShow(); | |||
XSLFTableStyles tblStyles = ppt.getTableStyles(); | |||
assertNotNull(tblStyles); | |||
assertEquals(0, tblStyles.getStyles().size()); | |||
} | |||
@SuppressWarnings("unused") | |||
@Test | |||
public void testStyle(){ | |||
CTTableStyle obj = CTTableStyle.Factory.newInstance(); | |||
XSLFTableStyle style = new XSLFTableStyle(obj); | |||
ppt.close(); | |||
} | |||
} |
@@ -18,13 +18,12 @@ | |||
package org.apache.poi.hslf.usermodel; | |||
import java.awt.geom.Rectangle2D; | |||
import java.io.Serializable; | |||
import java.util.ArrayList; | |||
import java.util.Collections; | |||
import java.util.Comparator; | |||
import java.util.HashSet; | |||
import java.util.List; | |||
import java.util.Set; | |||
import java.util.SortedSet; | |||
import java.util.TreeSet; | |||
import org.apache.poi.ddf.AbstractEscherOptRecord; | |||
import org.apache.poi.ddf.EscherArrayProperty; | |||
@@ -170,23 +169,6 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> { | |||
updateRowHeightsProperty(); | |||
} | |||
private static class TableCellComparator implements Comparator<HSLFShape>, Serializable { | |||
public int compare( HSLFShape o1, HSLFShape o2 ) { | |||
Rectangle2D anchor1 = o1.getAnchor(); | |||
Rectangle2D anchor2 = o2.getAnchor(); | |||
double delta = anchor1.getY() - anchor2.getY(); | |||
if (delta == 0) { | |||
delta = anchor1.getX() - anchor2.getX(); | |||
} | |||
// descending size | |||
if (delta == 0) { | |||
delta = (anchor2.getWidth()*anchor2.getHeight())-(anchor1.getWidth()*anchor1.getHeight()); | |||
} | |||
return (int)Math.signum(delta); | |||
} | |||
} | |||
private void cellListToArray() { | |||
List<HSLFTableCell> htc = new ArrayList<HSLFTableCell>(); | |||
for (HSLFShape h : getShapes()) { | |||
@@ -198,28 +180,52 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> { | |||
if (htc.isEmpty()) { | |||
throw new IllegalStateException("HSLFTable without HSLFTableCells"); | |||
} | |||
Collections.sort(htc, new TableCellComparator()); | |||
List<HSLFTableCell[]> lst = new ArrayList<HSLFTableCell[]>(); | |||
List<HSLFTableCell> row = new ArrayList<HSLFTableCell>(); | |||
double y0 = htc.get(0).getAnchor().getY(); | |||
SortedSet<Double> colSet = new TreeSet<Double>(); | |||
SortedSet<Double> rowSet = new TreeSet<Double>(); | |||
// #1 pass - determine cols and rows | |||
for (HSLFTableCell sh : htc) { | |||
Rectangle2D anchor = sh.getAnchor(); | |||
boolean isNextRow = (anchor.getY() > y0); | |||
if (isNextRow) { | |||
y0 = anchor.getY(); | |||
lst.add(row.toArray(new HSLFTableCell[row.size()])); | |||
row.clear(); | |||
} | |||
row.add(sh); | |||
colSet.add(anchor.getX()); | |||
rowSet.add(anchor.getY()); | |||
} | |||
lst.add(row.toArray(new HSLFTableCell[row.size()])); | |||
cells = lst.toArray(new HSLFTableCell[lst.size()][]); | |||
cells = new HSLFTableCell[rowSet.size()][colSet.size()]; | |||
List<Double> colLst = new ArrayList<Double>(colSet); | |||
List<Double> rowLst = new ArrayList<Double>(rowSet); | |||
// #2 pass - assign shape to table cells | |||
for (HSLFTableCell sh : htc) { | |||
Rectangle2D anchor = sh.getAnchor(); | |||
int row = rowLst.indexOf(anchor.getY()); | |||
int col = colLst.indexOf(anchor.getX()); | |||
assert(row != -1 && col != -1); | |||
cells[row][col] = sh; | |||
// determine gridSpan / rowSpan | |||
int gridSpan = calcSpan(colLst, anchor.getWidth(), col); | |||
int rowSpan = calcSpan(rowLst, anchor.getHeight(), row); | |||
sh.setGridSpan(gridSpan); | |||
sh.setRowSpan(rowSpan); | |||
} | |||
} | |||
private int calcSpan(List<Double> spaces, double totalSpace, int idx) { | |||
if (idx == spaces.size()-1) { | |||
return 1; | |||
} | |||
int span = 0; | |||
double remainingSpace = totalSpace; | |||
while (idx+1 < spaces.size() && remainingSpace > 0) { | |||
remainingSpace -= spaces.get(idx+1)-spaces.get(idx); | |||
span++; | |||
idx++; | |||
} | |||
return span; | |||
} | |||
static class LineRect { | |||
final HSLFLine l; | |||
final double lx1, lx2, ly1, ly2; | |||
@@ -258,6 +264,9 @@ implements HSLFShapeContainer, TableShape<HSLFShape,HSLFTextParagraph> { | |||
// TODO: this only works for non-rotated tables | |||
for (HSLFTableCell[] tca : cells) { | |||
for (HSLFTableCell tc : tca) { | |||
if (tc == null) { | |||
continue; | |||
} | |||
final Rectangle2D cellAnchor = tc.getAnchor(); | |||
/** |
@@ -33,8 +33,6 @@ import org.apache.poi.sl.usermodel.TableCell; | |||
/** | |||
* Represents a cell in a ppt table | |||
* | |||
* @author Yegor Kozlov | |||
*/ | |||
public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFShape,HSLFTextParagraph> { | |||
protected static final int DEFAULT_WIDTH = 100; | |||
@@ -45,6 +43,16 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
/* package */ HSLFLine borderTop; | |||
/* package */ HSLFLine borderBottom; | |||
/** | |||
* The number of columns to be spanned/merged | |||
*/ | |||
private int gridSpan = 1; | |||
/** | |||
* The number of columns to be spanned/merged | |||
*/ | |||
private int rowSpan = 1; | |||
/** | |||
* Create a TableCell object and initialize it from the supplied Record container. | |||
* | |||
@@ -129,7 +137,7 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
@Override | |||
public StrokeStyle getBorderStyle(final BorderEdge edge) { | |||
final Double width = getBorderWidth(edge); | |||
final Double width = getBorderWidth(edge); | |||
return (width == null) ? null : new StrokeStyle() { | |||
public PaintStyle getPaint() { | |||
return DrawPaint.createSolidPaint(getBorderColor(edge)); | |||
@@ -158,24 +166,24 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
if (style == null) { | |||
throw new IllegalArgumentException("StrokeStyle needs to be specified."); | |||
} | |||
// setting the line cap is not implemented, as the border lines aren't connected | |||
LineCompound compound = style.getLineCompound(); | |||
if (compound != null) { | |||
setBorderCompound(edge, compound); | |||
} | |||
LineDash dash = style.getLineDash(); | |||
if (dash != null) { | |||
setBorderDash(edge, dash); | |||
} | |||
double width = style.getLineWidth(); | |||
setBorderWidth(edge, width); | |||
} | |||
public Double getBorderWidth(BorderEdge edge) { | |||
HSLFLine l; | |||
switch (edge) { | |||
@@ -187,7 +195,7 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
} | |||
return (l == null) ? null : l.getLineWidth(); | |||
} | |||
@Override | |||
public void setBorderWidth(BorderEdge edge, double width) { | |||
HSLFLine l = addLine(edge); | |||
@@ -211,7 +219,7 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
if (edge == null || color == null) { | |||
throw new IllegalArgumentException("BorderEdge and/or Color need to be specified."); | |||
} | |||
HSLFLine l = addLine(edge); | |||
l.setLineColor(color); | |||
} | |||
@@ -225,15 +233,15 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
case left: l = borderLeft; break; | |||
default: throw new IllegalArgumentException(); | |||
} | |||
return (l == null) ? null : l.getLineDash(); | |||
return (l == null) ? null : l.getLineDash(); | |||
} | |||
@Override | |||
public void setBorderDash(BorderEdge edge, LineDash dash) { | |||
if (edge == null || dash == null) { | |||
throw new IllegalArgumentException("BorderEdge and/or LineDash need to be specified."); | |||
} | |||
HSLFLine l = addLine(edge); | |||
l.setLineDash(dash); | |||
} | |||
@@ -247,15 +255,15 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
case left: l = borderLeft; break; | |||
default: throw new IllegalArgumentException(); | |||
} | |||
return (l == null) ? null : l.getLineCompound(); | |||
return (l == null) ? null : l.getLineCompound(); | |||
} | |||
@Override | |||
public void setBorderCompound(BorderEdge edge, LineCompound compound) { | |||
if (edge == null || compound == null) { | |||
throw new IllegalArgumentException("BorderEdge and/or LineCompound need to be specified."); | |||
} | |||
HSLFLine l = addLine(edge); | |||
l.setLineCompound(compound); | |||
} | |||
@@ -381,7 +389,7 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
setEscherProperty(opt, EscherProperties.THREED__LIGHTFACE, 0x80000); | |||
anchorBorder(edge, line); | |||
return line; | |||
} | |||
@@ -397,4 +405,43 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell<HSLFSh | |||
public HSLFTable getParent() { | |||
return (HSLFTable)super.getParent(); | |||
} | |||
/** | |||
* Set the gridSpan (aka col-span) | |||
* | |||
* @param gridSpan the number of columns to be spanned/merged | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected void setGridSpan(int gridSpan) { | |||
this.gridSpan = gridSpan; | |||
} | |||
/** | |||
* Set the rowSpan | |||
* | |||
* @param rowSpan the number of rows to be spanned/merged | |||
* | |||
* @since POI 3.15-beta2 | |||
*/ | |||
protected void setRowSpan(int rowSpan) { | |||
this.rowSpan = rowSpan; | |||
} | |||
@Override | |||
public int getGridSpan() { | |||
return gridSpan; | |||
} | |||
@Override | |||
public int getRowSpan() { | |||
return rowSpan; | |||
} | |||
@Override | |||
public boolean isMerged() { | |||
// if a hslf cell is merged, it won't appear in the cell matrix, i.e. it doesn't exist | |||
// therefore this is always false | |||
return false; | |||
} | |||
} |