Połączenia komórek tabeli

Czasami pewne wiersze w tabeli wymagają nagłówka lub dużych bloków tekstu, które zajmują pełną szerokość tabeli. Dla prawidłowego projektowania tabeli, użytkownik może połączyć kilka komórek tabeli w jeden. Aspose.Words obsługuje połączone komórki podczas pracy ze wszystkimi formatami wejściowymi, w tym importując zawartość HTML.

Jak połączyć komórki tabeli

W Aspose.Words, Komórki połączone są reprezentowane przez następujące właściwości CellFormat klasa:

  • HorizontalMerge która opisuje, czy komórka jest częścią poziomego połączenia komórek
  • VerticalMerge która opisuje, czy komórka jest częścią pionowego połączenia komórek

Wartości tych właściwości decydują o łączeniu zachowania komórek:

work-with-merged-cells-aspose-words-java

Sprawdzanie, czy połączona jest komórka

Aby sprawdzić, czy komórka jest częścią sekwencji połączonych komórek, po prostu sprawdzić HorizontalMerge oraz VerticalMerge nieruchomości.

Poniższy przykład kodu pokazuje jak wydrukować typ połączenia poziomego i pionowego:

// 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));
}
}

Łączenie komórek tabeli podczas korzystania z DocumentBuilder

Aby połączyć komórki w tabeli utworzonej z DocumentBuilder, musisz ustawić odpowiedni typ połączenia dla każdej komórki, w której oczekuje się połączenia - najpierw CellMerge.First i wtedy CellMerge.Previous.

Należy również pamiętać, aby wyczyścić ustawienie połączenia dla tych komórek, w których nie jest wymagane połączenie - można to zrobić poprzez ustawienie pierwszej komórki nie-połączenia do CellMerge.None. Jeśli nie zostanie to zrobione, wszystkie komórki w tabeli zostaną połączone.

Poniższy przykład kodu pokazuje, jak stworzyć tabelę z dwoma wierszami, gdzie komórki w pierwszym wierszu są łączone poziomo:

// 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");

Poniższy przykład kodu pokazuje jak stworzyć tabelę dwukolumnową, gdzie komórki w pierwszej kolumnie są połączone pionowo:

// 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");

Łączenie komórek tabeli w innych przypadkach

W innych sytuacjach DocumentBuilder nie jest stosowany, jak w istniejącej tabeli, łączenie komórek w poprzedni sposób może nie być tak łatwe. Zamiast tego, możemy zawinąć podstawowe operacje związane z zastosowaniem właściwości łączenia do komórek w sposób, który znacznie ułatwia zadanie. Metoda ta jest podobna do metody automatyzacji połączeń, która jest wywoływana do łączenia szeregu komórek w tabeli.

Poniższy kod połączy komórki tabeli w określonym zakresie, zaczynając od danej komórki i kończąc na końcowej komórce. W tym przypadku zakres może obejmować wiele wierszy lub kolumn:

// 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);
}
}
}
}

Poniższy przykład kodu pokazuje jak połączyć szereg komórek pomiędzy dwoma określonymi komórkami:

// 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");

Komórki pionowe i poziome połączone w tabeli HTML

Jak już mówiliśmy w poprzednich artykułach, tabela w Microsoft Word jest zbiorem niezależnych wierszy. Każdy wiersz posiada zbiór komórek niezależnych od komórek innych wierszy. Tak więc, w Microsoft Word tabeli nie ma takiego obiektu jak “kolumna”, a “pierwsza kolumna” jest czymś w rodzaju “zbiór 1 komórek każdego wiersza w tabeli”. Pozwala to użytkownikom na posiadanie tabeli, w której na przykład pierwszy rząd składa się z dwóch komórek - 2cm i 1cm, a drugi rząd składa się z dwóch różnych komórek - 1cm i 2cm szerokości. I Aspose.Words wspiera tę koncepcję tabel.

Tabela w HTML ma zasadniczo inną strukturę: każdy wiersz ma taką samą liczbę komórek i (ważne jest dla zadania) każda komórka ma szerokość odpowiedniej kolumny, taką samą dla wszystkich komórek w jednej kolumnie. Więc jeśli HorizontalMerge oraz VerticalMerge zwraca nieprawidłową wartość, użyj następującego przykładu kodu:

// 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;
}

Przelicz do poziomu Komórki połączone

Czasami nie jest możliwe wykrycie, które komórki są połączone, ponieważ niektóre nowsze wersje Microsoft Word nie używać flag połączenia, gdy komórki są łączone poziomo. Ale w sytuacjach, gdy komórki są łączone poziomo przez ich szerokość za pomocą flag łączących, Aspose.Words zapewnia ConvertToHorizontallyMergedCells metoda konwersji komórek. Metoda ta po prostu zmienia tabelę i dodaje nowe komórki w razie potrzeby.

Poniższy przykład kodu pokazuje powyższą metodę w działaniu:

// 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();