defaultFactory.set(factory);\r
}\r
\r
+ /**\r
+ * Returns the DrawFactory, preferably via a graphics instance.\r
+ * If graphics is null, the current thread local is checked or\r
+ * if it is not set, a new factory is created. \r
+ *\r
+ * @param graphics the current graphics context or null\r
+ * @return the draw factory\r
+ */\r
public static DrawFactory getInstance(Graphics2D graphics) {\r
// first try to find the factory over the rendering hint\r
DrawFactory factory = null;\r
\r
package org.apache.poi.sl.draw;\r
\r
+import java.awt.BasicStroke;\r
import java.awt.Graphics2D;\r
import java.awt.geom.AffineTransform;\r
import java.awt.geom.Rectangle2D;\r
\r
import org.apache.poi.sl.usermodel.PlaceableShape;\r
import org.apache.poi.sl.usermodel.Shape;\r
+import org.apache.poi.sl.usermodel.StrokeStyle;\r
+import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;\r
+import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;\r
\r
\r
public class DrawShape implements Drawable {\r
protected Shape<?,?> getShape() {\r
return shape;\r
}\r
+ \r
+ protected static BasicStroke getStroke(StrokeStyle strokeStyle) {\r
+ float lineWidth = (float) strokeStyle.getLineWidth();\r
+ if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt\r
+\r
+ LineDash lineDash = strokeStyle.getLineDash();\r
+ if (lineDash == null) {\r
+ lineDash = LineDash.SOLID;\r
+ }\r
+\r
+ int dashPatI[] = lineDash.pattern;\r
+ final float dash_phase = 0;\r
+ float[] dashPatF = null;\r
+ if (dashPatI != null) {\r
+ dashPatF = new float[dashPatI.length];\r
+ for (int i=0; i<dashPatI.length; i++) {\r
+ dashPatF[i] = dashPatI[i]*Math.max(1, lineWidth);\r
+ }\r
+ }\r
+\r
+ LineCap lineCapE = strokeStyle.getLineCap();\r
+ if (lineCapE == null) lineCapE = LineCap.FLAT;\r
+ int lineCap;\r
+ switch (lineCapE) {\r
+ case ROUND:\r
+ lineCap = BasicStroke.CAP_ROUND;\r
+ break;\r
+ case SQUARE:\r
+ lineCap = BasicStroke.CAP_SQUARE;\r
+ break;\r
+ default:\r
+ case FLAT:\r
+ lineCap = BasicStroke.CAP_BUTT;\r
+ break;\r
+ }\r
+\r
+ int lineJoin = BasicStroke.JOIN_ROUND;\r
+\r
+ return new BasicStroke(lineWidth, lineCap, lineJoin, lineWidth, dashPatF, dash_phase);\r
+ }\r
}\r
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;\r
import org.apache.poi.sl.usermodel.Shadow;\r
import org.apache.poi.sl.usermodel.SimpleShape;\r
-import org.apache.poi.sl.usermodel.StrokeStyle;\r
-import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;\r
-import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;\r
import org.apache.poi.util.Units;\r
\r
\r
}\r
\r
public BasicStroke getStroke() {\r
- StrokeStyle strokeStyle = getShape().getStrokeStyle();\r
-\r
- float lineWidth = (float) strokeStyle.getLineWidth();\r
- if (lineWidth == 0.0f) lineWidth = 0.25f; // Both PowerPoint and OOo draw zero-length lines as 0.25pt\r
-\r
- LineDash lineDash = strokeStyle.getLineDash();\r
- if (lineDash == null) {\r
- lineDash = LineDash.SOLID;\r
- }\r
-\r
- int dashPatI[] = lineDash.pattern;\r
- final float dash_phase = 0;\r
- float[] dashPatF = null;\r
- if (dashPatI != null) {\r
- dashPatF = new float[dashPatI.length];\r
- for (int i=0; i<dashPatI.length; i++) {\r
- dashPatF[i] = dashPatI[i]*Math.max(1, lineWidth);\r
- }\r
- }\r
-\r
- LineCap lineCapE = strokeStyle.getLineCap();\r
- if (lineCapE == null) lineCapE = LineCap.FLAT;\r
- int lineCap;\r
- switch (lineCapE) {\r
- case ROUND:\r
- lineCap = BasicStroke.CAP_ROUND;\r
- break;\r
- case SQUARE:\r
- lineCap = BasicStroke.CAP_SQUARE;\r
- break;\r
- default:\r
- case FLAT:\r
- lineCap = BasicStroke.CAP_BUTT;\r
- break;\r
- }\r
-\r
- int lineJoin = BasicStroke.JOIN_ROUND;\r
-\r
- return new BasicStroke(lineWidth, lineCap, lineJoin, lineWidth, dashPatF, dash_phase);\r
+ return getStroke(getShape().getStrokeStyle());\r
}\r
\r
protected void drawShadow(\r
\r
import java.awt.Color;\r
import java.awt.Graphics2D;\r
+import java.awt.Paint;\r
+import java.awt.geom.Line2D;\r
+import java.awt.geom.Rectangle2D;\r
\r
import org.apache.poi.sl.usermodel.GroupShape;\r
+import org.apache.poi.sl.usermodel.StrokeStyle;\r
import org.apache.poi.sl.usermodel.StrokeStyle.LineCompound;\r
import org.apache.poi.sl.usermodel.StrokeStyle.LineDash;\r
import org.apache.poi.sl.usermodel.TableCell;\r
import org.apache.poi.sl.usermodel.TableCell.BorderEdge;\r
+import org.apache.poi.util.Internal;\r
import org.apache.poi.sl.usermodel.TableShape;\r
\r
public class DrawTableShape extends DrawShape {\r
- // to be implemented ...\r
+ /**\r
+ * Additional spacing between cells\r
+ */\r
+ @Internal\r
+ public static final int borderSize = 2;\r
+ \r
public DrawTableShape(TableShape<?,?> shape) {\r
super(shape);\r
}\r
- \r
- protected Drawable getDrawable(Graphics2D graphics) {\r
+\r
+ protected Drawable getGroupShape(Graphics2D graphics) {\r
if (shape instanceof GroupShape) {\r
DrawFactory df = DrawFactory.getInstance(graphics);\r
return df.getDrawable((GroupShape<?,?>)shape);\r
}\r
\r
public void applyTransform(Graphics2D graphics) {\r
- Drawable d = getDrawable(graphics);\r
+ Drawable d = getGroupShape(graphics);\r
if (d != null) {\r
d.applyTransform(graphics);\r
+ } else {\r
+ super.applyTransform(graphics);\r
}\r
}\r
\r
public void draw(Graphics2D graphics) {\r
- Drawable d = getDrawable(graphics);\r
+ Drawable d = getGroupShape(graphics);\r
if (d != null) {\r
d.draw(graphics);\r
+ return;\r
}\r
+\r
+ TableShape<?,?> ts = getShape();\r
+ DrawPaint drawPaint = DrawFactory.getInstance(graphics).getPaint(ts);\r
+ final int rows = ts.getNumberOfRows();\r
+ final int cols = ts.getNumberOfColumns();\r
+ \r
+ // draw background boxes\r
+ for (int row=0; row<rows; row++) {\r
+ for (int col=0; col<cols; col++) {\r
+ TableCell<?,?> tc = ts.getCell(row, col);\r
+ if (tc == null || tc.isMerged()) {\r
+ continue;\r
+ }\r
+\r
+ Paint fillPaint = drawPaint.getPaint(graphics, tc.getFillStyle().getPaint());\r
+ graphics.setPaint(fillPaint);\r
+ Rectangle2D cellAnc = tc.getAnchor();\r
+ graphics.fill(cellAnc);\r
+ \r
+ for (BorderEdge edge : BorderEdge.values()) {\r
+ StrokeStyle stroke = tc.getBorderStyle(edge);\r
+ if (stroke == null) {\r
+ continue;\r
+ }\r
+ graphics.setStroke(getStroke(stroke));\r
+ Paint linePaint = drawPaint.getPaint(graphics, stroke.getPaint());\r
+ graphics.setPaint(linePaint);\r
+\r
+ double x=cellAnc.getX(), y=cellAnc.getY(), w=cellAnc.getWidth(), h=cellAnc.getHeight();\r
+ Line2D line;\r
+ switch (edge) {\r
+ default:\r
+ case bottom:\r
+ line = new Line2D.Double(x-borderSize, y+h, x+w+borderSize, y+h);\r
+ break;\r
+ case left:\r
+ line = new Line2D.Double(x, y, x, y+h+borderSize);\r
+ break;\r
+ case right:\r
+ line = new Line2D.Double(x+w, y, x+w, y+h+borderSize);\r
+ break;\r
+ case top:\r
+ line = new Line2D.Double(x-borderSize, y, x+w+borderSize, y);\r
+ break;\r
+ }\r
+\r
+ graphics.draw(line);\r
+ }\r
+ }\r
+ }\r
+\r
+ // draw text\r
+ drawContent(graphics);\r
}\r
\r
public void drawContent(Graphics2D graphics) {\r
- Drawable d = getDrawable(graphics);\r
+ Drawable d = getGroupShape(graphics);\r
if (d != null) {\r
d.drawContent(graphics);\r
+ return;\r
+ }\r
+ \r
+ TableShape<?,?> ts = getShape();\r
+ DrawFactory df = DrawFactory.getInstance(graphics);\r
+\r
+ final int rows = ts.getNumberOfRows();\r
+ final int cols = ts.getNumberOfColumns();\r
+ \r
+ for (int row=0; row<rows; row++) {\r
+ for (int col=0; col<cols; col++) {\r
+ TableCell<?,?> tc = ts.getCell(row, col);\r
+ DrawTextShape dts = df.getDrawable(tc);\r
+ dts.drawContent(graphics);\r
+ }\r
}\r
}\r
\r
@Override\r
protected TableShape<?,?> getShape() {\r
return (TableShape<?,?>)shape;\r
- } \r
- \r
+ }\r
+\r
/**\r
* Format the table and apply the specified Line to all cell boundaries,\r
* both outside and inside.\r
TableShape<?,?> table = getShape();\r
final int rows = table.getNumberOfRows();\r
final int cols = table.getNumberOfColumns();\r
- \r
+\r
BorderEdge edges[] = { BorderEdge.top, BorderEdge.left, null, null };\r
for (int row = 0; row < rows; row++) {\r
for (int col = 0; col < cols; col++) {\r
*/\r
public void setOutsideBorders(Object... args){\r
if (args.length == 0) return;\r
- \r
+\r
TableShape<?,?> table = getShape();\r
final int rows = table.getNumberOfRows();\r
final int cols = table.getNumberOfColumns();\r
- \r
+\r
BorderEdge edges[] = new BorderEdge[4];\r
for (int row = 0; row < rows; row++) {\r
for (int col = 0; col < cols; col++) {\r
*/\r
public void setInsideBorders(Object... args) {\r
if (args.length == 0) return;\r
- \r
+\r
TableShape<?,?> table = getShape();\r
final int rows = table.getNumberOfRows();\r
final int cols = table.getNumberOfColumns();\r
- \r
+\r
BorderEdge edges[] = new BorderEdge[2];\r
for (int row = 0; row < rows; row++) {\r
for (int col = 0; col < cols; col++) {\r
}\r
}\r
}\r
- \r
+\r
/**\r
* Apply the border attributes (args) to the given cell and edges\r
*\r
}\r
}\r
}\r
- \r
}\r
\r
package org.apache.poi.sl.draw;\r
\r
+import java.awt.Dimension;\r
import java.awt.Graphics2D;\r
import java.awt.Paint;\r
import java.awt.font.FontRenderContext;\r
import org.apache.poi.sl.usermodel.TextRun;\r
import org.apache.poi.sl.usermodel.TextRun.TextCap;\r
import org.apache.poi.sl.usermodel.TextShape;\r
+import org.apache.poi.sl.usermodel.TextShape.TextDirection;\r
import org.apache.poi.util.StringUtil;\r
import org.apache.poi.util.Units;\r
\r
+\r
public class DrawTextParagraph implements Drawable {\r
/** Keys for passing hyperlinks to the graphics context */\r
public static final XlinkAttribute HYPERLINK_HREF = new XlinkAttribute("href");\r
* @return wrapping width in points\r
*/\r
protected double getWrappingWidth(boolean firstLine, Graphics2D graphics){\r
- // internal margins for the text box\r
+ TextShape<?,?> ts = paragraph.getParentShape();\r
\r
- Insets2D insets = paragraph.getParentShape().getInsets();\r
+ // internal margins for the text box\r
+ Insets2D insets = ts.getInsets();\r
double leftInset = insets.left;\r
double rightInset = insets.right;\r
\r
- Rectangle2D anchor = DrawShape.getAnchor(graphics, paragraph.getParentShape());\r
-\r
int indentLevel = paragraph.getIndentLevel();\r
if (indentLevel == -1) {\r
// default to 0, if indentLevel is not set\r
rightMargin = 0d;\r
}\r
\r
+ Rectangle2D anchor = DrawShape.getAnchor(graphics, ts);\r
+ TextDirection textDir = ts.getTextDirection();\r
double width;\r
- TextShape<?,?> ts = paragraph.getParentShape();\r
if (!ts.getWordWrap()) {\r
- // if wordWrap == false then we return the advance to the right border of the sheet\r
- width = ts.getSheet().getSlideShow().getPageSize().getWidth() - anchor.getX();\r
+ Dimension pageDim = ts.getSheet().getSlideShow().getPageSize();\r
+ // if wordWrap == false then we return the advance to the (right) border of the sheet\r
+ switch (textDir) {\r
+ default:\r
+ width = pageDim.getWidth() - anchor.getX();\r
+ break;\r
+ case VERTICAL:\r
+ width = pageDim.getHeight() - anchor.getX();\r
+ break;\r
+ case VERTICAL_270:\r
+ width = anchor.getX();\r
+ break;\r
+ }\r
} else {\r
- width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin;\r
+ switch (textDir) {\r
+ default:\r
+ width = anchor.getWidth() - leftInset - rightInset - leftMargin - rightMargin;\r
+ break;\r
+ case VERTICAL:\r
+ case VERTICAL_270:\r
+ width = anchor.getHeight() - leftInset - rightInset - leftMargin - rightMargin;\r
+ break;\r
+ }\r
if (firstLine && !isHSLF()) {\r
if (bullet != null){\r
if (indent > 0) width -= indent;\r
import org.apache.poi.sl.usermodel.TextParagraph.BulletStyle;\r
import org.apache.poi.sl.usermodel.TextRun;\r
import org.apache.poi.sl.usermodel.TextShape;\r
+import org.apache.poi.sl.usermodel.TextShape.TextDirection;\r
\r
public class DrawTextShape extends DrawSimpleShape {\r
\r
\r
// remember the initial transform\r
AffineTransform tx = graphics.getTransform();\r
-\r
+ \r
// Transform of text in flipped shapes is special.\r
// At this point the flip and rotation transform is already applied\r
// (see DrawShape#applyTransform ), but we need to restore it to avoid painting "upside down".\r
graphics.scale(-1, 1);\r
graphics.translate(-anchor.getX(), -anchor.getY());\r
}\r
- \r
+\r
Double textRot = s.getTextRotation();\r
if (textRot != null && textRot != 0) {\r
graphics.translate(anchor.getCenterX(), anchor.getCenterY());\r
break;\r
}\r
\r
+ TextDirection textDir = s.getTextDirection();\r
+ if (textDir == TextDirection.VERTICAL || textDir == TextDirection.VERTICAL_270) {\r
+ double deg = (textDir == TextDirection.VERTICAL) ? 90 : 270;\r
+ graphics.translate(anchor.getCenterX(), anchor.getCenterY());\r
+ graphics.rotate(Math.toRadians(deg));\r
+ graphics.translate(-anchor.getCenterX(), -anchor.getCenterY());\r
+ \r
+ // old top/left edge is now bottom/left or top/right - as we operate on the already\r
+ // rotated drawing context, both verticals can be moved in the same direction\r
+ double w = anchor.getWidth();\r
+ double h = anchor.getHeight();\r
+ graphics.translate((w-h)/2d,(h-w)/2d);\r
+ }\r
+\r
drawParagraphs(graphics, x, y);\r
\r
// restore the transform\r
* @param edge the border edge to be cleared\r
*/\r
void removeBorder(BorderEdge edge);\r
+\r
+ /**\r
+ * Get the number of columns to be spanned/merged\r
+ *\r
+ * @return the grid span\r
+ * \r
+ * @since POI 3.15-beta2\r
+ */\r
+ int getGridSpan();\r
+ \r
+ /**\r
+ * Get the number of rows to be spanned/merged\r
+ *\r
+ * @return the row span\r
+ * \r
+ * @since POI 3.15-beta2\r
+ */\r
+ int getRowSpan();\r
+\r
+ /**\r
+ * Return if this cell is part of a merged cell. The top/left cell of a merged region is not regarded as merged -\r
+ * its grid and/or row span is greater than one. \r
+ *\r
+ * @return true if this a merged cell\r
+ * \r
+ * @since POI 3.15-beta2\r
+ */\r
+ boolean isMerged();\r
}\r
import org.apache.poi.util.POILogger;\r
import org.apache.xmlbeans.XmlObject;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTColor;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTFontReference;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTHslColor;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveFixedPercentage;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTPresetColor;\r
color = Color.black;\r
}\r
}\r
+ } else if (ch instanceof CTFontReference) {\r
+ // try next ...\r
} else {\r
throw new IllegalArgumentException("Unexpected color choice: " + ch.getClass());\r
}\r
\r
/**\r
* Base super-class class for all shapes in PresentationML\r
- *\r
- * @author Yegor Kozlov\r
*/\r
@Beta\r
public abstract class XSLFShape implements Shape<XSLFShape,XSLFTextParagraph> {\r
}\r
\r
protected PaintStyle getFillPaint() {\r
+ final XSLFTheme theme = getSheet().getTheme();\r
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {\r
public boolean fetch(XSLFShape shape) {\r
XmlObject pr = null;\r
PaintStyle paint = null;\r
PackagePart pp = getSheet().getPackagePart();\r
for (XmlObject obj : pr.selectPath("*")) {\r
- paint = selectPaint(obj, null, pp);\r
+ paint = selectPaint(obj, null, pp, theme);\r
if (paint != null) {\r
setValue(paint);\r
return true;\r
if (fillRef == null) {\r
fillRef = getBgRef();\r
}\r
- paint = selectPaint(fillRef);\r
+ paint = selectPaint(fillRef, theme);\r
\r
return paint;\r
}\r
\r
protected PaintStyle getPaint(XmlObject spPr, CTSchemeColor phClr) {\r
PaintStyle paint = null;\r
- PackagePart pp = getSheet().getPackagePart();\r
+ XSLFSheet sheet = getSheet(); \r
+ PackagePart pp = sheet.getPackagePart();\r
+ XSLFTheme theme = sheet.getTheme();\r
for (XmlObject obj : spPr.selectPath("*")) {\r
- paint = selectPaint(obj, phClr, pp);\r
+ paint = selectPaint(obj, phClr, pp, theme);\r
if(paint != null) break;\r
}\r
return paint;\r
*\r
* @return the applied Paint or null if none was applied\r
*/\r
- protected PaintStyle selectPaint(XmlObject obj, final CTSchemeColor phClr, final PackagePart parentPart) {\r
+ protected static PaintStyle selectPaint(XmlObject obj, final CTSchemeColor phClr, final PackagePart parentPart, final XSLFTheme theme) {\r
if (obj instanceof CTNoFillProperties) {\r
return null;\r
} else if (obj instanceof CTSolidColorFillProperties) {\r
- return selectPaint((CTSolidColorFillProperties)obj, phClr);\r
+ return selectPaint((CTSolidColorFillProperties)obj, phClr, theme);\r
} else if (obj instanceof CTBlipFillProperties) {\r
return selectPaint((CTBlipFillProperties)obj, parentPart);\r
} else if (obj instanceof CTGradientFillProperties) {\r
- return selectPaint((CTGradientFillProperties) obj, phClr);\r
+ return selectPaint((CTGradientFillProperties) obj, phClr, theme);\r
} else if (obj instanceof CTStyleMatrixReference) {\r
- return selectPaint((CTStyleMatrixReference)obj);\r
+ return selectPaint((CTStyleMatrixReference)obj, theme);\r
} else {\r
return null;\r
}\r
}\r
\r
- protected PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr) {\r
- final XSLFTheme theme = getSheet().getTheme();\r
+ protected static PaintStyle selectPaint(CTSolidColorFillProperties solidFill, CTSchemeColor phClr, final XSLFTheme theme) {\r
if (phClr == null && solidFill.isSetSchemeClr()) {\r
phClr = solidFill.getSchemeClr();\r
}\r
return DrawPaint.createSolidPaint(c.getColorStyle());\r
}\r
\r
- protected PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {\r
+ protected static PaintStyle selectPaint(final CTBlipFillProperties blipFill, final PackagePart parentPart) {\r
final CTBlip blip = blipFill.getBlip();\r
return new TexturePaint() {\r
private PackagePart getPart() {\r
}; \r
}\r
\r
- protected PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr) {\r
+ protected static PaintStyle selectPaint(final CTGradientFillProperties gradFill, CTSchemeColor phClr, final XSLFTheme theme) {\r
\r
final CTGradientStop[] gs = gradFill.getGsLst().getGsArray();\r
\r
\r
final ColorStyle cs[] = new ColorStyle[gs.length];\r
final float fractions[] = new float[gs.length];\r
- XSLFTheme theme = getSheet().getTheme();\r
\r
int i=0;\r
for (CTGradientStop cgs : gs) {\r
}; \r
}\r
\r
- protected PaintStyle selectPaint(CTStyleMatrixReference fillRef) {\r
+ protected static PaintStyle selectPaint(CTStyleMatrixReference fillRef, final XSLFTheme theme) {\r
if (fillRef == null) return null;\r
\r
// The idx attribute refers to the index of a fill style or\r
// values 1001 and above refer to the index of a background fill style within the bgFillStyleLst element.\r
int idx = (int)fillRef.getIdx();\r
CTSchemeColor phClr = fillRef.getSchemeClr();\r
- XSLFSheet sheet = getSheet();\r
- XSLFTheme theme = sheet.getTheme();\r
XmlObject fillProps = null;\r
CTStyleMatrix matrix = theme.getXmlObject().getThemeElements().getFmtScheme();\r
if (idx >= 1 && idx <= 999) {\r
} else if (idx >= 1001 ){\r
fillProps = matrix.getBgFillStyleLst().selectPath("*")[idx - 1001];\r
}\r
- return (fillProps == null) ? null : selectPaint(fillProps, phClr, theme.getPackagePart());\r
+ return (fillProps == null) ? null : selectPaint(fillProps, phClr, theme.getPackagePart(), theme);\r
}\r
\r
@Override\r
\r
/**\r
* Represents a single (non-group) shape in a .pptx slide show\r
- *\r
- * @author Yegor Kozlov\r
*/\r
@Beta\r
public abstract class XSLFSimpleShape extends XSLFShape\r
}\r
\r
protected PaintStyle getLinePaint() {\r
+ final XSLFTheme theme = getSheet().getTheme();\r
PropertyFetcher<PaintStyle> fetcher = new PropertyFetcher<PaintStyle>() {\r
public boolean fetch(XSLFShape shape) {\r
CTLineProperties spPr = shape.getSpPr().getLn();\r
PaintStyle paint = null;\r
PackagePart pp = getSheet().getPackagePart();\r
for (XmlObject obj : spPr.selectPath("*")) {\r
- paint = selectPaint(obj, null, pp);\r
+ paint = selectPaint(obj, null, pp, theme);\r
if (paint != null) {\r
setValue(paint);\r
return true;\r
\r
CTShapeStyle style = shape.getSpStyle();\r
if (style != null) {\r
- paint = selectPaint(style.getLnRef());\r
+ paint = selectPaint(style.getLnRef(), theme);\r
if (paint != null) {\r
setValue(paint);\r
return true;\r
int idx = (int)lnRef.getIdx();\r
CTSchemeColor phClr = lnRef.getSchemeClr();\r
if(idx > 0){\r
- XSLFTheme theme = getSheet().getTheme();\r
XmlObject lnProps = theme.getXmlObject().\r
getThemeElements().getFmtScheme().getLnStyleLst().selectPath("*")[idx - 1];\r
paint = getPaint(lnProps, phClr);\r
\r
import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;\r
\r
+import java.awt.geom.Rectangle2D;\r
import java.util.ArrayList;\r
import java.util.Collections;\r
import java.util.Iterator;\r
import javax.xml.namespace.QName;\r
\r
import org.apache.poi.POIXMLException;\r
+import org.apache.poi.sl.draw.DrawFactory;\r
+import org.apache.poi.sl.draw.DrawTableShape;\r
+import org.apache.poi.sl.draw.DrawTextShape;\r
import org.apache.poi.sl.usermodel.TableShape;\r
import org.apache.poi.util.Internal;\r
import org.apache.poi.util.Units;\r
\r
/**\r
* Represents a table in a .pptx presentation\r
- *\r
- * @author Yegor Kozlov\r
*/\r
public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>,\r
TableShape<XSLFShape,XSLFTextParagraph> {\r
- static String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";\r
+ /* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table";\r
\r
private CTTable _table;\r
private List<XSLFTableRow> _rows;\r
_table = (CTTable) rs[0];\r
CTTableRow[] trArray = _table.getTrArray();\r
_rows = new ArrayList<XSLFTableRow>(trArray.length);\r
- for(CTTableRow row : trArray) _rows.add(new XSLFTableRow(row, this));\r
+ for(CTTableRow row : trArray) {\r
+ XSLFTableRow xr = new XSLFTableRow(row, this);\r
+ _rows.add(xr);\r
+ }\r
+ updateRowColIndexes();\r
}\r
\r
@Override\r
public XSLFTableRow addRow(){\r
CTTableRow tr = _table.addNewTr();\r
XSLFTableRow row = new XSLFTableRow(tr, this);\r
- row.setHeight(20.0); // default height is 20 points\r
+ // default height is 20 points\r
+ row.setHeight(20.0); \r
_rows.add(row);\r
+ updateRowColIndexes();\r
return row;\r
}\r
\r
}\r
}\r
}\r
+ \r
+ /**\r
+ * Get assigned TableStyle\r
+ *\r
+ * @return the assigned TableStyle\r
+ * \r
+ * @since POI 3.15-beta2\r
+ */\r
+ protected XSLFTableStyle getTableStyle() {\r
+ CTTable tab = getCTTable();\r
+ // TODO: support inline table style\r
+ if (!tab.isSetTblPr() || !tab.getTblPr().isSetTableStyleId()) {\r
+ return null;\r
+ }\r
+ \r
+ String styleId = tab.getTblPr().getTableStyleId();\r
+ XSLFTableStyles styles = getSheet().getSlideShow().getTableStyles();\r
+ for (XSLFTableStyle style : styles.getStyles()) {\r
+ if (style.getStyleId().equals(styleId)) {\r
+ return style;\r
+ }\r
+ }\r
+ return null;\r
+ }\r
+ \r
+ /* package */ void updateRowColIndexes() {\r
+ int rowIdx = 0;\r
+ for (XSLFTableRow xr : this) {\r
+ int colIdx = 0;\r
+ for (XSLFTableCell tc : xr) {\r
+ tc.setRowColIndex(rowIdx, colIdx);\r
+ colIdx++;\r
+ }\r
+ rowIdx++;\r
+ }\r
+ }\r
+\r
+ /* package */ void updateCellAnchor() {\r
+ int rows = getNumberOfRows();\r
+ int cols = getNumberOfColumns();\r
+\r
+ double colWidths[] = new double[cols];\r
+ double rowHeights[] = new double[rows];\r
+\r
+ for (int row=0; row<rows; row++) {\r
+ rowHeights[row] = getRowHeight(row);\r
+ }\r
+ for (int col=0; col<cols; col++) {\r
+ colWidths[col] = getColumnWidth(col);\r
+ }\r
+\r
+ Rectangle2D tblAnc = getAnchor();\r
+ DrawFactory df = DrawFactory.getInstance(null);\r
+ \r
+ double newY = tblAnc.getY();\r
+\r
+ // #1 pass - determine row heights, the height values might be too low or 0 ...\r
+ for (int row=0; row<rows; row++) {\r
+ double maxHeight = 0;\r
+ for (int col=0; col<cols; col++) {\r
+ XSLFTableCell tc = getCell(row, col);\r
+ if (tc.getGridSpan() != 1 || tc.getRowSpan() != 1) {\r
+ continue;\r
+ }\r
+ // need to set the anchor before height calculation\r
+ tc.setAnchor(new Rectangle2D.Double(0,0,colWidths[col],0));\r
+ DrawTextShape dts = df.getDrawable(tc);\r
+ maxHeight = Math.max(maxHeight, dts.getTextHeight());\r
+ }\r
+ rowHeights[row] = Math.max(rowHeights[row],maxHeight);\r
+ }\r
+ \r
+ // #2 pass - init properties\r
+ for (int row=0; row<rows; row++) {\r
+ double newX = tblAnc.getX();\r
+ for (int col=0; col<cols; col++) {\r
+ Rectangle2D bounds = new Rectangle2D.Double(newX, newY, colWidths[col], rowHeights[row]);\r
+ XSLFTableCell tc = getCell(row, col);\r
+ tc.setAnchor(bounds);\r
+ newX += colWidths[col]+DrawTableShape.borderSize;\r
+ }\r
+ newY += rowHeights[row]+DrawTableShape.borderSize;\r
+ }\r
+ \r
+ // #3 pass - update merge info\r
+ for (int row=0; row<rows; row++) {\r
+ for (int col=0; col<cols; col++) {\r
+ XSLFTableCell tc = getCell(row, col);\r
+ Rectangle2D mergedBounds = tc.getAnchor();\r
+ for (int col2=col+1; col2<col+tc.getGridSpan(); col2++) {\r
+ assert(col2 < cols);\r
+ XSLFTableCell tc2 = getCell(row, col2);\r
+ assert(tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1);\r
+ mergedBounds.add(tc2.getAnchor());\r
+ }\r
+ for (int row2=row+1; row2<row+tc.getRowSpan(); row2++) {\r
+ assert(row2 < rows);\r
+ XSLFTableCell tc2 = getCell(row2, col);\r
+ assert(tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1);\r
+ mergedBounds.add(tc2.getAnchor());\r
+ }\r
+ tc.setAnchor(mergedBounds);\r
+ }\r
+ }\r
+ \r
+ }\r
}\r
package org.apache.poi.xslf.usermodel;\r
\r
import java.awt.Color;\r
+import java.awt.geom.Rectangle2D;\r
\r
import org.apache.poi.sl.draw.DrawPaint;\r
import org.apache.poi.sl.usermodel.PaintStyle;\r
import org.apache.poi.sl.usermodel.TableCell;\r
import org.apache.poi.sl.usermodel.VerticalAlignment;\r
import org.apache.poi.util.Units;\r
+import org.apache.poi.xslf.usermodel.XSLFTableStyle.TablePartStyle;\r
+import org.apache.xmlbeans.XmlObject;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTFillProperties;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTFontReference;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineEndProperties;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTLineProperties;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTSRgbColor;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPoint2D;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTPositiveSize2D;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTRegularTextRun;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTSchemeColor;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTSolidColorFillProperties;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTable;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCell;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableCellProperties;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTablePartStyle;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTableProperties;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyleTextStyle;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTTextBody;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTextParagraph;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTransform2D;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STCompoundLine;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STLineCap;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndLength;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndType;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STLineEndWidth;\r
+import org.openxmlformats.schemas.drawingml.x2006.main.STOnOffStyleType;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STPenAlignment;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STPresetLineDashVal;\r
import org.openxmlformats.schemas.drawingml.x2006.main.STTextAnchoringType;\r
*/\r
public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape,XSLFTextParagraph> {\r
private CTTableCellProperties _tcPr = null;\r
+ private final XSLFTable table;\r
+ private int row = 0, col = 0;\r
\r
- /*package*/ XSLFTableCell(CTTableCell cell, XSLFSheet sheet){\r
- super(cell, sheet);\r
+ /**\r
+ * Volatile/temporary anchor - e.g. for rendering\r
+ */\r
+ private Rectangle2D anchor = null;\r
+\r
+ /*package*/ XSLFTableCell(CTTableCell cell, XSLFTable table){\r
+ super(cell, table.getSheet());\r
+ this.table = table;\r
}\r
\r
@Override\r
protected CTTextBody getTextBody(boolean create){\r
- CTTableCell cell = (CTTableCell)getXmlObject();\r
+ CTTableCell cell = getCell();\r
CTTextBody txBody = cell.getTxBody();\r
if (txBody == null && create) {\r
txBody = cell.addNewTxBody();\r
\r
protected CTTableCellProperties getCellProperties(boolean create) {\r
if (_tcPr == null) {\r
- CTTableCell cell = (CTTableCell)getXmlObject();\r
+ CTTableCell cell = getCell();\r
_tcPr = cell.getTcPr();\r
if (_tcPr == null && create) {\r
_tcPr = cell.addNewTcPr();\r
}\r
};\r
}\r
- \r
+\r
@Override\r
public void setBorderStyle(BorderEdge edge, StrokeStyle style) {\r
if (style == null) {\r
throw new IllegalArgumentException("StrokeStyle needs to be specified.");\r
}\r
- \r
+\r
LineCap cap = style.getLineCap();\r
if (cap != null) {\r
setBorderCap(edge, cap);\r
}\r
- \r
+\r
LineCompound compound = style.getLineCompound();\r
if (compound != null) {\r
setBorderCompound(edge, compound);\r
}\r
- \r
+\r
LineDash dash = style.getLineDash();\r
if (dash != null) {\r
setBorderDash(edge, dash);\r
}\r
- \r
+\r
double width = style.getLineWidth();\r
setBorderWidth(edge, width);\r
}\r
}\r
\r
CTLineProperties ln = setBorderDefaults(edge);\r
-\r
- CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();\r
- rgb.setVal(new byte[]{(byte)color.getRed(), (byte)color.getGreen(), (byte)color.getBlue()});\r
- ln.addNewSolidFill().setSrgbClr(rgb);\r
+ CTSolidColorFillProperties fill = ln.addNewSolidFill();\r
+ XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr());\r
+ c.setColor(color);\r
}\r
\r
public Color getBorderColor(BorderEdge edge) {\r
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill()) return null;\r
\r
CTSolidColorFillProperties fill = ln.getSolidFill();\r
- if (!fill.isSetSrgbClr()) {\r
- // TODO for now return null for all colors except explicit RGB\r
- return null;\r
- }\r
- byte[] val = fill.getSrgbClr().getVal();\r
- return new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]);\r
+ XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr());\r
+ return c.getColor();\r
}\r
\r
public LineCompound getBorderCompound(BorderEdge edge) {\r
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill() || !ln.isSetCap()) {\r
return null;\r
}\r
- \r
+\r
return LineCap.fromOoxmlId(ln.getCap().intValue());\r
}\r
\r
CTTableCellProperties spPr = getCellProperties(true);\r
if (color == null) {\r
if(spPr.isSetSolidFill()) spPr.unsetSolidFill();\r
- }\r
- else {\r
+ } else {\r
CTSolidColorFillProperties fill = spPr.isSetSolidFill() ? spPr.getSolidFill() : spPr.addNewSolidFill();\r
-\r
- CTSRgbColor rgb = CTSRgbColor.Factory.newInstance();\r
- rgb.setVal(new byte[]{(byte) color.getRed(), (byte) color.getGreen(), (byte) color.getBlue()});\r
-\r
- fill.setSrgbClr(rgb);\r
+ XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr());\r
+ c.setColor(color);\r
}\r
}\r
\r
@Override\r
public Color getFillColor(){\r
CTTableCellProperties spPr = getCellProperties(false);\r
- if (spPr == null || !spPr.isSetSolidFill()) return null;\r
+ if (spPr == null || !spPr.isSetSolidFill()) {\r
+ return null;\r
+ }\r
\r
CTSolidColorFillProperties fill = spPr.getSolidFill();\r
- if (!fill.isSetSrgbClr()) {\r
- // TODO for now return null for all colors except explicit RGB\r
+ XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr());\r
+ return c.getColor();\r
+ }\r
+\r
+ @SuppressWarnings("resource")\r
+ @Override\r
+ public PaintStyle getFillPaint() {\r
+ Color c = getFillColor();\r
+ if (c != null) {\r
+ return DrawPaint.createSolidPaint(c);\r
+ }\r
+\r
+ CTTablePartStyle tps = getTablePartStyle(null);\r
+ if (tps == null || !tps.isSetTcStyle()) {\r
+ tps = getTablePartStyle(TablePartStyle.wholeTbl);\r
+ if (tps == null || !tps.isSetTcStyle()) {\r
+ return null;\r
+ }\r
+ }\r
+\r
+ XMLSlideShow slideShow = table.getSheet().getSlideShow();\r
+ assert(slideShow != null);\r
+ XSLFTheme theme = slideShow.getSlides().get(0).getTheme();\r
+ CTFillProperties pr = tps.getTcStyle().getFill();\r
+\r
+ for (XmlObject obj : pr.selectPath("*")) {\r
+ PaintStyle paint = XSLFShape.selectPaint(obj, null, slideShow.getPackagePart(), theme);\r
+\r
+ if (paint != null) {\r
+ return paint;\r
+ }\r
+ }\r
+\r
+ return null;\r
+ }\r
+\r
+ /**\r
+ * Retrieves the part style depending on the location of this cell\r
+ *\r
+ * @param tablePartStyle the part to be returned, usually this is null\r
+ * and only set when used as a helper method\r
+ * @return the table part style\r
+ */\r
+ private CTTablePartStyle getTablePartStyle(TablePartStyle tablePartStyle) {\r
+ CTTable ct = table.getCTTable();\r
+ if (!ct.isSetTblPr()) {\r
+ return null;\r
+ }\r
+\r
+ CTTableProperties pr = ct.getTblPr();\r
+ boolean bandRow = (pr.isSetBandRow() && pr.getBandRow());\r
+ boolean firstRow = (pr.isSetFirstRow() && pr.getFirstRow());\r
+ boolean lastRow = (pr.isSetLastRow() && pr.getLastRow());\r
+ boolean bandCol = (pr.isSetBandCol() && pr.getBandCol());\r
+ boolean firstCol = (pr.isSetFirstCol() && pr.getFirstCol());\r
+ boolean lastCol = (pr.isSetLastCol() && pr.getLastCol());\r
+\r
+ TablePartStyle tps;\r
+ if (tablePartStyle != null) {\r
+ tps = tablePartStyle;\r
+ } else if (row == 0 && firstRow) {\r
+ tps = TablePartStyle.firstRow;\r
+ } else if (row == table.getNumberOfRows()-1 && lastRow) {\r
+ tps = TablePartStyle.lastRow;\r
+ } else if (col == 0 && firstCol) {\r
+ tps = TablePartStyle.firstCol;\r
+ } else if (col == table.getNumberOfColumns()-1 && lastCol) {\r
+ tps = TablePartStyle.lastCol;\r
+ } else {\r
+ tps = TablePartStyle.wholeTbl;\r
+\r
+ int br = row + (firstRow ? 1 : 0);\r
+ int bc = col + (firstCol ? 1 : 0);\r
+ if (bandRow && (br & 1) == 0) {\r
+ tps = TablePartStyle.band1H;\r
+ } else if (bandCol && (bc & 1) == 0) {\r
+ tps = TablePartStyle.band1V;\r
+ }\r
+ }\r
+\r
+ XSLFTableStyle tabStyle = table.getTableStyle();\r
+ if (tabStyle == null) {\r
return null;\r
}\r
- byte[] val = fill.getSrgbClr().getVal();\r
- return new Color(0xFF & val[0], 0xFF & val[1], 0xFF & val[2]);\r
+\r
+ CTTablePartStyle part = tabStyle.getTablePartStyle(tps);\r
+ return (part == null) ? tabStyle.getTablePartStyle(TablePartStyle.wholeTbl) : part;\r
}\r
\r
void setGridSpan(int gridSpan_) {\r
- ((CTTableCell)getXmlObject()).setGridSpan(gridSpan_);\r
+ getCell().setGridSpan(gridSpan_);\r
+ }\r
+\r
+ @Override\r
+ public int getGridSpan() {\r
+ CTTableCell c = getCell();\r
+ return (c.isSetGridSpan()) ? c.getGridSpan() : 1;\r
}\r
\r
void setRowSpan(int rowSpan_) {\r
- ((CTTableCell)getXmlObject()).setRowSpan(rowSpan_);\r
+ getCell().setRowSpan(rowSpan_);\r
+ }\r
+\r
+ @Override\r
+ public int getRowSpan() {\r
+ CTTableCell c = getCell();\r
+ return (c.isSetRowSpan()) ? c.getRowSpan() : 1;\r
}\r
\r
void setHMerge(boolean merge_) {\r
- ((CTTableCell)getXmlObject()).setHMerge(merge_);\r
+ getCell().setHMerge(merge_);\r
}\r
\r
void setVMerge(boolean merge_) {\r
- ((CTTableCell)getXmlObject()).setVMerge(merge_);\r
+ getCell().setVMerge(merge_);\r
}\r
\r
@Override\r
vt = STTextVerticalType.WORD_ART_VERT;\r
break;\r
}\r
- \r
+\r
cellProps.setVert(vt);\r
}\r
}\r
} else {\r
orientation = STTextVerticalType.HORZ;\r
}\r
- \r
+\r
switch (orientation.intValue()) {\r
default:\r
case STTextVerticalType.INT_HORZ:\r
return TextDirection.STACKED;\r
}\r
}\r
-}\r
+\r
+ private CTTableCell getCell() {\r
+ return (CTTableCell)getXmlObject();\r
+ }\r
+\r
+ /* package */ void setRowColIndex(int row, int col) {\r
+ this.row = row;\r
+ this.col = col;\r
+ }\r
+\r
+ /**\r
+ * Return a fake-xfrm which is used for calculating the text height\r
+ */\r
+ protected CTTransform2D getXfrm() {\r
+ Rectangle2D anc = getAnchor();\r
+ CTTransform2D xfrm = CTTransform2D.Factory.newInstance();\r
+ CTPoint2D off = xfrm.addNewOff();\r
+ off.setX(Units.toEMU(anc.getX()));\r
+ off.setY(Units.toEMU(anc.getY()));\r
+ CTPositiveSize2D size = xfrm.addNewExt();\r
+ size.setCx(Units.toEMU(anc.getWidth()));\r
+ size.setCy(Units.toEMU(anc.getHeight()));\r
+ return xfrm;\r
+ }\r
+\r
+ /**\r
+ * There's no real anchor for table cells - this method is used to temporarily store the location\r
+ * of the cell for a later retrieval, e.g. for rendering\r
+ *\r
+ * @since POI 3.15-beta2\r
+ */\r
+ @Override\r
+ public void setAnchor(Rectangle2D anchor) {\r
+ if (this.anchor == null) {\r
+ this.anchor = (Rectangle2D)anchor.clone();\r
+ } else {\r
+ this.anchor.setRect(anchor);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ @Override\r
+ public Rectangle2D getAnchor() {\r
+ if (anchor == null) {\r
+ table.updateCellAnchor();\r
+ }\r
+ // anchor should be set, after updateCellAnchor is through\r
+ assert(anchor != null);\r
+ return anchor;\r
+ }\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ @Override\r
+ public boolean isMerged() {\r
+ CTTableCell c = getCell();\r
+ return (c.isSetHMerge() && c.getHMerge()) || (c.isSetVMerge() && c.getVMerge());\r
+ }\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ @Override\r
+ protected XSLFCellTextParagraph newTextParagraph(CTTextParagraph p) {\r
+ return new XSLFCellTextParagraph(p, this);\r
+ }\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ private class XSLFCellTextParagraph extends XSLFTextParagraph {\r
+ protected XSLFCellTextParagraph(CTTextParagraph p, XSLFTextShape shape) {\r
+ super(p, shape);\r
+ }\r
+\r
+ @Override\r
+ protected XSLFCellTextRun newTextRun(CTRegularTextRun r) {\r
+ return new XSLFCellTextRun(r, this);\r
+ }\r
+ }\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ private class XSLFCellTextRun extends XSLFTextRun {\r
+ protected XSLFCellTextRun(CTRegularTextRun r, XSLFTextParagraph p) {\r
+ super(r, p);\r
+ }\r
+\r
+ @Override\r
+ public PaintStyle getFontColor(){\r
+ CTTableStyleTextStyle txStyle = getTextStyle();\r
+ if (txStyle == null) {\r
+ return super.getFontColor();\r
+ }\r
+\r
+ CTSchemeColor phClr = null;\r
+ CTFontReference fontRef = txStyle.getFontRef();\r
+ if (fontRef != null) {\r
+ phClr = fontRef.getSchemeClr();\r
+ }\r
+ \r
+ XSLFTheme theme = getSheet().getTheme();\r
+ final XSLFColor c = new XSLFColor(txStyle, theme, phClr);\r
+ return DrawPaint.createSolidPaint(c.getColorStyle());\r
+ }\r
+\r
+ @Override\r
+ public boolean isBold() {\r
+ CTTableStyleTextStyle txStyle = getTextStyle();\r
+ if (txStyle == null) {\r
+ return super.isBold();\r
+ } else {\r
+ return txStyle.isSetB() && txStyle.getB().intValue() == STOnOffStyleType.INT_ON;\r
+ }\r
+ }\r
+\r
+ @Override\r
+ public boolean isItalic() {\r
+ CTTableStyleTextStyle txStyle = getTextStyle();\r
+ if (txStyle == null) {\r
+ return super.isItalic();\r
+ } else {\r
+ return txStyle.isSetI() && txStyle.getI().intValue() == STOnOffStyleType.INT_ON;\r
+ }\r
+ }\r
+ \r
+ private CTTableStyleTextStyle getTextStyle() {\r
+ CTTablePartStyle tps = getTablePartStyle(null);\r
+ if (tps == null || !tps.isSetTcTxStyle()) {\r
+ tps = getTablePartStyle(TablePartStyle.wholeTbl);\r
+ }\r
+ return (tps == null) ? null : tps.getTcTxStyle();\r
+ }\r
+ }\r
+}
\ No newline at end of file
\r
/**\r
* Represents a table in a .pptx presentation\r
- *\r
- * @author Yegor Kozlov\r
*/\r
public class XSLFTableRow implements Iterable<XSLFTableCell> {\r
private CTTableRow _row;\r
CTTableCell[] tcArray = _row.getTcArray();\r
_cells = new ArrayList<XSLFTableCell>(tcArray.length);\r
for(CTTableCell cell : tcArray) {\r
- _cells.add(new XSLFTableCell(cell, table.getSheet()));\r
+ _cells.add(new XSLFTableCell(cell, table));\r
}\r
}\r
\r
public XSLFTableCell addCell(){\r
CTTableCell c = _row.addNewTc();\r
c.set(XSLFTableCell.prototype());\r
- XSLFTableCell cell = new XSLFTableCell(c, _table.getSheet());\r
+ XSLFTableCell cell = new XSLFTableCell(c, _table);\r
_cells.add(cell);\r
\r
if(_table.getNumberOfColumns() < _row.sizeOfTcArray()) {\r
_table.getCTTable().getTblGrid().addNewGridCol().setW(Units.toEMU(100.0)); \r
}\r
+ _table.updateRowColIndexes();\r
return cell;\r
}\r
\r
\r
package org.apache.poi.xslf.usermodel;\r
\r
+import org.openxmlformats.schemas.drawingml.x2006.main.CTTablePartStyle;\r
import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle;\r
\r
/**\r
- * Represents a table in a .pptx presentation\r
- *\r
- * @author Yegor Kozlov\r
+ * Represents a table style in a .pptx presentation\r
*/\r
public class XSLFTableStyle {\r
private CTTableStyle _tblStyle;\r
\r
+ public enum TablePartStyle {\r
+ wholeTbl, band1H, band2H, band1V, band2V, firstCol, lastCol, firstRow, lastRow, seCell, swCell, neCell, nwCell;\r
+ }\r
+ \r
/*package*/ XSLFTableStyle(CTTableStyle style){\r
_tblStyle = style;\r
}\r
public String getStyleId(){\r
return _tblStyle.getStyleId();\r
}\r
+\r
+ /**\r
+ * @since POI 3.15-beta2\r
+ */\r
+ protected CTTablePartStyle getTablePartStyle(TablePartStyle tps) {\r
+ switch (tps) {\r
+ default:\r
+ case wholeTbl:\r
+ return _tblStyle.getWholeTbl();\r
+ case band1H:\r
+ return _tblStyle.getBand1H();\r
+ case band2H:\r
+ return _tblStyle.getBand2H();\r
+ case band1V:\r
+ return _tblStyle.getBand1V();\r
+ case band2V:\r
+ return _tblStyle.getBand2V();\r
+ case firstCol:\r
+ return _tblStyle.getFirstCol();\r
+ case lastCol:\r
+ return _tblStyle.getLastCol();\r
+ case firstRow:\r
+ return _tblStyle.getFirstRow();\r
+ case lastRow:\r
+ return _tblStyle.getLastRow();\r
+ case seCell:\r
+ return _tblStyle.getSeCell();\r
+ case swCell:\r
+ return _tblStyle.getSwCell();\r
+ case neCell:\r
+ return _tblStyle.getNeCell();\r
+ case nwCell:\r
+ return _tblStyle.getNwCell();\r
+ }\r
+ }\r
}
\ No newline at end of file
==================================================================== */
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;
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>{
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){
* Represents a paragraph of text within the containing text body.\r
* The paragraph is the highest level text separation mechanism.\r
*\r
- * @author Yegor Kozlov\r
* @since POI-3.8\r
*/\r
@Beta\r
for(XmlObject ch : _p.selectPath("*")){\r
if(ch instanceof CTRegularTextRun){\r
CTRegularTextRun r = (CTRegularTextRun)ch;\r
- _runs.add(new XSLFTextRun(r, this));\r
+ _runs.add(newTextRun(r));\r
} else if (ch instanceof CTTextLineBreak){\r
CTTextLineBreak br = (CTTextLineBreak)ch;\r
CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();\r
r.setRPr(br.getRPr());\r
r.setT("\n");\r
- _runs.add(new XSLFTextRun(r, this));\r
+ _runs.add(newTextRun(r));\r
} else if (ch instanceof CTTextField){\r
CTTextField f = (CTTextField)ch;\r
CTRegularTextRun r = CTRegularTextRun.Factory.newInstance();\r
r.setRPr(f.getRPr());\r
r.setT(f.getT());\r
- _runs.add(new XSLFTextRun(r, this));\r
+ _runs.add(newTextRun(r));\r
}\r
}\r
}\r
CTRegularTextRun r = _p.addNewR();\r
CTTextCharacterProperties rPr = r.addNewRPr();\r
rPr.setLang("en-US");\r
- XSLFTextRun run = new XSLFTextRun(r, this);\r
+ XSLFTextRun run = newTextRun(r);\r
_runs.add(run);\r
return run;\r
}\r
int level = getIndentLevel();\r
\r
// wind up and find the root master sheet which must be slide master\r
- final String nsDecl =\r
- "declare namespace p='http://schemas.openxmlformats.org/presentationml/2006/main' " +\r
- "declare namespace a='http://schemas.openxmlformats.org/drawingml/2006/main' ";\r
- final String xpaths[] = {\r
- nsDecl+".//p:txStyles/p:" + defaultStyleSelector +"/a:lvl" +(level+1)+ "pPr",\r
- nsDecl+".//p:notesStyle/a:lvl" +(level+1)+ "pPr"\r
- };\r
+ final String nsPML = "http://schemas.openxmlformats.org/presentationml/2006/main";\r
+ final String nsDML = "http://schemas.openxmlformats.org/drawingml/2006/main";\r
XSLFSheet masterSheet = _shape.getSheet();\r
for (XSLFSheet m = masterSheet; m != null; m = (XSLFSheet)m.getMasterSheet()) {\r
masterSheet = m;\r
XmlObject xo = masterSheet.getXmlObject();\r
- for (String xpath : xpaths) {\r
- XmlObject[] o = xo.selectPath(xpath);\r
- if (o.length == 1) {\r
- return (CTTextParagraphProperties)o[0];\r
+ XmlCursor cur = xo.newCursor();\r
+ try {\r
+ cur.push();\r
+ if ((cur.toChild(nsPML, "txStyles") && cur.toChild(nsPML, defaultStyleSelector)) ||\r
+ (cur.pop() && cur.toChild(nsPML, "notesStyle"))) {\r
+ if (cur.toChild(nsDML, "lvl" +(level+1)+ "pPr")) {\r
+ return (CTTextParagraphProperties)cur.getObject();\r
+ }\r
}\r
+ } finally {\r
+ cur.dispose();\r
}\r
}\r
\r
List<XSLFTextRun> otherRs = other.getTextRuns();\r
int i=0;\r
for(CTRegularTextRun rtr : thisP.getRArray()) {\r
- XSLFTextRun run = new XSLFTextRun(rtr, this);\r
+ XSLFTextRun run = newTextRun(rtr);\r
run.copy(otherRs.get(i++));\r
_runs.add(run);\r
}\r
@Override\r
public Double getDefaultFontSize() {\r
CTTextCharacterProperties endPr = _p.getEndParaRPr();\r
+ if (endPr == null || !endPr.isSetSz()) {\r
+ endPr = getDefaultMasterStyle().getDefRPr();\r
+ }\r
return (endPr == null || !endPr.isSetSz()) ? 12 : (endPr.getSz() / 100.);\r
}\r
\r
return false;\r
}\r
}\r
+\r
+ /**\r
+ * Helper method to allow subclasses to provide their own text run\r
+ *\r
+ * @param r the xml reference\r
+ * \r
+ * @return a new text paragraph\r
+ * \r
+ * @since POI 3.15-beta2\r
+ */\r
+ protected XSLFTextRun newTextRun(CTRegularTextRun r) {\r
+ return new XSLFTextRun(r, this);\r
+ }\r
}
\ No newline at end of file
/**\r
* Represents a run of text within the containing text body. The run element is the\r
* lowest level text separation mechanism within a text body.\r
- *\r
- * @author Yegor Kozlov\r
*/\r
@Beta\r
public class XSLFTextRun implements TextRun {\r
private final CTRegularTextRun _r;\r
private final XSLFTextParagraph _p;\r
\r
- XSLFTextRun(CTRegularTextRun r, XSLFTextParagraph p){\r
+ protected XSLFTextRun(CTRegularTextRun r, XSLFTextParagraph p){\r
_r = r;\r
_p = p;\r
}\r
CTTextBody txBody = getTextBody(false);
if (txBody != null) {
for (CTTextParagraph p : txBody.getPArray()) {
- _paragraphs.add(new XSLFTextParagraph(p, this));
+ _paragraphs.add(newTextParagraph(p));
}
}
}
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;
// copy properties from last paragraph / textrun or paragraph end marker
CTTextParagraphProperties pPr = null;
CTTextCharacterProperties rPr = null;
-
+
boolean firstPara;
XSLFTextParagraph para;
if (_paragraphs.isEmpty()) {
rPr = ctp.getEndParaRPr();
}
}
-
+
XSLFTextRun run = null;
for (String lineTxt : text.split("\\r\\n?|\\n")) {
if (!firstPara) {
}
firstPara = false;
}
-
+
assert(run != null);
return run;
}
} else {
p = txBody.addNewP();
}
- XSLFTextParagraph paragraph = new XSLFTextParagraph(p, this);
+ XSLFTextParagraph paragraph = newTextParagraph(p);
_paragraphs.add(paragraph);
return paragraph;
}
fetchShapeProperty(fetcher);
return fetcher.getValue() == null ? false : fetcher.getValue();
}
-
+
@Override
public void setTextDirection(TextDirection orientation){
CTTextBodyProperties bodyPr = getTextBodyPr(true);
if (bodyPr != null && bodyPr.isSetRot()) {
return bodyPr.getRot() / 60000.;
}
- return null;
+ return null;
}
-
+
@Override
public void setTextRotation(Double rotation) {
CTTextBodyProperties bodyPr = getTextBodyPr(true);
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.
Insets2D insets = new Insets2D(getTopInset(), getLeftInset(), getBottomInset(), getRightInset());
return insets;
}
-
+
@Override
public void setInsets(Insets2D insets) {
setTopInset(insets.top);
setBottomInset(insets.bottom);
setRightInset(insets.right);
}
-
+
@Override
public boolean getWordWrap(){
PropertyFetcher<Boolean> fetcher = new TextBodyPropertyFetcher<Boolean>(){
}
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;
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){
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);
}
clearText();
-
+
for (XSLFTextParagraph srcP : otherTS.getTextParagraphs()) {
XSLFTextParagraph tgtP = addNewTextParagraph();
tgtP.copy(srcP);
default:
case NOTES:
case HALF_BODY:
- case QUARTER_BODY:
+ case QUARTER_BODY:
case BODY:
setPlaceholder(Placeholder.BODY);
break;
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);
}
-
-
}
\ No newline at end of file
import static org.junit.Assert.assertEquals;\r
import static org.junit.Assert.assertNotNull;\r
\r
+import java.io.IOException;\r
+\r
import org.junit.Test;\r
-import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle;\r
\r
-/**\r
- * @author Yegor Kozlov\r
- */\r
public class TestXSLFTableStyles {\r
\r
@Test\r
- public void testRead(){\r
+ public void testRead() throws IOException {\r
XMLSlideShow ppt = new XMLSlideShow();\r
XSLFTableStyles tblStyles = ppt.getTableStyles();\r
assertNotNull(tblStyles);\r
\r
assertEquals(0, tblStyles.getStyles().size());\r
- }\r
-\r
- @SuppressWarnings("unused")\r
- @Test\r
- public void testStyle(){\r
- CTTableStyle obj = CTTableStyle.Factory.newInstance();\r
- XSLFTableStyle style = new XSLFTableStyle(obj);\r
+ \r
+ ppt.close();\r
}\r
}
\ No newline at end of file
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;
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()) {
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;
// 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();
/**
/**
* 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;
/* 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.
*
@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));
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) {
}
return (l == null) ? null : l.getLineWidth();
}
-
+
@Override
public void setBorderWidth(BorderEdge edge, double width) {
HSLFLine l = addLine(edge);
if (edge == null || color == null) {
throw new IllegalArgumentException("BorderEdge and/or Color need to be specified.");
}
-
+
HSLFLine l = addLine(edge);
l.setLineColor(color);
}
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);
}
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);
}
setEscherProperty(opt, EscherProperties.THREED__LIGHTFACE, 0x80000);
anchorBorder(edge, line);
-
+
return line;
}
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;
+ }
}