POIの座標について調べてみる
業務システムでは「Excelファイルをアップロードしてデータを入力したい」「検索結果をExcelファイルでダウンロードしたい」というような要求が日常茶飯事だ。
そんなときに役に立つのが、ApacheのPOI。
POIではセルの値を読み書きするだけでなく、図形を描くことだってできる。
しかし、どうやって座標を設定すればいいの?と悩んでしまう。
そこで、POIの座標について調べてみた。
POIで行の高さと列の幅を取得してみる
まずは、POIで行の高さと列の幅を取得してみた。
public static void main(String[] args) throws IOException { try (Workbook workbook = new XSSFWorkbook()) { Sheet sheet = workbook.createSheet(); Row row = sheet.createRow(0); System.out.println("行1の高さ(ポイント)=" + row.getHeightInPoints()); System.out.println("列Aの幅(ピクセル)=" + sheet.getColumnWidthInPixels(0)); File file = new File("test.xlsx"); try (OutputStream out = new FileOutputStream(file)) { workbook.write(out); } } }
実行すると、
行1の高さ(ポイント)=15.0 列Aの幅(ピクセル)=56.0136
となった。
Excelと高さが違う?
実際にtest.xlsxを開いてみると…、行1の高さは「13.50(18ピクセル)」、列Aの幅は「8.38(72ピクセル)」と出る。
高さの13.5というのは、ポイント単位らしい。
あれ?15.0ではない?
そこでPOIのXSSFRowクラスのソースを見てみると、
public float getHeightInPoints() { if (this._row.isSetHt()) { return (float) this._row.getHt(); } return _sheet.getDefaultRowHeightInPoints(); }
のようになっている。つまり、行の高さが設定されていない場合は、シートのデフォルト値を見に行くようだ。
更に、test.xlsxの拡張子を.zipに変えて展開し、xl/worksheets/sheet1.xmlを見てみる。
<sheetFormatPr defaultRowHeight="15.0"/>
となっていて、この値が返されているというみたいだ。
このデフォルト値は実際にはExcelで使われていないのだろうか?
試したところ、デフォルトのままではなく、明示的にrow.setHeightInPointsを指定してxlsxを出力すると、Excelで見える値と合うようだ。
Excelと幅も違う?
一方幅の方は、XSSFSheetのソースを見ると、
public int getColumnWidth(int columnIndex) { CTCol col = columnHelper.getColumn(columnIndex, false); double width = col == null || !col.isSetWidth() ? getDefaultColumnWidth() : col.getWidth(); return (int)(width*256); } @Override public float getColumnWidthInPixels(int columnIndex) { float widthIn256 = getColumnWidth(columnIndex); return (float)(widthIn256/256.0*XSSFWorkbook.DEFAULT_CHARACTER_WIDTH); } public int getDefaultColumnWidth() { CTSheetFormatPr pr = worksheet.getSheetFormatPr(); return pr == null ? 8 : (int)pr.getBaseColWidth(); }
のようになっている。
DEFAULT_CHARACTER_WIDTHは7.0017fと定義されているので、8 * 256 / 256 * 7.0017f = 56.0136が返されたということか。
こっちも、Excelのデフォルト値と違うなあ…。
試しにsheet.setColumnWidth(0, 8*256)でExcelブックを作ると、Excel上では64ピクセルになった。
72ピクセルにするには、9*256を設定する必要があるようだ。
9の意味は不明…。
フォントサイズを変えるとどうなる?
でも、たぶんデフォルトの幅とか高さはフォントサイズに依存するよね。
自分の環境のExcelでは11がデフォルトだ。
POIのAPIを見たが、デフォルトフォントらしきものは見当たらない。
しかし、例によってxlsxファイルをzipとして展開すると、xl/styles.xmlに
... <fonts x14ac:knownfonts="1" count="2"> <sz val="11"></sz> ... </fonts>
というのがあった。ということは、
workbook.getFontAt((short)0).setFontHeightInPoints((short)16);
でいけるかな?やってみたら、実際デフォルトフォントサイズが16のExcelブックを作れた。
でも、Excelの行の高さは18.75、列幅は8.09(96ピクセル)と大きくなったが、POIから取得したデフォルトの高さと幅は11ポイントの時と同じだった。
デフォルトではなく明示的に列幅を sheet.setColumnWidth(0, 9*256) のように設定したらどうなるか?
Excel上では99ピクセルになった。やっぱり、フォントサイズに依存するようだ。
いろいろなフォントサイズで試したところ、
Excel上のピクセル数 = columnWidth / 256 * ceil(フォントサイズ / 1.5)
が成り立っているようだ。この1.5の意味も不明…。
まとめ
- デフォルトのままだと、POIから取得した行の高さとか列幅は期待した値にならないみたい。
- Row.setHeightInPointsを設定すると実際のExcelの行の高さと合う。
- Sheet.setColumnWidthで設定した値から実際のExcelの列幅が得られるけど、関係式の意味はよく分からなかった。
...あまりまとまってないなあ。。
OS | Windows 7 |
Office | 2010 |
Java | 8u31 |
Apache POI | 3.11 |