LibreOfficeの水平アラインメントバグ

2019年2月3日

以下は本当にバグか不明だが、つまりLibreOffice側の問題か不明であるが、Bugzillaに出しておいた。

https://bugs.documentfoundation.org/show_bug.cgi?id=123139

問題としてはApache POIで作成したXLSXをLibreOffice Calcに読み込ませると水平アラインメントが無視されてしまうという現象である。

サンプルコード

このバグのデモコードは簡単だ。

import java.io.*;
import java.nio.file.*;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.*;

public class TestMain {
  public static void main(String[] args) throws Exception {

    try (SXSSFWorkbook workbook = new SXSSFWorkbook()) {

      CellStyle center = workbook.createCellStyle();
      center.setAlignment(HorizontalAlignment.CENTER);

      CellStyle left = workbook.createCellStyle();
      left.setAlignment(HorizontalAlignment.LEFT);

      Sheet sheet = workbook.createSheet("foobar");

      Row row0 = sheet.createRow((short) 0);
      Cell cell0 = row0.createCell((short) 0);
      cell0.setCellStyle(center);
      cell0.setCellValue("foo");

      Row row1 = sheet.createRow((short) 1);
      Cell cell1 = row1.createCell((short) 0);
      cell1.setCellStyle(left);
      cell1.setCellValue("bar");

      try (OutputStream out = Files.newOutputStream(Paths.get("out.xlsx"))) {
        workbook.write(out);
      }
    }
  }
}

こいつをLibreOffice 5.1.6.2 で表示させてみると以下になる。

一行目はセンタリングだが、二行目は左詰めにならないといけない。

生成されるXML

生成されるxlsxファイルというのは、主にxmlファイルからなるzipファイルなので、これを解凍すれば中身が見える。不要な部分は省略してある。

つまり、fooはcenter、barはleftになっているはずだが、表示上はそうならない。

xl/styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
 .....
   <cellXfs count="3">
      <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0" />
      <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0">
         <alignment horizontal="center" />
      </xf>
      <xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0">
         <alignment horizontal="left" />
      </xf>
   </cellXfs>
</styleSheet>

xl/worksheets/sheet1.xml

<?xml version="1.0" encoding="UTF-8"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
....
   <sheetData>
      <row r="1">
         <c r="A1" s="1" t="inlineStr">
            <is>
               <t>foo</t>
            </is>
         </c>
      </row>
      <row r="2">
         <c r="A2" s="2" t="inlineStr">
            <is>
               <t>bar</t>
            </is>
         </c>
      </row>
   </sheetData>
....
</worksheet>

対応方法

これはどうしようも無い。結局のところ、水平アライメントを指定しない方法しかないようだ。つまり、文字列は左づめ、数値は右づめになる。

import java.io.*;
import java.nio.file.*;

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.*;

public class TestMain {
  public static void main(String[] args) throws Exception {

    try (SXSSFWorkbook workbook = new SXSSFWorkbook()) {

      CellStyle center = workbook.createCellStyle();
      Sheet sheet = workbook.createSheet("foobar");

      Row row0 = sheet.createRow((short) 0);
      Cell cell0 = row0.createCell((short) 0);
      cell0.setCellStyle(center);
      cell0.setCellValue("foo");

      Row row1 = sheet.createRow((short) 1);
      Cell cell1 = row1.createCell((short) 0);
      cell1.setCellStyle(center);
      cell1.setCellValue(123.456);

      try (OutputStream out = Files.newOutputStream(Paths.get("out.xlsx"))) {
        workbook.write(out);
      }
    }
  }
}

xl/styles.xml

<?xml version="1.0" encoding="UTF-8"?>
<sst xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" count="0" uniqueCount="0" />

xl/worksheets/sheet1.xml

<?xml version="1.0" encoding="UTF-8"?>
<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">
   <dimension ref="A1" />
   <sheetViews>
      <sheetView workbookViewId="0" tabSelected="true" />
   </sheetViews>
   <sheetFormatPr defaultRowHeight="15.0" />
   <sheetData>
      <row r="1">
         <c r="A1" s="1" t="inlineStr">
            <is>
               <t>foo</t>
            </is>
         </c>
      </row>
      <row r="2">
         <c r="A2" s="1" t="n">
            <v>123.456</v>
         </c>
      </row>
   </sheetData>
   <pageMargins bottom="0.75" footer="0.3" header="0.3" left="0.7" right="0.7" top="0.75" />
</worksheet>