Apache POI中如何设置列宽

68
我正在使用Apache POI API编写Java工具,将XML转换为MS Excel。在我的XML输入中,我以点为单位接收列宽。但是,Apache POI API根据字体大小等有一种略微奇怪的逻辑来设置列宽(请参阅API文档)。是否有将点转换为Excel期望的宽度的公式?有人之前做过这个吗?虽然有setRowHeightInPoints()方法,但没有用于列的方法。P.S.:输入的XML格式为ExcelML,我必须将其转换为MS Excel。
5个回答

101

很不幸,只有来自Sheet类的函数setColumnWidth(int columnIndex, int width)可用;其中宽度是标准字体(工作簿中的第一个字体)中的字符数目。如果您的字体在变化,则无法使用该函数。

这里解释了如何根据字体大小计算宽度的公式:

width = Truncate([{NumOfVisibleChar} * {MaxDigitWidth} + {5PixelPadding}] / {MaxDigitWidth}*256) / 256

在输入数据后,您可以始终使用 autoSizeColumn(int column, boolean useMergedCells) 来自动调整您的Sheet中列的宽度。


2
autoSizeColumn()要小心使用; 因为它依赖于可能不存在的字体,所以在某些系统上可能会引发错误。 - mcv

34
请注意使用 autoSizeColumn() 方法。它可以在小文件上无问题使用,但请注意该方法每列只调用一次(在最后),不应在循环中调用,否则将没有意义。
请避免在大型 Excel 文件上使用 autoSizeColumn() 方法,该方法会导致性能问题。
我们在一个 110k 行 / 11 列的文件上使用它。该方法花费了约 6 分钟来自动调整所有列的大小。
有关更多详细信息,请参阅:如何加速 Apache POI 中的列自适应大小?

2
这是对被接受答案的评论,而不是原问题的自包含答案。 - Jeremy Caney
另外,autoSizeColumn()在某些系统上可能会引发错误,因为它需要访问可能未安装的字体。我花了两天时间才弄清楚这一点。 - mcv

17
你也可以使用此博客中提到的实用方法:使用Apache POI从Excel获取单元格宽度和高度。它可以解决你的问题。
从该博客中复制并粘贴:
static public class PixelUtil {

    public static final short EXCEL_COLUMN_WIDTH_FACTOR = 256;
    public static final short EXCEL_ROW_HEIGHT_FACTOR = 20;
    public static final int UNIT_OFFSET_LENGTH = 7;
    public static final int[] UNIT_OFFSET_MAP = new int[] { 0, 36, 73, 109, 146, 182, 219 };

    public static short pixel2WidthUnits(int pxs) {
        short widthUnits = (short) (EXCEL_COLUMN_WIDTH_FACTOR * (pxs / UNIT_OFFSET_LENGTH));
        widthUnits += UNIT_OFFSET_MAP[(pxs % UNIT_OFFSET_LENGTH)];
        return widthUnits;
    }

    public static int widthUnits2Pixel(short widthUnits) {
        int pixels = (widthUnits / EXCEL_COLUMN_WIDTH_FACTOR) * UNIT_OFFSET_LENGTH;
        int offsetWidthUnits = widthUnits % EXCEL_COLUMN_WIDTH_FACTOR;
        pixels += Math.floor((float) offsetWidthUnits / ((float) EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH));
        return pixels;
    }

    public static int heightUnits2Pixel(short heightUnits) {
        int pixels = (heightUnits / EXCEL_ROW_HEIGHT_FACTOR);
        int offsetWidthUnits = heightUnits % EXCEL_ROW_HEIGHT_FACTOR;
        pixels += Math.floor((float) offsetWidthUnits / ((float) EXCEL_ROW_HEIGHT_FACTOR / UNIT_OFFSET_LENGTH));
        return pixels;
    }
}

当你想要获取单元格的宽度和高度时,可以使用以下代码以像素为单位获取数值,这些数值仅供参考。

PixelUtil.heightUnits2Pixel((short) row.getHeight())
PixelUtil.widthUnits2Pixel((short) sh.getColumnWidth(columnIndex));

@Nelda.techspiress:我同意。这段代码非常好,应该被添加到Apache POI中! - kevinarpe
我赞同@kevinarpe,这段代码节省了我很多时间!lu_ko,感谢你 :) - Just Another Developer
实际上,这段代码有两个问题。使用"short"会导致pixel2WidthUnits周围的像素值为负数,建议使用int类型。其次,没有必要使用复杂的OFFSET MAP数组进行计算。这个公式可以得到与pixel2WidthUnits完全相同的结果: return pxs * EXCEL_COLUMN_WIDTH_FACTOR / UNIT_OFFSET_LENGTH - Vijay Jagdale

3

使用 Scala,有一个很好的包装器 spoiwo

你可以这样做:

Workbook(mySheet.withColumns(
      Column(autoSized = true),
      Column(width = new Width(100, WidthUnit.Character)),
      Column(width = new Width(100, WidthUnit.Character)))
    )

3
我通过为所有列和单元格设置默认宽度来解决了我的问题,例如下面的代码所示:
int width = 15; // Where width is number of caracters 
sheet.setDefaultColumnWidth(width);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接