Объединить ячейки таблицы
Иногда для определенных строк в таблице требуется заголовок или большие блоки текста, занимающие всю ширину таблицы. Для правильного оформления таблицы пользователь может объединить несколько ячеек таблицы в одну. Aspose.Words поддерживает объединенные ячейки при работе со всеми входными форматами, включая импорт содержимого HTML.
Как объединить ячейки таблицы
В Aspose.Words объединенные ячейки представлены следующими свойствами класса CellFormat:
- HorizontalMerge, который описывает, является ли ячейка частью горизонтального слияния ячеек
- VerticalMerge, который описывает, является ли ячейка частью вертикального объединения ячеек
Значения этих свойств определяют поведение ячеек при слиянии:
- Первая ячейка в последовательности объединенных ячеек будет иметь CellMerge.First
- Все последующие объединенные ячейки будут иметь CellMerge.Previous
- Ячейка, которая не была объединена, будет иметь CellMerge.None
Проверка того, объединена ли ячейка
Чтобы проверить, является ли ячейка частью последовательности объединенных ячеек, мы просто проверяем свойства HorizontalMerge и VerticalMerge.
В следующем примере кода показано, как напечатать тип слияния горизонтальных и вертикальных ячеек:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(getMyDir() + "Table with merged cells.docx"); | |
Table table = (Table) doc.getChild(NodeType.TABLE, 0, true); | |
for (Row row : (Iterable<Row>) table.getRows()) | |
{ | |
for (Cell cell : (Iterable<Cell>) row.getCells()) | |
{ | |
System.out.println(printCellMergeType(cell)); | |
} | |
} |
Объединяйте ячейки таблицы При использовании DocumentBuilder
Чтобы объединить ячейки в таблице, созданной с помощью DocumentBuilder, вам необходимо задать соответствующий тип слияния для каждой ячейки, в которой ожидается слияние – сначала CellMerge.First, а затем CellMerge.Previous.
Кроме того, вы должны помнить, что для тех ячеек, где слияние не требуется, необходимо отменить настройку слияния – это можно сделать, установив для первой ячейки, не подлежащей слиянию, значение CellMerge.None. Если этого не сделать, все ячейки в таблице будут объединены.
В следующем примере кода показано, как создать таблицу с двумя строками, в которой ячейки в первой строке объединяются по горизонтали:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
builder.insertCell(); | |
builder.getCellFormat().setHorizontalMerge(CellMerge.FIRST); | |
builder.write("Text in merged cells."); | |
builder.insertCell(); | |
// This cell is merged to the previous and should be empty. | |
builder.getCellFormat().setHorizontalMerge(CellMerge.PREVIOUS); | |
builder.endRow(); | |
builder.insertCell(); | |
builder.getCellFormat().setHorizontalMerge(CellMerge.NONE); | |
builder.write("Text in one cell."); | |
builder.insertCell(); | |
builder.write("Text in another cell."); | |
builder.endRow(); | |
builder.endTable(); | |
doc.save(getArtifactsDir() + "WorkingWithTables.HorizontalMerge.docx"); |
В следующем примере кода показано, как создать таблицу с двумя столбцами, в которой ячейки в первом столбце объединяются по вертикали:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(); | |
DocumentBuilder builder = new DocumentBuilder(doc); | |
builder.insertCell(); | |
builder.getCellFormat().setVerticalMerge(CellMerge.FIRST); | |
builder.write("Text in merged cells."); | |
builder.insertCell(); | |
builder.getCellFormat().setVerticalMerge(CellMerge.NONE); | |
builder.write("Text in one cell"); | |
builder.endRow(); | |
builder.insertCell(); | |
// This cell is vertically merged to the cell above and should be empty. | |
builder.getCellFormat().setVerticalMerge(CellMerge.PREVIOUS); | |
builder.insertCell(); | |
builder.getCellFormat().setVerticalMerge(CellMerge.NONE); | |
builder.write("Text in another cell"); | |
builder.endRow(); | |
builder.endTable(); | |
doc.save(getArtifactsDir() + "WorkingWithTables.VerticalMerge.docx"); |
Объединяйте ячейки таблицы в других случаях
В других ситуациях, когда DocumentBuilder не используется, например, в существующей таблице, объединение ячеек предыдущим способом может оказаться не таким простым. Вместо этого мы можем обернуть основные операции, связанные с применением свойств объединения к ячейкам, в метод, который значительно упрощает задачу. Этот метод аналогичен методу автоматизации слияния, который вызывается для объединения диапазона ячеек в таблице.
Приведенный ниже код объединит ячейки таблицы в указанном диапазоне, начиная с указанной ячейки и заканчивая конечной ячейкой. В этом случае диапазон может охватывать несколько строк или столбцов:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
void mergeCells(Cell startCell, Cell endCell) | |
{ | |
Table parentTable = startCell.getParentRow().getParentTable(); | |
// Find the row and cell indices for the start and end cell. | |
Point startCellPos = new Point(startCell.getParentRow().indexOf(startCell), | |
parentTable.indexOf(startCell.getParentRow())); | |
Point endCellPos = new Point(endCell.getParentRow().indexOf(endCell), parentTable.indexOf(endCell.getParentRow())); | |
// Create a range of cells to be merged based on these indices. | |
// Inverse each index if the end cell is before the start cell. | |
Rectangle mergeRange = new Rectangle(Math.min(startCellPos.x, endCellPos.y), | |
Math.min(startCellPos.y, endCellPos.y), | |
Math.abs(endCellPos.x - startCellPos.x) + 1, Math.abs(endCellPos.y - startCellPos.y) + 1); | |
for (Row row : parentTable.getRows()) | |
{ | |
for (Cell cell : row.getCells()) | |
{ | |
Point currentPos = new Point(row.indexOf(cell), parentTable.indexOf(row)); | |
// Check if the current cell is inside our merge range, then merge it. | |
if (mergeRange.contains(currentPos)) | |
{ | |
cell.getCellFormat().setHorizontalMerge(currentPos.x == mergeRange.getX() ? CellMerge.FIRST : CellMerge.PREVIOUS); | |
cell.getCellFormat().setVerticalMerge(currentPos.y == mergeRange.getY() ? CellMerge.FIRST : CellMerge.PREVIOUS); | |
} | |
} | |
} | |
} |
В следующем примере кода показано, как объединить диапазон ячеек между двумя указанными ячейками:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(getMyDir() + "Table with merged cells.docx"); | |
Table table = doc.getFirstSection().getBody().getTables().get(0); | |
// We want to merge the range of cells found inbetween these two cells. | |
Cell cellStartRange = table.getRows().get(0).getCells().get(0); | |
Cell cellEndRange = table.getRows().get(1).getCells().get(1); | |
// Merge all the cells between the two specified cells into one. | |
mergeCells(cellStartRange, cellEndRange); | |
doc.save(getArtifactsDir() + "WorkingWithTables.MergeCellRange.docx"); |
Объединенные по вертикали и горизонтали ячейки в таблице HTML
Как мы уже говорили в предыдущих статьях, таблица в Microsoft Word представляет собой набор независимых строк. Каждая строка содержит набор ячеек, которые не зависят от ячеек других строк. Таким образом, в таблице Microsoft Word нет такого объекта, как “столбец”, а “1-й столбец” - это что-то вроде “набора 1-х ячеек каждой строки таблицы”. Это позволяет пользователям иметь таблицу, в которой, например, 1–я строка состоит из двух ячеек – шириной 2 см и 1 см1, а 2-я строка состоит из двух разных ячеек - шириной 1 см и 2 см. И Aspose.Words поддерживают эту концепцию таблиц.
Таблица в HTML имеет принципиально иную структуру: каждая строка имеет одинаковое количество ячеек и (это важно для задачи) каждая ячейка имеет ширину соответствующего столбца, одинаковую для всех ячеек в одном столбце. Поэтому, если HorizontalMerge и VerticalMerge возвращают неверное значение, используйте следующий пример кода:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(getMyDir() + "Table with merged cells.docx"); | |
SpanVisitor visitor = new SpanVisitor(doc); | |
doc.accept(visitor); |
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
/// <summary> | |
/// Helper class that contains collection of rowinfo for each row. | |
/// </summary> | |
public static class TableInfo | |
{ | |
public ArrayList<RowInfo> getRows() { return mRows; } | |
private ArrayList<RowInfo> mRows = new ArrayList<>(); | |
} | |
/// <summary> | |
/// Helper class that contains collection of cellinfo for each cell. | |
/// </summary> | |
public static class RowInfo | |
{ | |
public ArrayList<CellInfo> getCells() { return mCells; }; | |
private ArrayList<CellInfo> mCells = new ArrayList<>(); | |
} | |
/// <summary> | |
/// Helper class that contains info about cell. currently here is only colspan and rowspan. | |
/// </summary> | |
public static class CellInfo | |
{ | |
public CellInfo(int colSpan, int rowSpan) | |
{ | |
mColSpan = colSpan; | |
mRowSpan = rowSpan; | |
} | |
public int getColSpan() { return mColSpan; }; | |
private int mColSpan; | |
public int getRowSpan() { return mRowSpan; }; | |
private int mRowSpan; | |
} | |
public static class SpanVisitor extends DocumentVisitor | |
{ | |
/// <summary> | |
/// Creates new SpanVisitor instance. | |
/// </summary> | |
/// <param name="doc"> | |
/// Is document which we should parse. | |
/// </param> | |
public SpanVisitor(Document doc) throws Exception { | |
mWordTables = doc.getChildNodes(NodeType.TABLE, true); | |
// We will parse HTML to determine the rowspan and colspan of each cell. | |
ByteArrayOutputStream htmlStream = new ByteArrayOutputStream(); | |
HtmlSaveOptions options = new HtmlSaveOptions(); | |
{ | |
options.setImagesFolder(System.getProperty("java.io.tmpdir")); | |
} | |
doc.save(htmlStream, options); | |
// Load HTML into the XML document. | |
org.jsoup.nodes.Document document = Jsoup.parse(htmlStream.toString()); | |
// Get collection of tables in the HTML document. | |
Elements tables = document.getElementsByTag("table"); | |
for (Element table : tables) { | |
TableInfo tableInf = new TableInfo(); | |
// Get collection of rows in the table. | |
Elements rows = table.getElementsByTag("tr"); | |
for (Element row : rows) { | |
RowInfo rowInf = new RowInfo(); | |
// Get collection of cells. | |
Elements cells = row.getElementsByTag("td"); | |
for (Element cell : cells) { | |
// Determine row span and colspan of the current cell. | |
String colSpanAttr = cell.attributes().get("colspan"); | |
String rowSpanAttr = cell.attributes().get("rowspan"); | |
int colSpan = StringUtils.isNotBlank(colSpanAttr) ? Integer.parseInt(colSpanAttr) : 0; | |
int rowSpan = StringUtils.isNotBlank(rowSpanAttr) ? Integer.parseInt(rowSpanAttr) : 0; | |
CellInfo cellInf = new CellInfo(colSpan, rowSpan); | |
rowInf.getCells().add(cellInf); | |
} | |
tableInf.getRows().add(rowInf); | |
} | |
mTables.add(tableInf); | |
} | |
} | |
public int visitCellStart(Cell cell) | |
{ | |
int tabIdx = mWordTables.indexOf(cell.getParentRow().getParentTable()); | |
int rowIdx = cell.getParentRow().getParentTable().indexOf(cell.getParentRow()); | |
int cellIdx = cell.getParentRow().indexOf(cell); | |
int colSpan = 0; | |
int rowSpan = 0; | |
if (tabIdx < mTables.size() && | |
rowIdx < mTables.get(tabIdx).getRows().size() && | |
cellIdx < mTables.get(tabIdx).getRows().get(rowIdx).getCells().size()) | |
{ | |
colSpan = mTables.get(tabIdx).getRows().get(rowIdx).getCells().get(cellIdx).getColSpan(); | |
rowSpan = mTables.get(tabIdx).getRows().get(rowIdx).getCells().get(cellIdx).getRowSpan(); | |
} | |
System.out.println(MessageFormat.format("{0}.{1}.{2} colspan={3}\t rowspan={4}", tabIdx, rowIdx, cellIdx, colSpan, rowSpan)); | |
return VisitorAction.CONTINUE; | |
} | |
private ArrayList<TableInfo> mTables = new ArrayList<>(); | |
private NodeCollection mWordTables; | |
} |
Преобразование в горизонтально объединенные ячейки
Иногда невозможно определить, какие ячейки объединены, потому что в некоторых более новых версиях Microsoft Word больше не используются флаги объединения при горизонтальном объединении ячеек. Но для ситуаций, когда ячейки объединяются в ячейку по горизонтали по ширине с помощью флагов объединения, Aspose.Words предоставляет метод преобразования ячеек ConvertToHorizontallyMergedCells
. Этот метод просто преобразует таблицу и добавляет новые ячейки по мере необходимости.
Следующий пример кода показывает описанный выше метод в действии:
// For complete examples and data files, please go to https://github.com/aspose-words/Aspose.Words-for-Java.git. | |
Document doc = new Document(getMyDir() + "Table with merged cells.docx"); | |
Table table = doc.getFirstSection().getBody().getTables().get(0); | |
// Now merged cells have appropriate merge flags. | |
table.convertToHorizontallyMergedCells(); |