YAPC::Asia Tokyo2010に参加して

スクリプト言語Perlのカンファレンス(会議)に15,16日と行ってきました。

生で生みの親であるLallyWall氏を拝見できたこと。
なぜか忍者がいたこと。
Mac bookとiPadだらけだったこと。
色々勉強になったこと。
ほったらかしのモジュール(HTML::AAとImage::Magick::Thumbnail::Simple)をどうにかしないといけないと思ったこと。
PostScript::Simpleを拡張して業務で使っているので何とかしてみたいこと。
PostScriptが笑いのタネにされていたこと。

ことこと。

TCPDF5.8.034のフォント指定の不具合

PHPでPDFを扱うにはまずTCPDF一択なわけですが、バージョンをよく見ると4.6.026という3年ぐらい前のソースなので新しくしようと思い立ちました。
動いていたので特に問題はないのですが、これだけ古いとどうかなという感覚的なものです。
最新版の5.8.034を入れて、動作を確認したところ、特に問題なし。
次のステップとして自前で用意したIPAフォントを指定したところ、処理が戻ってこないままタイムアウトになりました。
古い4.6に戻すと正常に出力されます。
新しい5.8にするとタイムアウトします。
だめだこりゃ。
別のバージョンで試すしかない。
どのミラーも最新版に更新されていて、最新版しか置いてない。
あちこち探し回った結果、発見したのが
http://ftp.heanet.ie/disk1/sourceforge/t/project/tc/tcpdf/OldFiles/
これで1つづつ遡りながら、動くバージョンを選ぶ作業に移ります。

時間をかけてマイナーバージョンごとに動作を確認して言ったのですが、どうも5.2から実装されたらしいサブセット埋め込みで不具合が発生している模様。
解説サイトでは5.4で解消されたと書いてあるけれど、検証していないみたいで実際に走らせるとタイムアウトになってしまう。
時間を大きくとれば言いなんて書いてあるけど、レスポンスが分単位なんて実際問題ありえないし、これは実装の不具合としか言いようがない。
結局、5.2の前のバージョン、5.1.002を導入してシステムを構築することにしました。

ActionScript3で表示を画像に変換するPNGEncについて

ActionScript3でビットマップを保存する方法があるのですが、オブジェクトをビットマップに変換(キャッシュ)したものをサーバに送信して画像として1つのファイルに保存するわけです。もちろんJavaScriptに送信すれば画像としても表示できますが、クライアントのみで完結させても面白くないので省きます。
さて、ビットマップを送信できる状態にするためのライブラリとして、PNGEncというパッケージに行き着くのですが、公開が古くて何箇所か修正が必要だという解説ページはあるのですが、だったらなぜ修正版を掲載しないのかという素朴な疑問に行き着くわけで、修正版を載せておくことにします。
以下のコードを「PNGEnc.as」としてテキストで保存します。インポートや使い方については、ここでは省略します。

package {

import flash.geom.*;
import flash.display.*;
import flash.utils.*;

public class PNGEnc {

    public static function encode(img:BitmapData):ByteArray {
        // Create output byte array
        var png:ByteArray = new ByteArray();
        // Write PNG signature
        png.writeUnsignedInt(0x89504e47);
        png.writeUnsignedInt(0x0D0A1A0A);
        // Build IHDR chunk
        var IHDR:ByteArray = new ByteArray();
        IHDR.writeInt(img.width);
        IHDR.writeInt(img.height);
        IHDR.writeUnsignedInt(0x08060000); // 32bit RGBA
        IHDR.writeByte(0);
        writeChunk(png,0x49484452,IHDR);
        // Build IDAT chunk
        var IDAT:ByteArray= new ByteArray();
        for(var i:int=0;i < img.height;i++) {
            // no filter
            IDAT.writeByte(0);
            var p:uint;
            if ( !img.transparent ) {
                for(var j:int=0;j < img.width;j++) {
                    p = img.getPixel(j,i);
                    IDAT.writeUnsignedInt(
                        uint(((p&0xFFFFFF) << 8)|0xFF));
                }
            } else {
                for(var j2:int=0;j2 < img.width;j2++) {
                    p = img.getPixel32(j2,i);
                    IDAT.writeUnsignedInt(
                        uint(((p&0xFFFFFF) << 8)|((p >>> 24))));
                }
            }
        }
        IDAT.compress();
        writeChunk(png,0x49444154,IDAT);
        // Build IEND chunk
        writeChunk(png,0x49454E44,null);
        // return PNG
        return png;
    }

    private static var crcTable:Array;
    private static var crcTableComputed:Boolean = false;

    private static function writeChunk(png:ByteArray, 
            type:uint, data:ByteArray) {
        if (!crcTableComputed) {
            crcTableComputed = true;
            crcTable = [];
            for (var n:uint = 0; n < 256; n++) {
                var c2:uint = n;
                for (var k:uint = 0; k < 8; k++) {
                    if (c2 & 1) {
                        c2 = uint(uint(0xedb88320) ^ 
                            uint(c2 >>> 1));
                    } else {
                        c2 = uint(c2 >>> 1);
                    }
                }
                crcTable[n] = c2;
            }
        }
        var len:uint = 0;
        if (data != null) {
            len = data.length;
        }
        png.writeUnsignedInt(len);
        var p:uint = png.position;
        png.writeUnsignedInt(type);
        if ( data != null ) {
            png.writeBytes(data);
        }
        var e:uint = png.position;
        png.position = p;
        var c:uint = 0xffffffff;
        for (var i:int = 0; i < (e-p); i++) {
            c = uint(crcTable[
                (c ^ png.readUnsignedByte()) & 
                uint(0xff)] ^ uint(c >>> 8));
        }
        c = uint(c^uint(0xffffffff));
        png.position = e;
        png.writeUnsignedInt(c);
    }
}

}

Windows7にもてあそばれる

一通り入れた後に、MySQLを入れ忘れたのに気がついて入れてみたところ、PHPからアクセスできない。実際にはphpMyAdmin3からアクセスできない。エラーメッセージも表示されず、1分ぐらい待たされて方真っ白な画面になってしまう。
コマンドからは問題なくアクセスできるものの、mod_phpからアクセスできない。
このときのPHPは5.3.3、MySQLは5.1.50(64bit)でした。
httpd.confやphp.iniやmy.iniをいじっては見たものの改善せず。
ここで不貞寝しました。

そして翌日。
まず最初にMySQLをノーマルからエッセンシャルに入れ替えたり、32bitにしたりバージョンを入れ替えたりしたけれどうまくいかない。PHPを入れる際にVCのライブラリを更新したのでそれに問題があるのではと考え、仕方がなくWindows7を入れなおすことに。

とりあえずWindows7を上書きでインストールしなおしてみたところ、前のシステムが複製されて残っていて、とっても気持ちが悪い状態に。仕方がないので改めてパーティションをフォーマットしなおしてから、改めて入れなおすことに。

まず最初にMySQL5.1.50(64bit)を入れて、次にApache2.2.16、そしてVCを更新せずにPHP5.3.3を入れなおしてみたところ、やはり現象は同じ。
ここでふとPHPのインストーラーにVCの対象を選択する新しい項目があったのを思い出して、前のバージョンで試してみようと思い立ちました。
PHP5.2.14を入れなおしたところ、zlib.dllがないといわれるのでネットから拾ってきて放り込んだところ、難なく起動。

問題だったのはPHP5.3.3.
データベースとの連携が高いはずのPHPで、このような障害が発生しました。
何でもかんでも組み込み関数にしてしまったPHPの成れの果てを垣間見た気がしました。

結局それ以外では、ActivePerlを64bitにするとMinGWと連携できないようなので、32bitで入れなおしたぐらいで、特に大きな問題もなく開発環境は整いました。
あとはデータを移していくだけです。これもかなり面倒なのですが。

PerlのJSONで文字化けを回避する

CGIでフォームから送信された文字列をJSONにエンコードしていたときの話です。
キーは英数字、値をUTF-8の文字列にした場合、エンコードした結果をSTDOUTに出力した場合は問題ないのに、DBに格納すると化けるという不思議な現象が発生しました。
「Perl JSON 文字化け」なので検索すると色々出てきますが、どれも参考にならず。「to_json()」も「encode_json()」もダメで、適当にいじっていたら「JSON->new->latin1->encode()」が正解でした。
とはいえ、なぜそうなのかが不明なので、環境によっては動かないかもしれません。
とりあえず、文字コード周りはエンコードを回避するためのパラメータを用意しておいて欲しいものです。
あと、JSONはpureperlなので、スクリプトをアップする際には使用したモジュールをコピーしておくと、動作が保障されると思われます。
でめたしでめたし。

Perlモジュールのバージョン違いに填まる

自作CMSでJSONを使うことにしました。
最近あまり使っていませんが理屈はわかっています。JavaScriptではevalです。
ではなくて、PerlではJSONモジュールを利用すれば問題ない、はずでした。
結論から言うと、ローカルのテスト環境と、coreserverではJSONモジュールのバージョンが違うためにサブルーチンが見つからないとかいわれて怒られていたのでした。
ローカルの環境では2.21、coreserverは3年前の1.14で、to_jsonやfrom_jsonがありません。幸いなことにJSONhaPurePerlなのでコピーするだけでも動きます。
ということで、ライブラリパスを追加するのですが、pushではダメです。
先に読んでくれないと困るのでunshiftします。

BEGIN{ unshift @INC, (./'; }


これで問題なく動くようになりました。
ちなみに、unshiftではなくてpushを使ってしまっていて、なぜ新しいバージョンを読まないのだろうと数時間悩んだなんて恥ずかしくていえません。
なにはともあれでめたしでめたし。

ActionScriptの記法(コーディングスタイル)に馴染めない

ActoinScriptをいじり始めて半年ぐらいになりますが、まだまだ記法に馴染めません。
特にイベントをいちいち追加しなければならないところなどもどかしく、ボタンなんてクリックするためにあるんだからクリックイベントのメソッドぐらい用意して置けよなんて思うわけです。

btn.addEventListener( MouseEvent.CLICK, btn_onClick );
function btn_onClick( e:MouseEvent ):void{
  trace( e.target.name );
}


一般的なマニュアルではこうなっていますが、いちいち関数名を考える身にもなってくれと、どの道、このイベント以外からは参照しない処理だと、いちいち分けないといけない意味がわからないのです。
つまりこういうこと。

btn.addEventListener(
  MouseEvent.CLICK,
  function( e:MouseEvent ):void{ trace( e.target.name ); }
);


関数名の重複を怖がる必要はなくなりました。
JavaScriptが頭にあると、スクリプトは外部ファイルに置くべきという理屈から、いちいち関数を用意していたと思うのですが、パブリッシュが前提のActionScriptでは不要の作業。
よく考えればどうってこと無いのですが、解説サイトによっては「関数名の重複を避けるためにパッケージ化」しますなどと、むだな手間を掛けているのを見るとうんざり。
無名関数をもっと使いこなしたほうがいい気がします。
それに、処理速度とか、オブジェクト指向だとかそっちのけで、Perl使いとしてはこっちのほうが見やすいのです。
でめたしでめたし。

CentOS5.5でyum一括アップデートしたらPerlがCompress::Zlibでコケるようになった

昨日、yum一括更新を行ったのですが、その後でどうもクローラが正常に機能しなくなりました。
ログを見てみると、Compress::Zlibで以下のようなエラーを吐いていることを確認。(コピペでなくて入力なのでタイポあるかも)

dualvar is only available with the XS version of  Scalar::Util at /usr/perl5/lib/site_perl/5.8.8/Compress/Zlib.pm line 9


つまりはScalar::Utilのせいで動かないらしいです。
そこでCPANからScalar::UtilをiinstallしようとしてもNOT OKになってしまうので、とりあえず

force install Scalar::Utilde


でしのぐことに。
おそるおそるクローラを起動するとエラーを吐かずに正常に機能するようになりました。
でめたしでめたし。

Perlでgzファイルを解凍する

Perlでzipファイルを扱うには「Archive::Zip」というモジュールが便利なのですが、gzファイルも同じ感覚で扱おうとしたら以下のようなエラーが出ました。

format error: can't find EOCD signature


エラー処理をしないとさらに5行ぐらい表示されます。
どうもgzファイルには対応していないらしいです。
調べてみると「Compress::Zlib」というモジュールが使えるらしいので弄ってみました。
そもそもgzという圧縮形式はファイルをアーカイブする機能がないので、圧縮ファイルと解凍ファイルは1対1の関係です。サンプルではバッファの展開しか載っていないので、とりあえずその方法で使いました。

$dest = Compress::Zlib::memGunzip($buffer);


でめたしでめたし。

PHPのセーフモードに悶え苦しむ

PHPの開発はなんだかんだで自前鯖や自宅鯖だったので、モジュールでフルの状態で動かしていたわけです。
プリントライはcoreserverに乗せたのですが、ここはセーフモードなのです。
あらかじめわかってはいましたがここまで時間をとられるとは思いませんでした。
まぁ、なんだかんだでCGIモードでなければならない部分はAjaxで逃げて事なきを得ています。
とりあえず行き詰った部分を。

・execが使えない(セーフモードというよりは鯖の設定か)
・mail関数で第5引数があるとワーニング8セーフモードのせい)
・ディレクトリのオーナーが違うと書き込めない(セーフモードのせい・Apacheとphpのユーザ)