POI如何自动调整Excel单元格中字体的大小

在Excel里用Ctrl+向下方向键可以到达最后一行。
可以看到:
1、对2003版,最大行数是65536行
2、对2007以上版本,最大行数是1048576行

HSSFRow row = sheet.createRow((short) 0);
HSSFCellStyle style = workbook.createCellStyle();
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
HSSFFont font = workbook.createFont(); 
style.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
style.setAlignment(HSSFCellStyle.ALIGN_CENTER);
font.setFontName("宋体");
font.setFontHeightInPoints((short) 14);// 设置字体大小

问题

  目的是要将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代码库》里介绍

https://www.cnblogs.com/acm-bingzi/p/poiFontSizeAuto.html

原文地址:https://www.cnblogs.com/softidea/p/10207194.html