2018년 8월 23일 목요일

Apache POIで実現が手間、困難な事

■Apache POIで実現が手間、困難な事

投稿者:  | 2014年4月1日
今回の開発でApache POI(以下POI)を使用する機会があったので
POIで実現が手間、困難な事を紹介したいと思います。
ライブラリを選定する参考になれば幸いです。
今回使用した開発言語、POIとExcelのバージョンは下記の通りです。
・Java 1.7.0_45 64bit
・poi 3.8 20120326
・Excel 2010 xlsx形式

1.シートのコピーでオブジェクトのコピーが行えない

 図形やグラフ、フォーム部品などオブジェクトを含む
 シートのコピーを行った場合、ファイルが破損します。
 予めオブジェクトが置かれているシートを操作する、
 コピーしたシートにオブジェクトの追加などは
 問題ありません。

2.列単位の操作が行えない

 処理単位が行やセルしか無く、列単位での挿入は
 行をループさせながらセルを増やすという処理を
 実装しなければなりません。

3.基本的にセル範囲は行、セルの追加とは非同期

 結合セル、参照、印刷範囲などのセル範囲は行の追加削除、
 セルの追加削除を行っても自動的には更新されません。
 場合によってはファイルが破損するので注意が必要です。
 全て範囲を確認しながら範囲を広げたり、狭めたりを
 行わなければなりません。

4.行の自動高さ調整はされない

 多くのExcelライブラリがそうであるようにセルに
 折り返すようなテキストを挿入しても行の自動高さ調整は
 されません。

5.画像の細かい調整できない

 配置した画像の位置や大きさなどをピクセル指定などで
 調整する事ができないようです。
 配置自体はセル範囲に対して行うのですが、
 セルに対してフィットするように配置されて縦横比は
 無視されます。
 セル範囲からオフセットを行う引数もあるのですが、
 残念ながら今回は上手く機能させる事ができませんでした。

6.配置しているグラフを操作する方法がない

 3.8バージョンでは配置済みのグラフを取得できる処理があると
 思うのですが、上手く取得する事ができませんでした。
 結果、配置しているグラフのデータ範囲などを更新する
 事はできませんでした。
 名前定義は更新が可能なので上手くテンプレートを作成すれば
 データ範囲は調整が可能かも知れません。
 また、グラフの新規作成についてもドキュメントが非常に
 少なく、あまり現実的ではありません。
 グラフ自体は予め配置してデータ範囲を決めておけば、
 出力した値での集計は問題なく可能です。

7.非常に多くのメモリを使用する

 3.8時点でのPOIは全てのデータをメモリ上に展開する為、
 かなりのメモリを消費します。
 保存された実ファイルの10倍以上は使用するようです。
 クライアント側で動作させるプログラムなら大きな問題には
 ならないかも知れませんが、WEBサーバで動作させる場合には
 出力するデータに制限をかける事をオススメします。
以上が今回の開発で悩まされたPOIで実現が手間、困難な事です。
デメリットは大きいですが、オープンソースというメリットは
非常に大きいものです。
いくつかの問題点はラッピングクラスなどを実装すれば
使いやすいようにカスタマイズできると思います。
問題が多いオブジェクトに関してもある程度はテンプレートの
工夫などで切り抜ける事ができますので、
簡単な帳票や表などには十分使用できると思います。
今後はオブジェクトやメモリの問題なども改善していく
予定があるようなので、期待したい所です。
システム構築のご依頼でしたらお気軽にご相談下さい。
弊社お問い合わせ

POIで、読み込んだExcelファイルから図形を取得して値をセットする

POIで、読み込んだExcelファイルから図形を取得して値をセットする

  
Excelファイル(※2007以降の.xlsx形式)から目的の図形を取得し、値をセットする。
今回はテキストボックスを取得してセットするサンプル。

既に対象のWorkbookとSheetを取得している状態から。
// 図形描画の為のXSSFDrawingから図形のListを取得
XSSFDrawing drawing = sheet.createDrawingPatriarch();
List shapeList = drawing.getShapes();

// Listから目的の図形を探索
for (XSSFShape sh : shapeList) {
  // (1)実際の型を確認
  if (sh instanceof XSSFSimpleShape) {
     XSSFSimpleShape ss = (XSSFSimpleShape) sh;
     // (2)テキストの値をkeyに対象かどうか判断
     if (ss.getText().equals("key")) {
      ss.setText("value you want to set");
     }
  }
}

(1)Listで取得されるオブジェクトはXSSFShapeのサブクラス(※)であり、検査せずにSimpleShapeとして扱おうとするとClassCastExceptionが発生する。
 ※こいつら。↓
  XSSFConnector, XSSFGraphicFrame, XSSFPicture, XSSFShapeGroup, XSSFSimpleShape。

(2)APIDocなんかを読み漁ったけれども、定義した名前から図形を取得する方法がわからず。とりあえずテキストから判断するやり方で記述。

【環境】

 POI 3.10
 Java SE7

【参考】

XSSFSimpleShape (POI API Documentation)


[追記 2014/5/7] 

上記のやり方で値をセットすると、図形にもともと設定されていたフォントや揃方がクリアされてデフォルト値になってしまうことがわかった。

もともと設定されていたフォントなどを維持したままテキストのみを置き換える場合は下記のようにテキストを分解し、テキストの最小単位(TextRun)に対して操作を行わなければいけないらしい。
// 上記の11行目から
if (ss.getText().equals("key")) {
  for (XSSFTextParagraph p : ss.getTextParagraphs()) {
   for (XSSFTextRun r : p.getTextRuns()) {
    r.setText("value you want to set");  // ※1
   }
  }
}
※1 改行を含む文字列をブチ込んでも大丈夫だった。だがParagraphの扱いがよくわかっていないので、複数行を操作する場合はもう少し工夫が必要なのかもしれない。

FontはFontクラスで管理してたり、AlignはTextParagraphで管理していたり。Excelの実装自体がそうなのかもしれないけど、Excel上の設定と各オブジェクトの結びつきがわかりづらく、直感的に操作しづらい。。