我正在使用Apache POI,我遇到了一个奇怪的问题。我可以自动调整行的大小,但前提是该行中没有合并的单元格。这是一个例子:
new FileOutputStream('test.xlsx').withStream { OutputStream os ->
Workbook workbook = new XSSFWorkbook();
Sheet sheet = workbook.createSheet();
CellStyle wrapStyle = workbook.createCellStyle();
wrapStyle.setWrapText(true);
Row row = sheet.createRow(0); row.setRowStyle(wrapStyle);
Cell cell = row.createCell(0); cell.setCellStyle(wrapStyle);
cell.setCellValue("Very long text that needs to be wrapped")
cell = row.createCell(1); cell.setCellStyle(wrapStyle);
cell.setCellValue("Short text");
cell = row.createCell(2); cell.setCellStyle(wrapStyle);
cell.setCellValue("");
// These two lines break row auto-height!
//
CellRangeAddress cellRangeAddress = new CellRangeAddress(0, 0, 1, 2);
sheet.addMergedRegion(cellRangeAddress);
workbook.write(os);
}
这是一个错误吗?有没有人知道一种解决方法?
答案 0 :(得分:0)
经过更多的研究,结果证明这是Excel本身的问题,而不是POI。对于在其中包含合并单元格的所有行,Excel确实失去了将行自动调整为内容的功能。有关详细信息,请参阅:
http://excel.tips.net/T003207_Automatic_Row_Height_For_Merged_Cells_with_Text_Wrap.html http://blog.contextures.com/archives/2012/06/07/autofit-merged-cell-row-height/
解决方法基于预测行的最大单元格中的行数,然后手动调整行高。代码基于这个讨论:
http://mail-archives.apache.org/mod_mbox/poi-user/200906.mbox/%3C24216153.post@talk.nabble.com%3E
下面的 screen
和RowInfo
是我的自定义数据结构,用于跟踪工作表布局。您应该能够用等效函数和辅助函数替换它们。
NestedCellInfo
这个解决方案远非完美,但它让我继续前进。
答案 1 :(得分:0)
通过使用Val Blant的最后一部分,我做了一些比较容易使用的东西,但却很复杂。 请注意,出于个人原因,只有一行代码会在单元格的高度上添加一行。如果您不希望如此,请将其删除。也可以随意将其更改为非静态,我必须使用静态方法,因为我正在努力使特定类保持静态。
PS:这是我在stackoverflow上的第一篇文章,请保持温和。 :)
解决方案:
public static Boolean isCellMerged(Cell cell) {
Sheet sheet = cell.getSheet();
for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
Integer cellColumn = cell.getColumnIndex();
Integer cellRow = cell.getRowIndex();
if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {
return true;
}
}
return false;
}
public static List<List<Cell>> getCellsInRowsInsideRegionRange(Cell cell) {
Sheet sheet = cell.getSheet();
List<List<Cell>> mergedRowList = new ArrayList<>();
List<Cell> mergedCellsList = new ArrayList<>();
//Nejdříve musíme zjistit sloučenou sekci dané buňky
for (CellRangeAddress mergedRegionRange : sheet.getMergedRegions()) {
Integer cellColumn = cell.getColumnIndex();
Integer cellRow = cell.getRowIndex();
if (mergedRegionRange.containsColumn(cellColumn) && mergedRegionRange.containsRow(cellRow)) {
//Protože CellRangeAddress nemá moc metod, musíme si pomoci sami a získat z ní buňky a řádky
for (Row row : sheet) {
for (Cell iteratedCell : row) {
Integer iteratedCellColumn = iteratedCell.getColumnIndex();
Integer iteratedCellRow = iteratedCell.getRowIndex();
if (mergedRegionRange.containsColumn(iteratedCellColumn) && mergedRegionRange.containsRow(iteratedCellRow)) {
//Rozdělování jednotlivých řádků
//Není-li řádek bez buněk...
if (!mergedCellsList.isEmpty()) {
//Tak buňku přidáme do Listu buněk...
mergedCellsList.add(iteratedCell);
} else {
//Pokud se jedná o první buňku prvního řádku, tak přidáme rovnou
mergedCellsList.add(iteratedCell);
}
}
}
//Vložíme List buněk daného řádku do Listu řádků
if (!mergedCellsList.isEmpty()) {
mergedRowList.add(mergedCellsList);
}
//A vyresetujeme list buněk (začneme tak nanovo novým řádkem)
mergedCellsList = null;
mergedCellsList = new ArrayList<>();
}
//Vrátíme výsledný List řádků, obsahující Listy buněk ve sloučené sekci.
if (!mergedRowList.isEmpty()) {
return mergedRowList;
} else {
return null;
}
}
}
return null;
}
public static void adjustRowHeightForRowWithNonMergedCells(Row row) {
row.setHeight((short) -1);
}
public static void adjustRowHeightForRowWithMergedCells(Row row) {
Sheet sheet = row.getSheet();
Cell longestTextCell = null;
//Potřebujeme získat buňku s nejdelším textem
for (Cell iteratedCell : row) {
String iteratedTextString = iteratedCell.getStringCellValue();
if (longestTextCell != null && StringUtils.isNotBlank(longestTextCell.getStringCellValue())) {
if (iteratedTextString.length() > longestTextCell.getStringCellValue().length()) {
longestTextCell = iteratedCell;
}
} else {
longestTextCell = iteratedCell;
}
}
//Z textově nejobsáhlejší buňky potřebujeme dostat údaje
String longestText = "";
if (StringUtils.isNotBlank(longestTextCell.getStringCellValue()) && longestTextCell != null) {
longestText = longestTextCell.getStringCellValue();
//Protože textově nejobsáhlejší buňka nemusí nutně být sloučeného typu, je zapotřebí to všude ošetřit
Boolean isLongestTextCellMerged = isCellMerged(longestTextCell);
Float longestCellWidthInPixels = 0f;
Float longestMergedCellWidthInPixels = 0f;
//Získat šířku nesloučené nejobsáhlejší buňky je jednoduché
if (!isLongestTextCellMerged) {
Integer longestCellColumnIndex = longestTextCell.getColumnIndex();
longestCellWidthInPixels = sheet.getColumnWidthInPixels(longestCellColumnIndex);
} else {
//Musíme přijít na šířku sloučené buňky namísto buňky uvnitř sloučené buňky
List<List<Cell>> cellsInMergedRegion = getCellsInRowsInsideRegionRange(longestTextCell);
longestMergedCellWidthInPixels = 0f;
//Projdeme řádky
for (List<Cell> iteratedCell2List : cellsInMergedRegion) {
Float iteratedMergedCell2WidthInPixels = 0f;
//Projdeme jednotlivé buňky ve sloučené buňce na řádku a sečteme jejich šířky
for (Cell iteratedCell2 : iteratedCell2List) {
Integer iteratedCell2ColumnIndex = iteratedCell2.getColumnIndex();
Float iteratedCell2ColumnWidthInPixels = sheet.getColumnWidthInPixels(iteratedCell2ColumnIndex);
iteratedMergedCell2WidthInPixels = iteratedMergedCell2WidthInPixels + iteratedCell2ColumnWidthInPixels;
}
//Získáme šířku nejširší sloučené buňky na řádku
if (iteratedMergedCell2WidthInPixels > longestMergedCellWidthInPixels) {
longestMergedCellWidthInPixels = iteratedMergedCell2WidthInPixels;
}
//Resetujeme sčítání
iteratedMergedCell2WidthInPixels = 0f;
}
}
//Uložíme si nejširší buňku dle toho, zda je sloučená či nikoliv
Float longestWidthInPixels;
if (isLongestTextCellMerged) {
longestWidthInPixels = longestMergedCellWidthInPixels;
} else {
longestWidthInPixels = longestCellWidthInPixels;
}
//Potřebujeme font
Workbook wb = sheet.getWorkbook();
Short fontIndex = longestTextCell.getCellStyle().getFontIndex();
Font excelFont = wb.getFontAt(fontIndex);
//Potřebujeme i jeho styl
Integer excelFontStyle = java.awt.Font.PLAIN;
if (excelFont.getBold()) excelFontStyle = java.awt.Font.BOLD;
if (excelFont.getItalic()) excelFontStyle = java.awt.Font.ITALIC;
//Potřebujeme získat skutečný font i s velikostí
java.awt.Font currentFont = new java.awt.Font(excelFont.getFontName(), excelFontStyle, excelFont.getFontHeightInPoints());
//Získáme řetězec s vlastností
AttributedString attributedString = new AttributedString(longestText);
attributedString.addAttribute(TextAttribute.FONT, currentFont);
//Použijeme LineBreakMeasurer k zjištění kolik řádků bude text potřebovat
FontRenderContext fontRenderContext = new FontRenderContext(null, true, true);
LineBreakMeasurer measurer = new LineBreakMeasurer(attributedString.getIterator(), fontRenderContext);
Integer nextPosition = 0;
Integer lineCount = 0;
while (measurer.getPosition() < longestText.length()) {
nextPosition = measurer.nextOffset(longestWidthInPixels);
//Také musíme ošetřit případ manuálně zadaných LineBreaků pro všechny možné techtle mechtle :-S
String textLine = StringUtils.substring(longestText, measurer.getPosition(), nextPosition);
Boolean containsNewLine = StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r") || StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n");
if (containsNewLine) {
if (StringUtils.containsIgnoreCase(textLine, "\r\n") || StringUtils.containsIgnoreCase(textLine, "\\r\\n")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
} else {
if (StringUtils.containsIgnoreCase(textLine, "\r") || StringUtils.containsIgnoreCase(textLine, "\\r")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\r");
}
if (StringUtils.containsIgnoreCase(textLine, "\n") || StringUtils.containsIgnoreCase(textLine, "\\n")) {
lineCount = lineCount + StringUtils.countMatches(textLine, "\n");
}
}
lineCount = lineCount + StringUtils.countMatches(textLine, "\\r?\\n");
}
lineCount++;
measurer.setPosition(nextPosition);
}
//Máme počet řádků, zbývá konečný dopočet výšky řádku a jeho použití
if (lineCount > 1) {
Float fontHeight = currentFont.getLineMetrics(longestText, fontRenderContext).getHeight();
//Pro jistotu přidáme jeden řádek navíc, člověk nikdy neví...
lineCount = lineCount + 1;
//Potřebujeme získat poslední řádek
Row lastRow = null;
if (isCellMerged(longestTextCell)) {
List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
Integer lastRowInMergedSectionIndex = mergedCellsInRows.size() - 1;
List<Cell> lastRowInMergedSection = mergedCellsInRows.get(lastRowInMergedSectionIndex);
lastRow = lastRowInMergedSection.get(0).getRow();
} else {
lastRow = longestTextCell.getRow();
}
//Je potřeba ošetřit velikosti, pokud má sloučená buňka vícero řádků
Float cellsMergedAboveHeight = 0f;
if (isCellMerged(longestTextCell)) {
if (getCellsInRowsInsideRegionRange(longestTextCell).size() > 1) {
List<List<Cell>> mergedCellsInRows = getCellsInRowsInsideRegionRange(longestTextCell);
for (List<Cell> rowsWithCells : mergedCellsInRows){
if (!lastRow.equals(rowsWithCells.get(0).getRow())){
cellsMergedAboveHeight = cellsMergedAboveHeight + rowsWithCells.get(0).getRow().getHeight();
}
}
}
}
//Vzorec je ((Velikost fontu krát počet řádků plus (počet řádků krát volný prostor mezi řádky)) krát přepočet Excelu) mínus výška sloučených buněk nad posledním řádkem.
Short finalRowHeight = (short) (((fontHeight * lineCount + (lineCount * 15))* 10) - cellsMergedAboveHeight);
//A výsledek nastavíme na poslední řádek, protože jinak to przní sloupce vlevo a vpravo od vyšších řádků
lastRow.setHeight(finalRowHeight);
}
}
}