Working with Columns and Rows
Working with Columns
In both Word documents and in the Aspose.Words Document Object Model, there is no concept of a column. By design, table rows in Microsoft Word are completely independent and the base properties and operations are only contained on rows and cells of the table. This gives tables the possibility of some interesting attributes:
- Each row in a table can have a completely different number of cells.
- Vertically, the cells of each row can have different widths.
- It is possible to join tables with differing row formats and cell counts.
Any operations that are performed on columns in Microsoft Word are in actual fact “short-cut methods” which perform the operation by modifying the cells of the rows collectively in such a way that it appears they are being applied to columns. This structure of rows and cells is represented in the same way that tables are represented in Aspose.Words. In the Aspose.Words Document Object Model a Table node is made up of Row and then Cell nodes. There is also no native support for columns.
You can still achieve such operations on columns by iterating through the same cell index of the rows of a table. The code below makes such operations easier by proving a façade class which collects the cells which make up a “column” of a table.
The following code example demonstrates a facade object for working with a column of a table.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
/** | |
* Represents a facade object for a column of a table in a Microsoft Word | |
* document. | |
*/ | |
public class Column { | |
private int mColumnIndex; | |
private Table mTable; | |
private Column(Table table, int columnIndex) { | |
if (table == null) | |
throw new IllegalArgumentException("table"); | |
mTable = table; | |
mColumnIndex = columnIndex; | |
} | |
/** | |
* Returns a new column facade from the table and supplied zero-based index. | |
*/ | |
public static Column fromIndex(Table table, int columnIndex) { | |
return new Column(table, columnIndex); | |
} | |
/** | |
* Returns the cells which make up the column. | |
*/ | |
public Cell[] getCells() { | |
ArrayList<Cell> columnCells = getColumnCells(); | |
return columnCells.toArray(new Cell[columnCells.size()]); | |
} | |
/** | |
* Returns the index of the given cell in the column. | |
*/ | |
public int indexOf(Cell cell) { | |
return getColumnCells().indexOf(cell); | |
} | |
/** | |
* Inserts a brand new column before this column into the table. | |
* | |
* @throws Exception | |
*/ | |
public Column insertColumnBefore() throws Exception { | |
Cell[] columnCells = getCells(); | |
if (columnCells.length == 0) | |
throw new IllegalArgumentException("Column must not be empty"); | |
// Create a clone of this column. | |
for (Cell cell : columnCells) | |
cell.getParentRow().insertBefore(cell.deepClone(false), cell); | |
// This is the new column. | |
Column column = new Column(columnCells[0].getParentRow().getParentTable(), mColumnIndex); | |
// We want to make sure that the cells are all valid to work with (have at least one paragraph). | |
for (Cell cell : column.getCells()) | |
cell.ensureMinimum(); | |
// Increase the index which this column represents since there is now one extra column infront. | |
mColumnIndex++; | |
return column; | |
} | |
/** | |
* Removes the column from the table. | |
*/ | |
public void remove() { | |
for (Cell cell : getCells()) | |
cell.remove(); | |
} | |
/** | |
* Returns the text of the column. | |
*/ | |
public String toTxt() throws Exception { | |
StringBuilder builder = new StringBuilder(); | |
for (Cell cell : getCells()) | |
builder.append(cell.toString(SaveFormat.TEXT)); | |
return builder.toString(); | |
} | |
/** | |
* Provides an up-to-date collection of cells which make up the column | |
* represented by this facade. | |
*/ | |
private ArrayList<Cell> getColumnCells() { | |
ArrayList<Cell> columnCells = new ArrayList<Cell>(); | |
for (Row row : mTable.getRows()) { | |
Cell cell = row.getCells().get(mColumnIndex); | |
if (cell != null) | |
columnCells.add(cell); | |
} | |
return columnCells; | |
} | |
} |
The following code example shows how to insert a blank column into a table.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
public static void insertABlankColumnIntoATable(Document doc, Table table) throws Exception { | |
// Get the second column in the table. | |
Column column = Column.fromIndex(table, 1); | |
// Create a new column to the left of this column. | |
// This is the same as using the "Insert Column Before" command in Microsoft Word. | |
Column newColumn = column.insertColumnBefore(); | |
// Add some text to each of the column cells. | |
for (Cell cell : newColumn.getCells()) { | |
cell.getFirstParagraph().appendChild(new Run(doc, "Column Text " + newColumn.indexOf(cell))); | |
} | |
} |
The following code example shows how to get the plain text of a table column.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
public static void getTextOfATableColumn(Table table) throws Exception { | |
// Get the first column in the table. | |
Column column = Column.fromIndex(table, 0); | |
// Print the plain text of the column to the screen. | |
System.out.println(column.toTxt()); | |
} |
The following code example shows how to remove a column from a table in a document.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
public static void removeAColumnFromATable() throws Exception { | |
Document doc = new Document(dataDir + "Table.Document.doc"); | |
Table table = (Table) doc.getChild(NodeType.TABLE, 1, true); | |
// Get the third column from the table and remove it. | |
Column column = Column.fromIndex(table, 2); | |
column.remove(); | |
doc.save(dataDir + "Table.RemoveColumn Out.doc"); | |
} |
Specify Rows to Repeat on Subsequent Pages as Header Rows
A table can specify certain starting rows of a table to be used as header rows. This means if the table spans over many pages, these rows will be repeated at the top of the table for each page. In Microsoft Word, this option is found under Table Properties as “Repeat row as a header on subsequent pages” . Using this option you can choose to repeat only a single row or many rows in a table. In the case of a single header row, it must be the first row in the table. In addition when multiple header rows are used then the header row each of these rows must be consecutive and these rows must be on one page.
In Aspose.Words you can apply this setting by using the RowFormat.getHeadingFormat() property. Note that heading rows do not work in nested tables. That is, if you have a table within another table then this setting will have no effect. This is a limitation of Microsoft Word which does not allow this and not of Aspose.Words.
The following code example shows how to build a table which includes heading rows that repeat on subsequent pages.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
Table table = builder.startTable(); | |
builder.getRowFormat().setHeadingFormat(true); | |
builder.getParagraphFormat().setAlignment(ParagraphAlignment.CENTER); | |
builder.getCellFormat().setWidth(100); | |
builder.insertCell(); | |
builder.writeln("Heading row 1"); | |
builder.endRow(); | |
builder.insertCell(); | |
builder.writeln("Heading row 2"); | |
builder.endRow(); | |
builder.getCellFormat().setWidth(50); | |
builder.getParagraphFormat().clearFormatting(); | |
// Insert some content so the table is long enough to continue onto the next page. | |
for (int i = 0; i < 50; i++) { | |
builder.insertCell(); | |
builder.getRowFormat().setHeadingFormat(false); | |
builder.write("Column 1 Text"); | |
builder.insertCell(); | |
builder.write("Column 2 Text"); | |
builder.endRow(); | |
} | |
doc.save(dataDir + "Table.HeadingRow Out.doc"); |
Adjusting Width for Merged Cells
Using Aspose.Words, there is a possibility to lose the merged cells in the document while adjusting the column width when having some merged cells. So if you require to retain the merged cells at it is while adjusting column width using Aspose.Words API, the following code example demonstrates how to adjust cell width when some of the cells are merged. It is important to note that when cells are vertically merged, the display areas of the merged cells are combined. The combined area is used to display the contents of the first vertically merged cell and all other vertically merged cells must be empty.
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java | |
private static java.util.List<Cell> _startMergeCells = new java.util.ArrayList<Cell>(3); | |
private static final String dataDir = Utils.getSharedDataDir(WorkingWithColumns.class) + "Tables/"; | |
public static void main(String[] args) throws Exception { | |
// TODO Auto-generated method stub | |
Document doc = new Document(dataDir + "InputDoc.docx"); | |
Table table = (Table)doc.getChild(NodeType.TABLE, 0, true); | |
Row row = new Row(doc); | |
table.getRows().add(row); | |
for (int i = 0; i < 3; i++) | |
appendOneCellAndAddValueToRow(doc, row, i + "�FirstRow", CellMerge.FIRST); | |
Row row2 = new Row(doc); | |
table.getRows().add(row2); | |
for (int i = 0; i < 3; i++) | |
appendOneCellAndAddValueToRow(doc, row2, i + "�Row", CellMerge.PREVIOUS); | |
doc.save(dataDir + "out.docx"); | |
} | |
public static void appendOneCellAndAddValueToRow(Document doc, Row row, String value,int cellMerge) { | |
Cell cell = new Cell(doc); | |
cell.ensureMinimum(); | |
cell.getFirstParagraph().getRuns().clear(); | |
cell.getCellFormat().setVerticalMerge(cellMerge); | |
row.appendChild(cell); | |
if (cellMerge == CellMerge.FIRST) | |
_startMergeCells.add(cell); | |
InsertContent(cell, value); | |
} | |
private static void InsertContent(Cell cell, String value) | |
{ | |
Cell effectiveCell = cell; | |
DocumentBase doc = cell.getDocument(); | |
Paragraph para = cell.getFirstParagraph(); | |
if (cell.getCellFormat().getVerticalMerge() == CellMerge.PREVIOUS) | |
{ | |
para = new Paragraph(doc); | |
int cellIndex = GetCellIndex(cell); | |
// The consolidated area is used to display the contents of the first vertically merged cell. | |
// So, move content to the first cell of merged range. | |
effectiveCell = _startMergeCells.get(cellIndex); | |
} | |
para.getRuns().add(new Run(doc, value)); | |
if (para.getParentNode() == null) | |
effectiveCell.appendChild(para); | |
} | |
private static int GetCellIndex(Cell cell) | |
{ | |
Node nextCell = cell.getParentRow().getFirstCell(); | |
int index = -1; | |
while (null != nextCell) | |
{ | |
if (nextCell.getNodeType() == NodeType.CELL) | |
++index; | |
if (cell == nextCell) | |
break; | |
nextCell = nextCell.getNextSibling(); | |
} | |
return index; | |
} |