2016-06-10 277 views
1

我正在使用Apache POI,並且遇到了一個奇怪的問題。我可以自動調整行的大小,但前提是該行中沒有合併的單元格。這裏有一個例子:合併單元格的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); 
} 

此代碼生成以下文件: enter image description here

然而,當我註釋掉合併兩個細胞系,輸出看起來是這樣的: enter image description here

這是一個錯誤?有誰知道解決方法?

+0

你說的自動調整大小是什麼意思? sheet.setColumnWidth? – 2016-06-10 09:44:03

+0

@RC。我的意思是,應該在文本被包裝時動態調整行高度,如第二張圖中所示。我的整個代碼如上所示。 –

+0

可能的重複:http://stackoverflow.com/questions/19145628/auto-size-height-for-rows-in-apache-poi – 2016-06-10 10:03:47

回答

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/%[email protected]%3E

RowInfoNestedCellInfo是跟蹤紙張佈局的我的自定義數據結構。你應該能夠用你的等價物和輔助函數替換它們。

private void adjustRowHeights(Sheet sheet, List<RowInfo> rows, SortedSet<Integer> createdColumnNumbers) { 
    SortedMap<Integer, Float> columnWidthsInPx = [] as TreeMap; 
    createdColumnNumbers.each { 
     columnWidthsInPx.put(it, sheet.getColumnWidthInPixels(it)); 
    } 
    rows.each { RowInfo rowInfo -> 
     if (rowInfo.hasMergedCells) { 
      Row excelRow = sheet.getRow(rowInfo.rowIndex); 

     // Find the column with the longest text - that's the one that will determine 
     // the row height 
     // 
     NestedCellInfo longestCell = rowInfo.getCellWithLongestContent(); 
     String cellText = longestCell.getText(); 
     if (cellText != null && cellText.size() > 5) { 
      int colIdx = rowInfo.cells.indexOf(longestCell); 

      // Figure out available width in pixels, taking colspans into account 
      // 
      float columnWidthInPx = columnWidthsInPx[colIdx]; 
      int numberOfMergedColumns = longestCell.colSpan; 
      (numberOfMergedColumns - 1).times { 
       columnWidthInPx += columnWidthsInPx[colIdx + it]; 
      } 

      // Setup the font we'll use for figuring out where the text will be wrapped 
      // 
      XSSFFont cellFont = longestCell.getCellFont(); 
      int fontStyle = Font.PLAIN; 
      if (cellFont.getBold()) fontStyle = Font.BOLD; 
      if (cellFont.getItalic()) fontStyle = Font.ITALIC; 

      java.awt.Font currFont = new java.awt.Font(
       cellFont.getFontName(), 
       fontStyle, 
       cellFont.getFontHeightInPoints()); 

      AttributedString attrStr = new AttributedString(cellText); 
      attrStr.addAttribute(TextAttribute.FONT, currFont); 

      // Use LineBreakMeasurer to count number of lines needed for the text 
      // 
      FontRenderContext frc = new FontRenderContext(null, true, true); 
      LineBreakMeasurer measurer = new LineBreakMeasurer(attrStr.getIterator(), frc); 
      int nextPos = 0; 
      int lineCnt = 0; 
      while (measurer.getPosition() < cellText.length()) { 
       nextPos = measurer.nextOffset(columnWidthInPx); 
       lineCnt++; 
       measurer.setPosition(nextPos); 
      } 

      if (lineCnt > 1) { 
       excelRow.setHeight((short)(excelRow.getHeight() * lineCnt * /* fudge factor */ 0.7)); 
      } 
     } 
    } 
} 

這個解決方案遠非完美,但它允許我前進。

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

      } 
     } 
    } 
相關問題