• このエントリーをはてなブックマークに追加

ss-1360624152

はじめに

わけあって ImageMagickconvert コマンドを使って表のようなものを画像として動的に生成することになったのですが,そのことを考えるだけでもメンドイのに,その配置仕様が変更されることを考えると,もう気が滅入ってしまいます.

そんなことを思っていたら,配置を構成するパーツ(罫線とか矩形とか文字とか)の配置情報を直感的に指定できたり,動的に追加できたり,構成単位をグループ化して使い回せたりしてそこから convert コマンドを発行するようなツールが欲しくなったので,少々現実逃避した中で, Image::Layout というモジュールを書いてみました.

まぁ中身は convert コマンドのラッパですよ.

目次

なんだか長くなってしまったので,目次です:

  • はじめに
  • 目次
  • コンセプト的な
  • 基本
  • Line: 直線を配置する
  • Rect: 矩形を配置する
  • Text: 文字を配置する
  • Image: 画像を配置する
  • 独自に定義する
  • 独自に定義する その 2
  • 独自に定義する その 3
  • 実際のコマンドを表示する
  • コマンドのパスを変更する
  • おわりに

コンセプト的な

Image::Layout は,「配置」オブジェクトを追加していって,それらの配置情報を基に画像として書き出す感じになっています.

基本的な「配置」には,次のものがあり:

  • Line
  • Rect
  • Text
  • Image

それぞれが,相応に描画するための convert コマンドオプションと対応づけられています.例えば,Text の場合,-draw 'text ...' に対応づけられている,という感じ.

で,最終的にそれらのコマンドオプションをひとつにまとめて convert コマンドを発行することで画像が書き出されます.(外部コマンド使っているのに「書き出す」という表現はどうなんでしょ)

基本

コンストラクタ

生成したい画像の幅や高さ,背景色なんかを指定します:

use Image::Layout;
 
my $image = Image::Layout->new(
    width   => 500,
    height  => 250,
    bgcolor => '#f0f0f0',
);

画像を生成して保存する

save メソッドを呼びます:

$image->save( file => 'sample.jpg' );

この場合,こんな画像ができます:

ss-1360623090

「配置」を追加する

「配置」の追加は,add_layout メソッドによって,種別や配置する位置等を指定することで行います.

Line: 直線を配置する

種別に Line を指定します.pos が開始位置,to が終端位置となります:

$image->add_layout(
    type => 'Line',
    pos  => [0, 0],
    to   => [500, 250],
);
ss-1360623100

また,終端位置を,開始位置から相対的に指定する rel_to を使うこともできます:

$image->add_layout(
    type   => 'Line',
    pos    => [0, 125],
    rel_to => [500, 0],
    color  => '#ff0000',
);
ss-1360623110

あと,色とか線の幅とか:

$image->add_layout(
    type  => 'Line',
    pos   => ['50%w', '50%h'],
    to    => ['100%w', 0],
    color => '#0088ff',
    size  => 3,
);
ss-1360623117

ちなみに,100%w は「画像幅に対して 100%」,50%h は「画像高さに対して 50%」という意味で,これを,位置やサイズの値として用いることもできます.

Rect: 矩形を配置する

種別に Rect を指定します.あわせて,開始位置 pos,幅 width,高さ height,背景色 bgcolor なんかを指定します:

$image->add_layout(
    type   => 'Rect',
    pos    => [20, 20],
    width  => 50,
    height => 50,
    bgcolor => '#ff3489',
);
ss-1360623133

枠線の追加には,その幅と色,それぞれ border_widthborder_color,を指定します:

$image->add_layout(
    type   => 'Rect',
    pos    => ['50%w', '50%h'],
    width  => '33%w',
    height => '33%h',
    bgcolor => '#abcdef',
    border_width => 2,
    border_color => '#ff3489',
);
ss-1360623138

Text: 文字を配置する

種別に Text を指定します.あわせて,位置 pos,フォント font,内容 content,サイズ size なんかを指定します.

$image->add_layout(
    type    => 'Text',
    pos     => [20, 20],
    font    => "$basedir/resources/mplus-1c-thin.ttf",
    content => 'こんにちは!',
    size    => 50,
);
ss-1360623142

color や,枠線,幅 border_width と色 border_color,も指定できます:

$image->add_layout(
    type         => 'Text',
    pos          => [20, '66%h'],
    font         => "$basedir/resources/みかちゃん.ttf",
    content      => '色と線をつけてみる',
    color        => '#3489ff',
    size         => 30,
    border_color => '#ff3489',
    border_width => 1,
);
ss-1360623146

また,「起点」を変更することもできます.例えば,右下を「起点」とした場合,pos => [20, 20] は「右下から (20, 20)」といった意味を持つようになります.水平方向の起点として h_origin,垂直方向の起点として v_origin をそれぞれ指定することになります(デフォルトは左上):

# h_origin: left / center / right
# v_origin: top / middle / bottom
 
$image->add_layout(
    type     => 'Text',
    pos      => [0, 0],
    font     => "$basedir/resources/Roboto-Regular.ttf",
    content  => 'center-origined',
    size     => 30,
    h_origin => 'center',
    v_origin => 'middle',
);
$image->add_layout(
    type     => 'Text',
    pos      => [20, 20],
    font     => "$basedir/resources/Roboto-Regular.ttf",
    content  => 'bottom-rigtht-origined',
    size     => 30,
    h_origin => 'right',
    v_origin => 'bottom',
);
ss-1360623149

Image: 画像を配置する

種別 Image を指定します.あわせて,位置 pos,画像ファイル file,幅 width,高さ height なんかも:

$image->add_layout(
    type   => 'Image',
    pos    => [0, 0],
    file   => "$basedir/resources/miniika.jpg",
    width  => 200,
    height => 200,
);

デフォルトでは,対象画像の「長辺」側の値を基準として,アスペクト比が保持された状態で配置されます:

ss-1360623152

アスペクト比を無視する場合には,keep_aspect => 0 を指定します:

$image->add_layout(
    type        => 'Image',
    pos         => [250, 0],
    file        => "$basedir/resources/miniika.jpg",
    width       => 200,
    height      => 200,
    keep_aspect => 0,
);
ss-1360623156

また,file の代わりに,画像の存在する URL を指定するための url を使うこともできます.

種別 Text と同様,「起点」にも対応しています:

$image->add_layout(
    type     => 'Image',
    url      => 'http://upload.wikimedia.org/wikipedia/commons/thumb/3/3a/Cat03.jpg/600px-Cat03.jpg',
    pos      => [10, 10],
    width    => 100,
    height   => 100,
    h_origin => 'right',
    v_origin => 'bottom',
);
ss-1360623161

独自に定義する

「種別」を独自に定義して,これを利用することもできます.

定義には Image::Layout::Base を継承して init メソッドを定義することで行います.

次のコードは,「(中略)3つの矩形を配置する」種別 MyTool::MyLayout1 を定義しています:

package MyTool::MyLayout1;
use 5.10.0;
use warnings;
use utf8;
use parent 'Image::Layout::Base';
 
sub init {
    my ($self, %params) = @_;
    $self->add_layout(
        type    => 'Rect',
        pos     => [20, 20],
        width   => 75,
        height  => 50,
        bgcolor => $self->content,
    );
    $self->add_layout(
        type    => 'Rect',
        rel_pos => [0, 0],
        width   => 2,
        height  => 2,
        bgcolor => '#ff0000',
    );
    $self->add_layout(
        type    => 'Rect',
        rel_pos => [50, 50],
        width   => 100,
        height  => 50,
        bgcolor => '#ffabcd',
    );
}
 
1;

独自定義した「配置」の指定には,Plack::Builderenable などと同様,先頭に + を付加してフルパッケージ名で行います:

my $image = Image::Layout->new(...);
$image->add_layout(
    type    => '+MyTool::MyLayout1',
    pos     => [10, 10],
    content => '#ff3489',
);

位置の指定として pos の代わりに rel_pos を使うことで,その配置の「相対位置」を指定することもできます.

先の例の場合,pos => [10, 10]MyTool::MyLayout1 を配置しているため,その 3つの矩形の位置は,次のようになります:

  • 1 つ目の矩形: pos 指定なので (20, 20)
  • 2 つ目の矩形: rel_pos 指定なので,(10, 10) からの相対位置 (0, 0),つまり (10, 10)
  • 3 つ目の矩形: rel_pos 指定なので,(10, 10) からの相対位置 (50, 50),つまり (60, 60)
ss-1360623164
$image->add_layout(
    type    => '+MyTool::MyLayout1',
    pos     => ['25%w', '50%h'],
    content => '#3489ff',
);

今度は pos => ['25%w', '50%h'] つまり [125, 125] での指定.この場合,MyTool::MyLayout の 3つの矩形の位置は,次のようになります:

  • 1 つ目の矩形: pos 指定なので (20, 20)
  • 2 つ目の矩形: rel_pos 指定なので,(125, 125) からの相対位置 (0, 0),つまり (125, 125)
  • 3 つ目の矩形: rel_pos 指定なので,(125, 125) からの相対位置 (50, 50),つまり (175, 175)
ss-1360623166

独自に定義する その 2

add_layout で独自定義の「配置」を指定する際に,さらに独自なパラメータを指定できるようにしたい場合,バリデーションルールを拡張する必要があります.

バリデーションルールの拡張には,extra_validation_rule メソッドを定義して,ハッシュを返すようにしてやります.バリデーションルールの定義自体は,Data::Validator を参照してください:

package MyTool::MyLayout2;
use 5.10.0;
use warnings;
use utf8;
use parent 'Image::Layout::Base';
use MouseX::Types::Mouse qw/Str/;
 
sub extra_validation_rule {
    return (
        foobar => { isa => Str, default => 'foobar' },
        font   => { isa => Str },
    );
}
 
sub init {
    my ($self, %params) = @_;
 
    $self->add_layout(
        type    => '+MyTool::MyLayout1',
        rel_pos => [0, 0],
        content => '#ccffcc',
    );
 
    $self->add_layout(
        type    => 'Text',
        rel_pos => [10, 10],
        font    => $params{font},
        content => $params{foobar},
        size    => 20,
    );
}
 
1;

foobarfont が拡張したパラメータとなります.

また,この例では,MyTool::MyLayout1 と Text を配置するよう定義しています.こんな感じで,「独自定義した『配置』を配置するような『配置』を定義する」といった入れ子的な構造も扱えます,たぶん.

$image->add_layout(
    type   => '+MyTool::MyLayout2',
    pos    => ['50%w', 0],
    foobar => 'ほげほげふがふが',
    font   => "$basedir/resources/mplus-1c-thin.ttf",
);
ss-1360623170
$image->add_layout(
    type   => '+MyTool::MyLayout2',
    pos    => [0, '50%h'],
    font   => "$basedir/resources/Roboto-Regular.ttf",
);
ss-1360623184

独自に定義する その 3

compose メソッドを定義して,「convert コマンドオプションな文字列」を要素とするリストを返すようにしてやります:

package MyTool::MyLayout3;
use 5.10.0;
use warnings;
use utf8;
use parent 'Image::Layout::Base';
 
sub compose {
    my $self = shift;
    my $file = $self->content;
    my @cmd;
 
    push @cmd, << "    ...";
        $file
        -geometry 250x250+10+10
        -composite
    ...
 
    return @cmd;
}
 
1;

配置のしかたは同様です:

$image->add_layout(
    type    => '+MyTool::MyLayout3',
    pos     => [50, 50],
    content => "$basedir/resources/miniika.jpg",
);
ss-1360623190

実際のコマンドを表示する

save メソッドに show_cmd => 1 を指定することで,最終的に発行される convert コマンドを表示できます:

$image->save( file => ..., show_cmd => 1 );
ss-1360624152

こんなの自分で書きたくないですよねw

コマンドのパスを変更する

$Image::Layout::CONVERT を上書きしてください:

$Image::Layout::CONVERT = '/usr/bin/convert';

なお,デフォルトは次のとおりです:

/usr/bin/env convert

おわりに

以上,ImageMagick の convert コマンドで画像を生成することになったけど明らかにメンドそうなので,コマンドオプションに対応づけた「配置」を組み合わせることで長ったらしい convert コマンドを構成・発行するためのモジュール Image::Layout を書いてみたのとその紹介でした.

さて,現実に戻って目的の画像生成のスクリプトを準備せねば.