Combinar Celdas de Tabla
A veces, ciertas filas de una tabla requieren un encabezado o grandes bloques de texto que ocupan todo el ancho de la tabla. Para un diseño adecuado de la tabla, el usuario puede fusionar varias celdas de la tabla en una sola. Aspose.Words admite celdas combinadas cuando se trabaja con todos los formatos de entrada, incluida la importación de contenido HTML.
Cómo Combinar Celdas de Tabla
En Aspose.Words, las celdas combinadas se representan mediante las siguientes propiedades de la clase CellFormat:
- HorizontalMerge que describe si la celda es parte de una combinación horizontal de celdas
- VerticalMerge que describe si la celda es parte de una fusión vertical de celdas
Los valores de estas propiedades determinan el comportamiento de fusión de las celdas:
- La primera celda de una secuencia de celdas combinadas tendrá CellMerge.First
- Cualquier celda combinada posteriormente tendrá CellMerge.Previous
- Una celda que no esté fusionada tendrá CellMerge.None
Comprobación de si una celda está fusionada
Para verificar si una celda es parte de una secuencia de celdas combinadas, simplemente verificamos las propiedades HorizontalMerge y VerticalMerge.
El siguiente ejemplo de código muestra cómo imprimir el tipo de combinación de celdas horizontal y vertical:
// 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)); | |
} | |
} |
Fusionar Celdas De La Tabla Cuando Se Usa DocumentBuilder
Para fusionar celdas en una tabla creada con DocumentBuilder, debe establecer el tipo de fusión apropiado para cada celda donde se espera la fusión: primero CellMerge.First y luego CellMerge.Previous.
Además, debe recordar borrar la configuración de combinación para aquellas celdas donde no se requiere combinación; esto se puede hacer configurando la primera celda sin combinación en CellMerge.None. Si no se hace esto, se fusionarán todas las celdas de la tabla.
El siguiente ejemplo de código muestra cómo crear una tabla con dos filas donde las celdas de la primera fila se fusionan horizontalmente:
// 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"); |
El siguiente ejemplo de código muestra cómo crear una tabla de dos columnas donde las celdas de la primera columna se fusionan verticalmente:
// 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"); |
Combinar Celdas de Tabla en Otros Casos
En otras situaciones en las que no se usa DocumentBuilder, como en una tabla existente, fusionar celdas de la manera anterior puede no ser tan fácil. En cambio, podemos ajustar las operaciones básicas involucradas en la aplicación de propiedades de combinación a las celdas en un método que facilita mucho la tarea. Este método es similar al método de automatización de combinación, que se llama para combinar un rango de celdas en una tabla.
El siguiente código fusionará las celdas de la tabla en el rango especificado, comenzando en la celda dada y terminando en la celda final. En este caso, el rango puede abarcar varias filas o columnas:
// 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); | |
} | |
} | |
} | |
} |
El siguiente ejemplo de código muestra cómo fusionar un rango de celdas entre dos celdas especificadas:
// 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"); |
Celdas Combinadas Verticales y Horizontales en la Tabla HTML
Como hemos dicho en artículos anteriores, una tabla en Microsoft Word es un conjunto de filas independientes. Cada fila tiene un conjunto de celdas que son independientes de las celdas de otras filas. Por lo tanto, en la tabla Microsoft Word no existe un objeto como “columna”, y “1ra columna” es algo así como “el conjunto de las 1ras celdas de cada fila de la tabla”. Esto permite a los usuarios tener una tabla en la que, por ejemplo, la primera fila consta de dos celdas: 2 cm y 1 cm, y la segunda fila consta de dos celdas diferentes: 1 cm y 2 cm de ancho. Y Aspose.Words apoya este concepto de tablas.
Una tabla en HTML tiene una estructura esencialmente diferente: cada fila tiene el mismo número de celdas y (es importante para la tarea) cada celda tiene el ancho de la columna correspondiente, el mismo para todas las celdas de una columna. Entonces, si HorizontalMerge y VerticalMerge devuelven un valor incorrecto, use el siguiente ejemplo de código:
// 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; | |
} |
Convertir a Celdas Combinadas Horizontalmente
A veces no es posible detectar qué celdas se fusionan porque algunas versiones más recientes de Microsoft Word ya no usan los indicadores de fusión cuando las celdas se fusionan horizontalmente. Pero para situaciones en las que las celdas se fusionan en una celda horizontalmente por su ancho usando indicadores de fusión, Aspose.Words proporciona el método ConvertToHorizontallyMergedCells
para convertir celdas. Este método simplemente transforma la tabla y agrega nuevas celdas según sea necesario.
El siguiente ejemplo de código muestra el método anterior en funcionamiento:
// 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(); |