POIでコネクタを描いてみる
POIでコネクタは描けるのか?
SIerはExcelが大好きなので、設計書もExcelで作ることが多い(いいか悪いかは置いといて…)。
でも手で全部書くのは面倒なので、Javaのソースから自動生成できると便利だなー、とか思うことがある。
設計書には、オートシェイプとかコネクタを使って図も入れたい。
だけど、POIでコネクタは描けるんだろうか?
一応それらしいメソッドはあったが…
Javadocを覗くと、XSSFDrawingクラスにcreateConnectorというメソッドがあった。
これっぽいんだけど…、こいつの引数はXSSFClientAnchorなんだよね。
つまり、座標を指定するんだけど、接続先の図形は指定しなくていいの?
とりあえず、コードを書いて実行してみる。
try (Workbook workbook = new XSSFWorkbook()) {
Sheet sheet = workbook.createSheet();
XSSFDrawing drawing = (XSSFDrawing)sheet.createDrawingPatriarch();
XSSFClientAnchor anchor1 = drawing.createAnchor(0, 0, 400000, 100000, 1, 1, 1, 1);
XSSFTextBox textBox1 = drawing.createTextbox(anchor1);
textBox1.setLineWidth(1);
textBox1.setLineStyleColor(255, 0, 0);
textBox1.setFillColor(255, 255, 0);
XSSFClientAnchor anchor2 = drawing.createAnchor(0, 0, 400000, 100000, 2, 3, 2, 3);
XSSFTextBox textBox2 = drawing.createTextbox(anchor2);
textBox2.setLineWidth(1);
textBox2.setLineStyleColor(255, 0, 0);
textBox2.setFillColor(255, 255, 0);
XSSFClientAnchor anchor3 = drawing.createAnchor(200000, 100000, 200000, 0, 1, 1, 2, 3);
XSSFConnector connector = drawing.createConnector(anchor3);
connector.setLineWidth(1);
connector.setLineStyleColor(128, 0, 0);
File file = new File("connector.xlsx");
try (OutputStream out = new FileOutputStream(file)) {
workbook.write(out);
}
}
一応それらしいのはできたんだけど…

テキストボックスを動かしてみると、線が付いてこない。

これは「コネクタ」じゃなくて、ただの「線」だ。
コネクタのXMLを見てみる
とは言え、XSSFConnectorにそれらしいメソッドは見当たらない。
ただ、XMLレベルでいじれるメソッド(getCTConnector)はあるので、これを使えば何とかなるかも。
という訳で、コネクタが保存されたxlsxファイルのXML構造を調べてみる。
上で作成したxlsxファイルの他、ちゃんとコネクタの接点をつなげたxlsxファイルを用意して、拡張子をzipに変えて展開、xl/drawings/drawing1.xmlを見比べてみる。
すると、後者だけにある怪しい要素が見つかった。
<xdr:wsdr>
<xdr:twoCellAnchor>
<xdr:cxnSp>
<xdr:nvCxnSpPr>
<xdr:cNvCxnSpPr>
<a:stCxn id="2" idx="2"/>
<a:endCxn id="3" idx="0"/>
</xdr:cNvCxnSpPr>
...
おそらく、idは図形を特定するID、idxは図形における接点の場所(たぶん上が0で下が2)だろう。
IDの謎
で、以下のようなコードを追加したんだけど…、うまくいかない…。
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().addNewStCxn().setId(textBox1.getCTShape().getNvSpPr().getCNvPr().getId());
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().getStCxn().setIdx(2);
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().addNewEndCxn().setId(textBox2.getCTShape().getNvSpPr().getCNvPr().getId());
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().getEndCxn().setIdx(0);
いろいろ調べてみると、どうもIDがおかしいっぽい。
正しいxlsxの方は、
テキストボックス1 = 2 テキストボックス2 = 3 コネクタ = 4
なんだけど、POIの方は
テキストボックス1 = 1 テキストボックス2 = 2 コネクタ = 1
になっている。
IDが重複しているのがおかしいし、正しいxlsxでは1が欠番になっている。
以下のようにしたら、どうやらうまくいったようだ。
textBox1.getCTShape().getNvSpPr().getCNvPr().setId(2);
textBox2.getCTShape().getNvSpPr().getCNvPr().setId(3);
connector.getCTConnector().getNvCxnSpPr().getCNvPr().setId(4);
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().addNewStCxn().setId(textBox1.getCTShape().getNvSpPr().getCNvPr().getId());
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().getStCxn().setIdx(2);
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().addNewEndCxn().setId(textBox2.getCTShape().getNvSpPr().getCNvPr().getId());
connector.getCTConnector().getNvCxnSpPr().getCNvCxnSpPr().getEndCxn().setIdx(0);
ちゃんとつながっている!

とは言え、また今回も謎が残ってしまった…。
| OS | Windows 7 |
| Office | 2010 |
| Java | 8u31 |
| Apache POI | 3.11 |