セルの値を読む

Calcドキュメントのセル情報はtable:table-cell要素に記録されています。 Calcに表示される内容は子のtext:p要素に書き出されます。 text:p要素の内容は書式コードに従って整形された後の文字列です。 例えば数字が記録されたセルで「3桁ごとにコンマを打ち、頭に円記号を付ける」などの変換がされた後の文字列になります。

整形される前の値を取り出すにはtable:table-cellタグの属性を調べます。 office:value-type属性にセルに書かれている値のタイプが記録されています。 タイプに対応したもう1つの属性があるので、その値を読みます。

タイプには次のようなものがあります。(よく使いそうなもののみ抜粋)

value-type対応する属性
floatoffice:value
dateoffice:date-value
booleanoffice:boolean-value
stringoffice:string-value (式を使ったときのみ)

例えば、2,980円と書かれたセルはxmlでは次のようになります。

<table:table-cell office:value-type="float" office:value="2980">
    <text:p>2,980円</text:p>
</table:table-cell>

Integer.parseIntをするときはoffice:value属性の値を使った方がよいでしょう。 整形をCalcに任せて、書式のコーディングをしないのならtext:pの内容を使った方が楽です。

セルに式を書いた場合、式の内容はtable:table-cellタグのtable:formula属性に記録されます。 式の計算結果はvalue属性に出力されます。 Calcが計算した結果を利用するだけなら、意識する必要はありません。

// 各セルの内容を文字列に変換する
private String parseCellElement(Element cellElement)
{
    // セルに表示されている文字列を取り出すだけの場合はこれ
    // return cellElement.getTextContent();

    // セルのタイプで場合分け
    String valueType = cellElement.getAttribute("office:value-type");
    String res = null;
    if(valueType.equals("float") )
    {
        res = cellElement.getAttribute("office:value");
    }
    else if(valueType.equals("date") )
    {
        res = cellElement.getAttribute("office:date-value");
    }
    else if(valueType.equals("boolean") )
    {
        res = cellElement.getAttribute("office:boolean-value");
    }
    else
    {
        // 子ノードの「text:p」タグからテキスト情報を読む
        if(cellElement.hasChildNodes() )
        {
            StringBuilder textp;
            textp = parseTextpElement( (Element)cellElement.getFirstChild() );
            res = textp.toString();
        }
        else
        {
            res = "";
        }
    }

    return res;
}

text:pタグ内のスタイルやリンク

text:p要素にはテキストノードだけではなく文字修飾やハイパーリンクのタグが含まれています。 文字修飾はtext:spanという範囲タグで表現されます。 text:style-nameという属性にスタイル名が記録されています。 ハイパーリンクはtext:aというアンカータグで表現されます。 リンク先はxlink:href属性に記録されています。

<text:p>
    リンク
    <text:span text:style-name="T1">の</text:span>
    テスト ... 
    <text:a xlink:href="http://www15.plala.or.jp/kichijitsu/index.html">
        片鱗懐古のページ
    </text:a>
    へ移動
</text:p>

text:spanタグは文字列の一部を修飾するとき使用されます。 セルに含まれる文字列全体のスタイルが設定されている場合、table:table-cellタグにスタイルが記述されるようです。

spanとアンカーをhtmlのタグに変更し、文字列で返すコードは以下のようになります。

private StringBuilder parseTextpElement(Element textpElement)
{
    StringBuilder res = new StringBuilder();
    
    NodeList nodeList = textpElement.getChildNodes();
    int nodeLen = nodeList.getLength();
    
    for(int i = 0; i < nodeLen; i++)
    {
        Node node = nodeList.item(i);
        Short nodeType = node.getNodeType();
        
        switch(nodeType)
        {
        // テキストノードはそのまま追加
        case Node.TEXT_NODE:
            res.append(node.getNodeValue() );
            break;
        case Node.ELEMENT_NODE:
            String nodeName = node.getNodeName();
            Element elem = (Element)node;
            
            // 外部リンクはhtmlのアンカータグに変更
            if(nodeName.equals("text:a") )
            {
                String href = elem.getAttribute("xlink:href");
                res.append("<a href=\"");
                res.append(href);
                res.append("\">");
                res.append(elem.getFirstChild().getNodeValue() );
                res.append("</a>");
            }
            // 文字修飾はhtmlのspanタグに変更
            else if(nodeName.equals("text:span") )
            {
                String styleName = elem.getAttribute("text:style-name");
                if(styleName.isEmpty() )
                {
                    res.append("<span>");
                }
                else
                {
                    res.append("<span class=\"accent\">");
                }
                res.append(parseTextpElement(elem) );
                res.append("</span>");
            }
            // それ以外は再帰で子ノードを調べる
            else
            {
                res.append(parseTextpElement(elem) );
            }
            break;
        }
    }
    
    return res;
}