萩萩日記

世界に5人くらい存在するかもしれない僕のファンとドッペルゲンガーに送る日記

Spreadsheet::WriteExcel

PerlにはSpreadsheet::WriteExcelという素敵なモジュールがあって、リンク先にサンプルがあるようにエクセルなファイルをカンタンにグリグリと書き出せるのだけど、Perl 5.8以前で日本語を書き出そうとするとちょいと問題があって、基本的にはSpreadsheet::WriteExcel = Perl で Excel 出力にある方法でUTF-16な文字列を渡すことでうまく行くはずが、ひとつ問題があって、セルの結合をすると文字化けしてしまうのである。ガガーン。

なんでかなーってソースを見てみると、このモジュール、Perl 5.8以降ではwriteを呼ぶと文字列の種類によって内部的にwrite_unicodeが呼ばれる作りになっていて、Perl 5.8以前で使う際には明示的にwrite_unicode呼ぶ必要があるんだけど、セルを結合するときに使うメソッドmerge_rangeの中では、writeが呼ばれている。つまり、UNICODEの文字列を渡したいからといって明示的にwrite_unicodeする方法がない。困ったなーと思って適当なパッチを書いて作者に送ってみた。ちなみにモジュールの対象バージョンは2.14。

--- Spreadsheet/WriteExcel/Worksheet.pm.org    2005-05-09 10:14:00.000000000 +0900
+++ Spreadsheet/WriteExcel/Worksheet.pm        2005-09-16 17:06:56.000000000 +0900
@@ -3349,15 +3349,20 @@
     if ($_[0] =~ /^\D/) {
         @_ = $self->_substitute_cellref(@_);
     }
-    croak "Incorrect number of arguments" if @_ != 6;
-    croak "Final argument must be a format object" unless ref $_[5];
+    croak "Incorrect number of arguments" if @_ != 6 && @_ !=7;
+    if (@_ == 6) {
+        croak "Final argument must be a format object" unless ref $_[5];
+    } else {
+        croak "6th argument must be a format object" unless ref $_[5];
+    }

-    my $rwFirst  = $_[0];
-    my $colFirst = $_[1];
-    my $rwLast   = $_[2];
-    my $colLast  = $_[3];
-    my $string   = $_[4];
-    my $format   = $_[5];
+    my $rwFirst    = $_[0];
+    my $colFirst   = $_[1];
+    my $rwLast     = $_[2];
+    my $colLast    = $_[3];
+    my $string     = $_[4];
+    my $format     = $_[5];
+    my $is_unicode = $_[6];


     # Temp code to prevent merged formats in non-merged cells.
@@ -3380,7 +3385,11 @@
     ($colFirst, $colLast) = ($colLast, $colFirst) if $colFirst > $colLast;

     # Write the first cell
-    $self->write($rwFirst, $colFirst, $string, $format);
+    if($is_unicode){
+        $self->write_unicode($rwFirst, $colFirst, $string, $format);
+    }else{
+        $self->write($rwFirst, $colFirst, $string, $format);
+    }
     
     # Pad out the rest of the area with formatted blank cells.
     for my $row ($rwFirst .. $rwLast) {

採用されるといいですね。でもmerge_rangeってメソッドひとつでなんとかするより、merge_range_unicodeってメソッドを用意する方が良かったのかもしれません。