blob: d30bfa552112395948e1a9be0654766f4b368179 [file] [log] [blame]
/*
* ====================================================================
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* ====================================================================
*/
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.ColorStyle;
import org.apache.poi.sl.usermodel.PaintStyle;
import org.apache.poi.sl.usermodel.PaintStyle.SolidPaint;
import org.apache.poi.sl.usermodel.StrokeStyle;
import org.apache.poi.sl.usermodel.StrokeStyle.LineCap;
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.VerticalAlignment;
import org.apache.poi.util.Units;
import org.apache.poi.xddf.usermodel.XDDFLineProperties;
import org.apache.poi.xddf.usermodel.text.XDDFTextBody;
import org.apache.poi.xslf.usermodel.XSLFPropertiesDelegate.XSLFFillProperties;
import org.apache.poi.xslf.usermodel.XSLFTableStyle.TablePartStyle;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.drawingml.x2006.main.*;
/**
* Represents a cell of a table in a .pptx presentation
*/
public class XSLFTableCell extends XSLFTextShape implements TableCell<XSLFShape, XSLFTextParagraph> {
private CTTableCellProperties _tcPr;
private final XSLFTable table;
private int row, col;
/**
* Volatile/temporary anchor - e.g. for rendering
*/
private Rectangle2D anchor;
/* package */ XSLFTableCell(CTTableCell cell, XSLFTable table) {
super(cell, table.getSheet());
this.table = table;
}
@Override
protected CTTextBody getTextBody(boolean create) {
CTTableCell cell = getCell();
CTTextBody txBody = cell.getTxBody();
if (txBody == null && create) {
XDDFTextBody body = new XDDFTextBody(this);
cell.setTxBody(body.getXmlObject());
txBody = cell.getTxBody();
}
return txBody;
}
static CTTableCell prototype() {
CTTableCell cell = CTTableCell.Factory.newInstance();
CTTableCellProperties pr = cell.addNewTcPr();
pr.addNewLnL().addNewNoFill();
pr.addNewLnR().addNewNoFill();
pr.addNewLnT().addNewNoFill();
pr.addNewLnB().addNewNoFill();
return cell;
}
@SuppressWarnings("WeakerAccess")
protected CTTableCellProperties getCellProperties(boolean create) {
if (_tcPr == null) {
CTTableCell cell = getCell();
_tcPr = cell.getTcPr();
if (_tcPr == null && create) {
_tcPr = cell.addNewTcPr();
}
}
return _tcPr;
}
@Override
public void setLeftInset(double margin) {
CTTableCellProperties pr = getCellProperties(true);
pr.setMarL(Units.toEMU(margin));
}
@Override
public void setRightInset(double margin) {
CTTableCellProperties pr = getCellProperties(true);
pr.setMarR(Units.toEMU(margin));
}
@Override
public void setTopInset(double margin) {
CTTableCellProperties pr = getCellProperties(true);
pr.setMarT(Units.toEMU(margin));
}
@Override
public void setBottomInset(double margin) {
CTTableCellProperties pr = getCellProperties(true);
pr.setMarB(Units.toEMU(margin));
}
private CTLineProperties getCTLine(BorderEdge edge, boolean create) {
if (edge == null) {
throw new IllegalArgumentException("BorderEdge needs to be specified.");
}
CTTableCellProperties pr = getCellProperties(create);
if (pr == null) {
return null;
}
switch (edge) {
case bottom:
return (pr.isSetLnB()) ? pr.getLnB() : (create ? pr.addNewLnB() : null);
case left:
return (pr.isSetLnL()) ? pr.getLnL() : (create ? pr.addNewLnL() : null);
case top:
return (pr.isSetLnT()) ? pr.getLnT() : (create ? pr.addNewLnT() : null);
case right:
return (pr.isSetLnR()) ? pr.getLnR() : (create ? pr.addNewLnR() : null);
default:
return null;
}
}
public XDDFLineProperties getBorderProperties(BorderEdge edge) {
CTLineProperties props = getCTLine(edge, false);
return (props == null) ? null : new XDDFLineProperties(props);
}
public void setBorderProperties(BorderEdge edge, XDDFLineProperties properties) {
CTLineProperties props = getCTLine(edge, true);
if (props != null) {
props.set(properties.getXmlObject().copy());
}
}
@Override
public void removeBorder(BorderEdge edge) {
CTTableCellProperties pr = getCellProperties(false);
if (pr == null) {
return;
}
switch (edge) {
case bottom:
if (pr.isSetLnB()) {
pr.unsetLnB();
}
break;
case left:
if (pr.isSetLnL()) {
pr.unsetLnL();
}
break;
case top:
if (pr.isSetLnT()) {
pr.unsetLnT();
}
break;
case right:
if (pr.isSetLnR()) {
pr.unsetLnR();
}
break;
default:
throw new IllegalArgumentException();
}
}
@Override
public StrokeStyle getBorderStyle(final BorderEdge edge) {
final Double width = getBorderWidth(edge);
return (width == null) ? null : new StrokeStyle() {
@Override
public PaintStyle getPaint() {
return DrawPaint.createSolidPaint(getBorderColor(edge));
}
@Override
public LineCap getLineCap() {
return getBorderCap(edge);
}
@Override
public LineDash getLineDash() {
return getBorderDash(edge);
}
@Override
public LineCompound getLineCompound() {
return getBorderCompound(edge);
}
@Override
public double getLineWidth() {
return width;
}
};
}
@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);
}
@SuppressWarnings("WeakerAccess")
public Double getBorderWidth(BorderEdge edge) {
CTLineProperties ln = getCTLine(edge, false);
return (ln == null || !ln.isSetW()) ? null : Units.toPoints(ln.getW());
}
@Override
public void setBorderWidth(BorderEdge edge, double width) {
final CTLineProperties ln = getCTLine(edge, true);
if (ln == null) {
return;
}
ln.setW(Units.toEMU(width));
}
private CTLineProperties setBorderDefaults(BorderEdge edge) {
final CTLineProperties ln = getCTLine(edge, true);
if (ln == null) {
throw new IllegalStateException("CTLineProperties couldn't be initialized");
}
if (ln.isSetNoFill()) {
ln.unsetNoFill();
}
if (!ln.isSetPrstDash()) {
ln.addNewPrstDash().setVal(STPresetLineDashVal.SOLID);
}
if (!ln.isSetCmpd()) {
ln.setCmpd(STCompoundLine.SNG);
}
if (!ln.isSetAlgn()) {
ln.setAlgn(STPenAlignment.CTR);
}
if (!ln.isSetCap()) {
ln.setCap(STLineCap.FLAT);
}
if (!ln.isSetRound()) {
ln.addNewRound();
}
if (!ln.isSetHeadEnd()) {
CTLineEndProperties hd = ln.addNewHeadEnd();
hd.setType(STLineEndType.NONE);
hd.setW(STLineEndWidth.MED);
hd.setLen(STLineEndLength.MED);
}
if (!ln.isSetTailEnd()) {
CTLineEndProperties tl = ln.addNewTailEnd();
tl.setType(STLineEndType.NONE);
tl.setW(STLineEndWidth.MED);
tl.setLen(STLineEndLength.MED);
}
return ln;
}
@Override
public void setBorderColor(BorderEdge edge, Color color) {
if (color == null) {
throw new IllegalArgumentException("Colors need to be specified.");
}
CTLineProperties ln = setBorderDefaults(edge);
CTSolidColorFillProperties fill = ln.addNewSolidFill();
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr(), getSheet());
c.setColor(color);
}
@SuppressWarnings("WeakerAccess")
public Color getBorderColor(BorderEdge edge) {
CTLineProperties ln = getCTLine(edge, false);
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill()) {
return null;
}
CTSolidColorFillProperties fill = ln.getSolidFill();
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr(), getSheet());
return c.getColor();
}
@SuppressWarnings("WeakerAccess")
public LineCompound getBorderCompound(BorderEdge edge) {
CTLineProperties ln = getCTLine(edge, false);
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill() || !ln.isSetCmpd()) {
return null;
}
return LineCompound.fromOoxmlId(ln.getCmpd().intValue());
}
@Override
public void setBorderCompound(BorderEdge edge, LineCompound compound) {
if (compound == null) {
throw new IllegalArgumentException("LineCompound need to be specified.");
}
CTLineProperties ln = setBorderDefaults(edge);
ln.setCmpd(STCompoundLine.Enum.forInt(compound.ooxmlId));
}
@SuppressWarnings("WeakerAccess")
public LineDash getBorderDash(BorderEdge edge) {
CTLineProperties ln = getCTLine(edge, false);
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill() || !ln.isSetPrstDash()) {
return null;
}
return LineDash.fromOoxmlId(ln.getPrstDash().getVal().intValue());
}
@Override
public void setBorderDash(BorderEdge edge, LineDash dash) {
if (dash == null) {
throw new IllegalArgumentException("LineDash need to be specified.");
}
CTLineProperties ln = setBorderDefaults(edge);
if (!ln.isSetPrstDash()) {
ln.addNewPrstDash();
}
ln.getPrstDash().setVal(STPresetLineDashVal.Enum.forInt(dash.ooxmlId));
}
@SuppressWarnings("WeakerAccess")
public LineCap getBorderCap(BorderEdge edge) {
CTLineProperties ln = getCTLine(edge, false);
if (ln == null || ln.isSetNoFill() || !ln.isSetSolidFill() || !ln.isSetCap()) {
return null;
}
return LineCap.fromOoxmlId(ln.getCap().intValue());
}
@SuppressWarnings("WeakerAccess")
public void setBorderCap(BorderEdge edge, LineCap cap) {
if (cap == null) {
throw new IllegalArgumentException("LineCap need to be specified.");
}
CTLineProperties ln = setBorderDefaults(edge);
ln.setCap(STLineCap.Enum.forInt(cap.ooxmlId));
}
/**
* Specifies a solid color fill. The shape is filled entirely with the
* specified color.
*
* @param color
* the solid color fill. The value of <code>null</code> unsets
* the solidFIll attribute from the underlying xml
*/
@Override
public void setFillColor(Color color) {
CTTableCellProperties spPr = getCellProperties(true);
if (color == null) {
if (spPr.isSetSolidFill()) {
spPr.unsetSolidFill();
}
} else {
CTSolidColorFillProperties fill = spPr.isSetSolidFill() ? spPr.getSolidFill() : spPr.addNewSolidFill();
XSLFColor c = new XSLFColor(fill, getSheet().getTheme(), fill.getSchemeClr(), getSheet());
c.setColor(color);
}
}
/**
*
* @return solid fill color of null if not set
*/
@Override
public Color getFillColor() {
PaintStyle ps = getFillPaint();
if (ps instanceof SolidPaint) {
ColorStyle cs = ((SolidPaint) ps).getSolidColor();
return DrawPaint.applyColorTransform(cs);
}
return null;
}
@SuppressWarnings("resource")
@Override
public PaintStyle getFillPaint() {
XSLFSheet sheet = getSheet();
XSLFTheme theme = sheet.getTheme();
final boolean hasPlaceholder = getPlaceholder() != null;
XmlObject props = getCellProperties(false);
XSLFFillProperties fp = XSLFPropertiesDelegate.getFillDelegate(props);
if (fp != null) {
PaintStyle paint = selectPaint(fp, null, sheet.getPackagePart(), theme, hasPlaceholder);
if (paint != null) {
return paint;
}
}
CTTablePartStyle tps = getTablePartStyle(null);
if (tps == null || !tps.isSetTcStyle()) {
tps = getTablePartStyle(TablePartStyle.wholeTbl);
if (tps == null || !tps.isSetTcStyle()) {
return null;
}
}
XMLSlideShow slideShow = sheet.getSlideShow();
CTTableStyleCellStyle tcStyle = tps.getTcStyle();
if (tcStyle.isSetFill()) {
props = tcStyle.getFill();
} else if (tcStyle.isSetFillRef()) {
props = tcStyle.getFillRef();
} else {
return null;
}
fp = XSLFPropertiesDelegate.getFillDelegate(props);
if (fp != null) {
PaintStyle paint = selectPaint(fp, null, slideShow.getPackagePart(), theme, hasPlaceholder);
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;
}
CTTablePartStyle part = tabStyle.getTablePartStyle(tps);
return (part == null) ? tabStyle.getTablePartStyle(TablePartStyle.wholeTbl) : part;
}
void setGridSpan(int gridSpan_) {
getCell().setGridSpan(gridSpan_);
}
@Override
public int getGridSpan() {
CTTableCell c = getCell();
return (c.isSetGridSpan()) ? c.getGridSpan() : 1;
}
void setRowSpan(int rowSpan_) {
getCell().setRowSpan(rowSpan_);
}
@Override
public int getRowSpan() {
CTTableCell c = getCell();
return (c.isSetRowSpan()) ? c.getRowSpan() : 1;
}
void setHMerge() {
getCell().setHMerge(true);
}
void setVMerge() {
getCell().setVMerge(true);
}
@Override
public void setVerticalAlignment(VerticalAlignment anchor) {
CTTableCellProperties cellProps = getCellProperties(true);
if (anchor == null) {
if (cellProps.isSetAnchor()) {
cellProps.unsetAnchor();
}
} else {
cellProps.setAnchor(STTextAnchoringType.Enum.forInt(anchor.ordinal() + 1));
}
}
@Override
public VerticalAlignment getVerticalAlignment() {
CTTableCellProperties cellProps = getCellProperties(false);
VerticalAlignment align = VerticalAlignment.TOP;
if (cellProps != null && cellProps.isSetAnchor()) {
int ival = cellProps.getAnchor().intValue();
align = VerticalAlignment.values()[ival - 1];
}
return align;
}
/**
* @since POI 3.15-beta2
*/
@Override
public void setTextDirection(TextDirection orientation) {
CTTableCellProperties cellProps = getCellProperties(true);
if (orientation == null) {
if (cellProps.isSetVert()) {
cellProps.unsetVert();
}
} else {
STTextVerticalType.Enum vt;
switch (orientation) {
default:
case HORIZONTAL:
vt = STTextVerticalType.HORZ;
break;
case VERTICAL:
vt = STTextVerticalType.VERT;
break;
case VERTICAL_270:
vt = STTextVerticalType.VERT_270;
break;
case STACKED:
vt = STTextVerticalType.WORD_ART_VERT;
break;
}
cellProps.setVert(vt);
}
}
/**
* @since POI 3.15-beta2
*/
@Override
public TextDirection getTextDirection() {
CTTableCellProperties cellProps = getCellProperties(false);
STTextVerticalType.Enum orientation;
if (cellProps != null && cellProps.isSetVert()) {
orientation = cellProps.getVert();
} else {
orientation = STTextVerticalType.HORZ;
}
switch (orientation.intValue()) {
default:
case STTextVerticalType.INT_HORZ:
return TextDirection.HORIZONTAL;
case STTextVerticalType.INT_VERT:
case STTextVerticalType.INT_EA_VERT:
case STTextVerticalType.INT_MONGOLIAN_VERT:
return TextDirection.VERTICAL;
case STTextVerticalType.INT_VERT_270:
return TextDirection.VERTICAL_270;
case STTextVerticalType.INT_WORD_ART_VERT:
case STTextVerticalType.INT_WORD_ART_VERT_RTL:
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);
}
@Override
protected XmlObject getShapeProperties() {
return getCellProperties(false);
}
/**
* @since POI 3.15-beta2
*/
private final class XSLFCellTextParagraph extends XSLFTextParagraph {
private XSLFCellTextParagraph(CTTextParagraph p, XSLFTextShape shape) {
super(p, shape);
}
@Override
protected XSLFCellTextRun newTextRun(XmlObject r) {
return new XSLFCellTextRun(r, this);
}
}
/**
* @since POI 3.15-beta2
*/
private final class XSLFCellTextRun extends XSLFTextRun {
private XSLFCellTextRun(XmlObject 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, getSheet());
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();
}
}
}