Java tutorial
/* * ==================================================================== * 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.geom.Rectangle2D; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import javax.xml.namespace.QName; 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; import org.apache.xmlbeans.XmlCursor; import org.apache.xmlbeans.XmlObject; import org.apache.xmlbeans.impl.values.XmlAnyTypeImpl; import org.openxmlformats.schemas.drawingml.x2006.main.CTGraphicalObjectData; import org.openxmlformats.schemas.drawingml.x2006.main.CTNonVisualDrawingProps; import org.openxmlformats.schemas.drawingml.x2006.main.CTTable; import org.openxmlformats.schemas.drawingml.x2006.main.CTTableRow; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrame; import org.openxmlformats.schemas.presentationml.x2006.main.CTGraphicalObjectFrameNonVisual; /** * Represents a table in a .pptx presentation */ public class XSLFTable extends XSLFGraphicFrame implements Iterable<XSLFTableRow>, TableShape<XSLFShape, XSLFTextParagraph> { /* package */ static final String TABLE_URI = "http://schemas.openxmlformats.org/drawingml/2006/table"; private CTTable _table; private List<XSLFTableRow> _rows; /*package*/ XSLFTable(CTGraphicalObjectFrame shape, XSLFSheet sheet) { super(shape, sheet); CTGraphicalObjectData god = shape.getGraphic().getGraphicData(); XmlCursor xc = god.newCursor(); try { if (!xc.toChild(XSLFRelation.NS_DRAWINGML, "tbl")) { throw new IllegalStateException("a:tbl element was not found in\n " + god); } XmlObject xo = xc.getObject(); // Pesky XmlBeans bug - see Bugzilla #49934 // it never happens when using the full ooxml-schemas jar but may happen with the abridged poi-ooxml-schemas if (xo instanceof XmlAnyTypeImpl) { String errStr = "Schemas (*.xsb) for CTTable can't be loaded - usually this happens when OSGI " + "loading is used and the thread context classloader has no reference to " + "the xmlbeans classes"; throw new IllegalStateException(errStr); } _table = (CTTable) xo; } finally { xc.dispose(); } _rows = new ArrayList<>(_table.sizeOfTrArray()); for (CTTableRow row : _table.getTrList()) { _rows.add(new XSLFTableRow(row, this)); } updateRowColIndexes(); } @Override public XSLFTableCell getCell(int row, int col) { List<XSLFTableRow> rows = getRows(); if (row < 0 || rows.size() <= row) { return null; } XSLFTableRow r = rows.get(row); if (r == null) { // empty row return null; } List<XSLFTableCell> cells = r.getCells(); if (col < 0 || cells.size() <= col) { return null; } // cell can be potentially empty ... return cells.get(col); } @Internal public CTTable getCTTable() { return _table; } @Override public int getNumberOfColumns() { return _table.getTblGrid().sizeOfGridColArray(); } @Override public int getNumberOfRows() { return _table.sizeOfTrArray(); } @Override public double getColumnWidth(int idx) { return Units.toPoints(_table.getTblGrid().getGridColArray(idx).getW()); } @Override public void setColumnWidth(int idx, double width) { _table.getTblGrid().getGridColArray(idx).setW(Units.toEMU(width)); } @Override public double getRowHeight(int row) { return Units.toPoints(_table.getTrArray(row).getH()); } @Override public void setRowHeight(int row, double height) { _table.getTrArray(row).setH(Units.toEMU(height)); } public Iterator<XSLFTableRow> iterator() { return _rows.iterator(); } public List<XSLFTableRow> getRows() { return Collections.unmodifiableList(_rows); } public XSLFTableRow addRow() { CTTableRow tr = _table.addNewTr(); XSLFTableRow row = new XSLFTableRow(tr, this); // default height is 20 points row.setHeight(20.0); _rows.add(row); updateRowColIndexes(); return row; } /** * Remove the row on the given index * @param rowIdx the row index */ public void removeRow(int rowIdx) { _table.removeTr(rowIdx); _rows.remove(rowIdx); updateRowColIndexes(); } static CTGraphicalObjectFrame prototype(int shapeId) { CTGraphicalObjectFrame frame = CTGraphicalObjectFrame.Factory.newInstance(); CTGraphicalObjectFrameNonVisual nvGr = frame.addNewNvGraphicFramePr(); CTNonVisualDrawingProps cnv = nvGr.addNewCNvPr(); cnv.setName("Table " + shapeId); cnv.setId(shapeId); nvGr.addNewCNvGraphicFramePr().addNewGraphicFrameLocks().setNoGrp(true); nvGr.addNewNvPr(); frame.addNewXfrm(); CTGraphicalObjectData gr = frame.addNewGraphic().addNewGraphicData(); XmlCursor grCur = gr.newCursor(); grCur.toNextToken(); grCur.beginElement(new QName(XSLFRelation.NS_DRAWINGML, "tbl")); CTTable tbl = CTTable.Factory.newInstance(); tbl.addNewTblPr(); tbl.addNewTblGrid(); XmlCursor tblCur = tbl.newCursor(); tblCur.moveXmlContents(grCur); tblCur.dispose(); grCur.dispose(); gr.setUri(TABLE_URI); return frame; } /** * Merge cells of a table */ @SuppressWarnings("unused") public void mergeCells(int firstRow, int lastRow, int firstCol, int lastCol) { if (firstRow > lastRow) { throw new IllegalArgumentException( "Cannot merge, first row > last row : " + firstRow + " > " + lastRow); } if (firstCol > lastCol) { throw new IllegalArgumentException( "Cannot merge, first column > last column : " + firstCol + " > " + lastCol); } int rowSpan = (lastRow - firstRow) + 1; boolean mergeRowRequired = rowSpan > 1; int colSpan = (lastCol - firstCol) + 1; boolean mergeColumnRequired = colSpan > 1; for (int i = firstRow; i <= lastRow; i++) { XSLFTableRow row = _rows.get(i); for (int colPos = firstCol; colPos <= lastCol; colPos++) { XSLFTableCell cell = row.getCells().get(colPos); if (mergeRowRequired) { if (i == firstRow) { cell.setRowSpan(rowSpan); } else { cell.setVMerge(); } } if (mergeColumnRequired) { if (colPos == firstCol) { cell.setGridSpan(colSpan); } else { cell.setHMerge(); } } } } } /** * Get assigned TableStyle * * @return the assigned TableStyle * * @since POI 3.15-beta2 */ protected XSLFTableStyle getTableStyle() { CTTable tab = getCTTable(); // TODO: support inline table style if (!tab.isSetTblPr() || !tab.getTblPr().isSetTableStyleId()) { return null; } String styleId = tab.getTblPr().getTableStyleId(); XSLFTableStyles styles = getSheet().getSlideShow().getTableStyles(); for (XSLFTableStyle style : styles.getStyles()) { if (style.getStyleId().equals(styleId)) { return style; } } return null; } /* package */ void updateRowColIndexes() { int rowIdx = 0; for (XSLFTableRow xr : this) { int colIdx = 0; for (XSLFTableCell tc : xr) { tc.setRowColIndex(rowIdx, colIdx); colIdx++; } rowIdx++; } } /** * Calculates the bounding boxes of all cells and updates the dimension of the table */ public void updateCellAnchor() { int rows = getNumberOfRows(); int cols = getNumberOfColumns(); double[] colWidths = new double[cols]; double[] rowHeights = new double[rows]; for (int row = 0; row < rows; row++) { rowHeights[row] = getRowHeight(row); } for (int col = 0; col < cols; col++) { colWidths[col] = getColumnWidth(col); } Rectangle2D tblAnc = getAnchor(); DrawFactory df = DrawFactory.getInstance(null); double nextY = tblAnc.getY(); double nextX = tblAnc.getX(); // #1 pass - determine row heights, the height values might be too low or 0 ... for (int row = 0; row < rows; row++) { double maxHeight = 0; for (int col = 0; col < cols; col++) { XSLFTableCell tc = getCell(row, col); if (tc == null || tc.getGridSpan() != 1 || tc.getRowSpan() != 1) { continue; } // need to set the anchor before height calculation tc.setAnchor(new Rectangle2D.Double(0, 0, colWidths[col], 0)); DrawTextShape dts = df.getDrawable(tc); maxHeight = Math.max(maxHeight, dts.getTextHeight()); } rowHeights[row] = Math.max(rowHeights[row], maxHeight); } // #2 pass - init properties for (int row = 0; row < rows; row++) { nextX = tblAnc.getX(); for (int col = 0; col < cols; col++) { Rectangle2D bounds = new Rectangle2D.Double(nextX, nextY, colWidths[col], rowHeights[row]); XSLFTableCell tc = getCell(row, col); if (tc != null) { tc.setAnchor(bounds); nextX += colWidths[col] + DrawTableShape.borderSize; } } nextY += rowHeights[row] + DrawTableShape.borderSize; } // #3 pass - update merge info for (int row = 0; row < rows; row++) { for (int col = 0; col < cols; col++) { XSLFTableCell tc = getCell(row, col); if (tc == null) { continue; } Rectangle2D mergedBounds = tc.getAnchor(); for (int col2 = col + 1; col2 < col + tc.getGridSpan(); col2++) { assert (col2 < cols); XSLFTableCell tc2 = getCell(row, col2); assert (tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1); mergedBounds.add(tc2.getAnchor()); } for (int row2 = row + 1; row2 < row + tc.getRowSpan(); row2++) { assert (row2 < rows); XSLFTableCell tc2 = getCell(row2, col); assert (tc2.getGridSpan() == 1 && tc2.getRowSpan() == 1); mergedBounds.add(tc2.getAnchor()); } tc.setAnchor(mergedBounds); } } setAnchor( new Rectangle2D.Double(tblAnc.getX(), tblAnc.getY(), nextX - tblAnc.getX(), nextY - tblAnc.getY())); } }