2011年7月9日土曜日

POIを使用してJRubyでExcel操作

RubyでExcelファイルを生成したいと思い、Spreadsheetを使ってみたのですが、
テンプレートファイルからのコピーがうまく出来なかったため、POIとJRubyでExcelファイルを作成しました。
include_class "org.apache.poi.poifs.filesystem.POIFSFileSystem"
include_class "org.apache.poi.hssf.usermodel.HSSFWorkbook"
include_class "org.apache.poi.hssf.usermodel.HSSFSheet"
include_class "org.apache.poi.hssf.usermodel.HSSFRow"
include_class "org.apache.poi.hssf.usermodel.HSSFCell"
include_class "org.apache.poi.hssf.usermodel.HSSFDateUtil"
include_class "org.apache.poi.ss.util.CellRangeAddress"
include_class "org.jruby.util.IOInputStream"

def make_excel
  filename = "template.xls"
  ist = java.io.FileInputStream.new filename
  template = HSSFWorkbook.new ist
  @newSheet = template.createSheet
   template.setSheetName(1, "シート名")
  # シートの倍率設定
  @newSheet.setZoom(85, 100)
  # セルの結合数
  unionCellCount = @tempsheet.getNumMergedRegions()
  # 行のコピー
  rowCopy(0, 4, unionCellCount, 0)
  #値入力
  setCellValue(@newSheet.getRow(1), 1, 10)
  # 印刷倍率設定
  ps = @newSheet.getPrintSetup()
  ps.setScale(58)
  template.removeSheetAt(0)
  newFileName = "ファイル名.xls"
  io = java.io.FileOutputStream.new(newFileName)
  template.write(io)
end

シートの倍率設定など、基本的な操作は簡単に出来ます。
ただ、テンプレートからのコピーになると、若干めんどくさくなります。

# セルに値を設定
def setCellValue(targetRow, cellNum, content)
  targetCell = targetRow.getCell(cellNum)
  targetCell.setCellValue(content)
end

# セルの書式を設定
def setCellFormula(targetRow, cellNum, content)
  targetCell = targetRow.getCell(cellNum)
  targetCell.setCellFormula(content)
end
    
# 指定範囲の行コピー
def rowCopy(startCount, endCount, unionCount, cnt)
  for i in startCount .. endCount
    # テンプレートの行情報取得
    copyRow = @tempsheet.getRow(i)
    # 新しい行を追加
    newRow = @newSheet.createRow(i + cnt)
    length = copyRow.getLastCellNum()
    copyHeight = copyRow.getHeight()
    newRow.setHeight(copyHeight)
    for j in 0 .. length
      copyCell = copyRow.getCell(j)
      if copyCell != nil
        newCell = newRow.createCell(j)
        copyWidth = @tempsheet.getColumnWidth(j)
        @newSheet.setColumnWidth(j, copyWidth)
        copyCellStyle = copyCell.getCellStyle()
        newCell.setCellStyle(copyCellStyle)
        # セルの書式コピー
        case copyCell.getCellType()
        when HSSFCell::CELL_TYPE_STRING
          newCell.setCellValue(copyCell.getRichStringCellValue())
        when HSSFCell::CELL_TYPE_NUMERIC
          if HSSFDateUtil::isCellDateFormatted(copyCell) then
             newCell.setCellValue(copyCell.getDateCellValue())
        else
          newCell.setCellValue(copyCell.getNumericCellValue())
        end
        when HSSFCell::CELL_TYPE_FORMULA
          newCell.setCellFormula(copyCell.getCellFormula())
        when HSSFCell::CELL_TYPE_BOOLEAN
          newCell.setCellValue(copyCell.getBooleanCellValue())
        end
      end
    end
  end

  cra = nil
  # セルの結合設定のコピー
  for i in 0 .. unionCount - 1
    cra = @tempsheet.getMergedRegion(i)
    if cra.getFirstRow() >= startCount && cra.getFirstRow() <= endCount then
      cra.setFirstRow(cra.getFirstRow() + cnt)
      cra.setLastRow(cra.getLastRow() + cnt)
      @newSheet.addMergedRegion(cra);
    end
  end
end
行をコピーする際には、1行、1セルずつ、値、書式をコピーしています。