POI如何自動調整Excel單元格中字體的大小


問題

  目的是要將Excel中的文字全部顯示出來,可以設置對齊格式為【縮小字體填充】,但是這樣的話只能展示出一行數據,字體會變得很小。還有一種辦法,設置對齊格式為【自動換行】,然后讓單元格中的字體自動調整大小。

  我的實現思路是,設置單元格中的字體大小,最大10號字,最小5號字,判斷優先使用大的字體;如果最小的5號字也放不下,那就只能調整模板了。關鍵點就是判斷當前字號能否將內容完全展示在單元格中。

  需要提前說明一點,我的這個方法是不精確的算法,excel設置字體的時候太強大了,不同的字體的字間距、行間距都會不同。

 

關鍵點

  判斷字體大小是否合理的思路:

  1、計算出單元格的總寬度、總高度
  2、計算出其中的內容的總長度,不同的字號,長度是不同的
  3、內容總長度除以單元格寬度,得出實際上一共有多少行數據 x
  4、單元格的總高度除以內字體的高度,得出能展示出來的數據有多少行 y
  5、如果 y>=x ,那么表示所有的數據都能展示出來

  這個判斷單元格中的字體大小是否合理的思路也不難,難的是如何獲取到需要的參數。

 

注意點

  1、設置單元格字體大小的方法是:font.setFontHeightInPoints(k),但是獲取字體寬度和高度的方法並不精確,因為字體間會有字間距,換行以后行之間也有間距,所以這個算法並不精確。
    這個例子中,我的獲取字體高度的方法是直接取 k,獲取字體寬度的方法是 k*2

  2、在上訴關鍵點的第5步中,本來我的想法是 y向下取整, x向上取整,然后再進行比較。但是測試后發現,設置的字體都會偏小。 直接取y>=x,結果反而更合理些。

  3、進行相除運算,單位必須相同。POI中Point(坐標點)和Pixel(像素點)的大小關系,我在之前的文章有介紹過,引用結論,獲取行高的像素值的方法就是: (row.getHeightInPoints() / 72) * 96

 

代碼實例

  樣例中的單元格是合並單元格,起始坐標 (3,5),結束坐標 (3,8)

public static void main(String[] args) {

    try (InputStream is = new FileInputStream("E:\\test1.xls");
         Workbook book = new HSSFWorkbook(is);) {

        Cell cell = book.getSheetAt(0).getRow(3).getCell(5);
        String str = "一二三四五六七八九十一二三四五六七八九十一二三四五六七八九十";
        cell.setCellValue(str);

        setFontSize(book, cell);

        File f = new File("E:\\test2.xls");
        FileOutputStream out = new FileOutputStream(f);
        book.write(out);
    } catch (IOException e) {
        //return;
    }
}

/**
 * 設置單元格字體大小
 */
private static void setFontSize(Workbook book, Cell cell) {
    Font font = book.createFont();
    font.setFontName("EUDC");
    for (short k = 10; k >= 5; k--) {
        font.setFontHeightInPoints(k);
        if (checkCellReasonable(cell, k)) {
            break;
        }
    }
    //解決單元格樣式覆蓋的問題
    CellStyle cStyle = book.createCellStyle();
    cStyle.cloneStyleFrom(cell.getCellStyle());
    cStyle.setWrapText(true);
    cStyle.setFont(font);
    cell.setCellStyle(cStyle);
}

/**
 * 校驗單元格中的字體大小是否合理
 */
private static boolean checkCellReasonable(Cell cell, short fontSize) {
    int sum = cell.getStringCellValue().length();
    double cellWidth = getTotalWidth(cell);
    double fontWidth = (double) fontSize / 72 * 96 * 2;
    double cellHeight = cell.getRow().getHeightInPoints();
    double rows1 = fontWidth * sum / cellWidth;
    double rows2 = cellHeight / fontSize;
    return rows2 >= rows1;
}

/**
 * 獲取單元格的總寬度(單位:像素)
 */
private static double getTotalWidth(Cell cell) {
    int x = getColNum(cell.getSheet(), cell.getRowIndex(), cell.getColumnIndex());
    double totalWidthInPixels = 0;
    for (int i = 0; i < x; i++) {
        totalWidthInPixels += cell.getSheet().getColumnWidthInPixels(i + cell.getColumnIndex());
    }
    return totalWidthInPixels;
}

/**
 * 獲取單元格的列數,如果是合並單元格,就獲取總的列數
 */
private static int getColNum(Sheet sheet, int row, int column) {
    int sheetMergeCount = sheet.getNumMergedRegions();
    //判斷該單元格是否是合並區域的內容
    for (int i = 0; i < sheetMergeCount; i++) {
        CellRangeAddress ca = sheet.getMergedRegion(i);
        int firstColumn = ca.getFirstColumn();
        int lastColumn = ca.getLastColumn();
        int firstRow = ca.getFirstRow();
        int lastRow = ca.getLastRow();

        if (row >= firstRow && row <= lastRow && column >= firstColumn && column <= lastColumn) {
            return lastColumn - firstColumn + 1;
        }
    }
    return 1;
}

 

  其中,獲取單元格總寬度的方法getTotalWidth(Cell cell),有更簡單的方法,在《我的POI代碼庫》里介紹

 

原創文章,歡迎轉載,轉載請注明出處!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM