From d80170f9ac3e00fbed422180909e9ee94fe1cf4e Mon Sep 17 00:00:00 2001 From: Andreas Beeker Date: Sat, 4 Jun 2016 22:53:00 +0000 Subject: [PATCH] #57766 - XSLFTable isn't exported on convert slides of a .pptx slide show to a PNG/SVG image git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@1746856 13f79535-47bb-0310-9956-ffa450edef68 --- .../org/apache/poi/sl/draw/DrawFactory.java | 8 + .../org/apache/poi/sl/draw/DrawShape.java | 44 +++ .../apache/poi/sl/draw/DrawSimpleShape.java | 43 +-- .../apache/poi/sl/draw/DrawTableShape.java | 110 +++++- .../apache/poi/sl/draw/DrawTextParagraph.java | 38 +- .../org/apache/poi/sl/draw/DrawTextShape.java | 19 +- .../apache/poi/sl/usermodel/TableCell.java | 28 ++ .../apache/poi/xslf/usermodel/XSLFColor.java | 3 + .../apache/poi/xslf/usermodel/XSLFShape.java | 35 +- .../poi/xslf/usermodel/XSLFSimpleShape.java | 8 +- .../apache/poi/xslf/usermodel/XSLFTable.java | 124 ++++++- .../poi/xslf/usermodel/XSLFTableCell.java | 331 +++++++++++++++--- .../poi/xslf/usermodel/XSLFTableRow.java | 7 +- .../poi/xslf/usermodel/XSLFTableStyle.java | 44 ++- .../poi/xslf/usermodel/XSLFTableStyles.java | 9 +- .../poi/xslf/usermodel/XSLFTextParagraph.java | 50 ++- .../poi/xslf/usermodel/XSLFTextRun.java | 4 +- .../poi/xslf/usermodel/XSLFTextShape.java | 69 ++-- .../xslf/usermodel/TestXSLFTableStyles.java | 19 +- .../apache/poi/hslf/usermodel/HSLFTable.java | 83 +++-- .../poi/hslf/usermodel/HSLFTableCell.java | 81 ++++- 21 files changed, 893 insertions(+), 264 deletions(-) diff --git a/src/java/org/apache/poi/sl/draw/DrawFactory.java b/src/java/org/apache/poi/sl/draw/DrawFactory.java index d0584dadb..f06ccdc1b 100644 --- a/src/java/org/apache/poi/sl/draw/DrawFactory.java +++ b/src/java/org/apache/poi/sl/draw/DrawFactory.java @@ -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; diff --git a/src/java/org/apache/poi/sl/draw/DrawShape.java b/src/java/org/apache/poi/sl/draw/DrawShape.java index 0c465bc52..f4f53b52d 100644 --- a/src/java/org/apache/poi/sl/draw/DrawShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawShape.java @@ -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 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 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 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 { } } } - } diff --git a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java index 1ffa3cb94..efdd7f4da 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextParagraph.java @@ -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; diff --git a/src/java/org/apache/poi/sl/draw/DrawTextShape.java b/src/java/org/apache/poi/sl/draw/DrawTextShape.java index 24944c83b..b1179349d 100644 --- a/src/java/org/apache/poi/sl/draw/DrawTextShape.java +++ b/src/java/org/apache/poi/sl/draw/DrawTextShape.java @@ -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 diff --git a/src/java/org/apache/poi/sl/usermodel/TableCell.java b/src/java/org/apache/poi/sl/usermodel/TableCell.java index 0b8d7dc54..9516196de 100644 --- a/src/java/org/apache/poi/sl/usermodel/TableCell.java +++ b/src/java/org/apache/poi/sl/usermodel/TableCell.java @@ -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(); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java index e9a889d76..43034a2d1 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFColor.java @@ -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()); } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java index 5db266e42..6dfe1d0c9 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFShape.java @@ -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 { @@ -150,6 +148,7 @@ public abstract class XSLFShape implements Shape { } protected PaintStyle getFillPaint() { + final XSLFTheme theme = getSheet().getTheme(); PropertyFetcher fetcher = new PropertyFetcher() { public boolean fetch(XSLFShape shape) { XmlObject pr = null; @@ -178,7 +177,7 @@ public abstract class XSLFShape implements Shape { 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 { if (fillRef == null) { fillRef = getBgRef(); } - paint = selectPaint(fillRef); + paint = selectPaint(fillRef, theme); return paint; } @@ -365,9 +364,11 @@ public abstract class XSLFShape implements Shape { 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 { * * @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 { 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 { }; } - 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 { 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 { }; } - 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 { // 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 { } 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 diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java index 1f3393d1f..b90fbb067 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFSimpleShape.java @@ -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 fetcher = new PropertyFetcher() { 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); diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java index 21472d709..023c8f7b4 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTable.java @@ -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, TableShape { - 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 _rows; @@ -77,7 +79,11 @@ public class XSLFTable extends XSLFGraphicFrame implements Iterable(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 { 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 { private CTTableRow _row; @@ -44,7 +42,7 @@ public class XSLFTableRow implements Iterable { CTTableCell[] tcArray = _row.getTcArray(); _cells = new ArrayList(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 { 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; } diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyle.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyle.java index 31840314b..b3bcdfabd 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyle.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyle.java @@ -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(); + } + } } \ No newline at end of file diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java index 76455d64d..410f84101 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTableStyles.java @@ -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{ @@ -47,7 +47,10 @@ public class XSLFTableStyles extends POIXMLDocumentPart implements Iterable(tblStyleArray.length); for(CTTableStyle c : tblStyleArray){ diff --git a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java index 9576eea3c..b2a8b3314 100644 --- a/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java +++ b/src/ooxml/java/org/apache/poi/xslf/usermodel/XSLFTextParagraph.java @@ -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 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 fetcher = new TextBodyPropertyFetcher(){ @@ -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); } - - } \ No newline at end of file diff --git a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTableStyles.java b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTableStyles.java index 49b5fd494..12369937e 100644 --- a/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTableStyles.java +++ b/src/ooxml/testcases/org/apache/poi/xslf/usermodel/TestXSLFTableStyles.java @@ -19,27 +19,20 @@ package org.apache.poi.xslf.usermodel; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; -import org.junit.Test; -import org.openxmlformats.schemas.drawingml.x2006.main.CTTableStyle; +import java.io.IOException; + +import org.junit.Test; -/** - * @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(); } } \ No newline at end of file diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java index a1944c6ae..f007dd987 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTable.java @@ -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 { updateRowHeightsProperty(); } - private static class TableCellComparator implements Comparator, 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 htc = new ArrayList(); for (HSLFShape h : getShapes()) { @@ -198,28 +180,52 @@ implements HSLFShapeContainer, TableShape { if (htc.isEmpty()) { throw new IllegalStateException("HSLFTable without HSLFTableCells"); } - - Collections.sort(htc, new TableCellComparator()); - - List lst = new ArrayList(); - List row = new ArrayList(); - - double y0 = htc.get(0).getAnchor().getY(); + + SortedSet colSet = new TreeSet(); + SortedSet rowSet = new TreeSet(); + + // #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 colLst = new ArrayList(colSet); + List rowLst = new ArrayList(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 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 { // 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(); /** diff --git a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java index aee899483..a8e3294d1 100644 --- a/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java +++ b/src/scratchpad/src/org/apache/poi/hslf/usermodel/HSLFTableCell.java @@ -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 { protected static final int DEFAULT_WIDTH = 100; @@ -45,6 +43,16 @@ public final class HSLFTableCell extends HSLFTextBox implements TableCell