ActivePerl 5.10でImageMagickが正常に動作しない不具合の対処
Windows+ActivePerl 5.10でImageMagickをインストールしたときにImageMagickが正常に動作しない不具合の対処方法をまとめました。
このエントリーは、「Movable Type 5で「サーバーに Image::Magickか、Image::Magickの動作に必要な他のモジュールがインストールされていません。」となる事象について」から、さらに追跡調査したものです。
実験環境は次の通りです。
- Windows XP Professional Service Pack 3(2台)
- ActivePerl 5.10.1 Build 1006
- ImageMagick 6.5.5-5 Q8/ImageMagick 6.5.6-10 Q8/ImageMagick 6.5.9-1 Q8(5.10.1 Build 1006に対応する最初のバージョン・途中のバージョン・最後のバージョンで実験)
- Movable Type 5.02
PerlMagickは、前回はppmコマンドでインストールしましたが、今回はImageMagickに同梱されているものをそのまま使用しました。
ImageMagickの過去バージョンは以下のリンクからダウンロードできます。
1.問題点
ImageMagickをインストールしているにもかかわらず、次のようにダッシュボードに「Image::Magickが設定されていません。」というメッセージが表示されます。

この状態でアイテムをアップロードしてもサムネイルが作成されず、真っ黒な表示になります。

サムネイルが作成されていれば、5項のキャプチャ画像のように表示されます。
2.問題の解析
詳細は割愛しますが、サムネイルが作成されない原因は、前エントリーでも記した通り、プログラム内の「require Image::Magick」というところでエラーになっているためです。
今回は、エラーの詳細を知るために、次のサンプルプログラムをコマンドプロンプトから実行しました。「1.png」はサンプルプログラムと同じフォルダに配置している、任意の画像ファイルです。
#!/usr/bin/perl
eval { require Image::Magick };
if (my $err = $@) {
die $err;
}
my $file = '1.png';
(my $ext = $file) =~ s/.*\.//;
my %arg = (magick => lc($ext));
my $magick = Image::Magick->new(%arg);
$magick->Read($file);
my ($width, $height) = $magick->Get('width', 'height');
print $width."x".$height;
NGを確認するのであれば、最初の5行だけあればいいです。
#!/usr/bin/perl
eval { require Image::Magick };
if (my $err = $@) {
die $err;
}
ImageMagick 6.5.7-9 Q8のインストール直後にサンプルプログラムを実行すると、次のように「CORE_RL_magick.dllが見つからなかったため、このアプリケーションを開始できませんでした。アプリケーションをインストールし直すとこの問題は解決される場合があります。」というエラーが表示されました。
実行後、コマンドプロンプトには次のように「指定されたモジュールが見つかりません。」というエラーが表示されました。
Can't load 'C:/usr/site/lib/auto/Image/Magick/Magick.dll' for module Image::Magick: load_file:指定されたモジュールが見つかりません。 at C:/usr/lib/DynaLoader.pm line 201.
at test.pl line 3
Compilation failed in require at test.pl line 3.
&Image::Magick::constant not defined. The required ImageMagick libraries are not installed or not installed properly.
END failed--call queue aborted at test.pl line 5.
別のPCで、ImageMagick 6.5.7-9 Q8インストール直後にサンプルプログラムを実行すると、こちらは「再帰が深すぎます。スタックがオーバーフローしました。」というエラーが表示されました。
Can't load 'C:/usr/site/lib/auto/Image/Magick/Magick.dll' for module Image::Magick: load_file:再帰が深すぎます。スタックがオーバーフローしました。 at C:/usr/lib/DynaLoader.pm line 201.
at test2.pl line 3
Compilation failed in require at test2.pl line 3.
&Image::Magick::constant not defined. The required ImageMagick libraries are not installed or not installed properly.
END failed--call queue aborted at test2.pl line 5.
なお、サンプルプログラムではなく、コマンドプロンプトからImageMagickの動作を確認する方法は以下です。すべて正常に動作すれば、imdisplayコマンド実行後にImageMagickのロゴが表示されます。ただし、Perlのrequire文は実行しないので、これが正常に実行されても、Perlを使っているツールの不具合が解消されているとは限らないようです。
ImageMagick - Install from Binary Distribution
C:¥>convert logo: logo.gif
C:¥>identify logo.gif
C:¥>imdisplay logo.gif
4.原因
「CORE_RL_magick.dll」はImageMagickのインストールフォルダ直下にあるライブラリなのですが、このライブラリが正常に参照できていないのが真の原因と推測されます。コマンドプロンプトに表示されたメッセージは、「CORE_RL_magick.dll」がみつからないことによる、二次的なエラーと思われます。
「CORE_RL_magick.dll」を正しく参照できるようにするには、環境変数が設定されていればよいと思います。ImageMagickのインストール直後は、環境変数「Path」にImageMagickのインストールフォルダが設定されていますが、インストール直後はPathが効いておらず、「CORE_RL_magick.dll」が参照できないようです。
なお、ImageMagickのインストールとアンインストールを何回か繰り返して試したところ、「CORE_RL_magick.dllが見つからなかったため~」というダイアログは表示されず、コマンドプロンプト上のエラーメッセージのみが表示されるケースもありました。
5.対処1
ImageMagick 6.5.6-10 Q8/ImageMagick 6.5.9-1 Q8については、WIndowsを再起動することで、コマンドプロンプトから利用可能になり、Movable Typeでもサムネイルが表示されました。ImageMagick 6.5.5-5 Q8では再起動してもNGのままでした。

また、ImageMagickのインストールとアンインストールを何回か繰り返して試したところ、再起動せずにコマンドプロンプトからは動作OKとなり、Movable Typeでは再起動しないとサムネイルが表示されないというケースがありました。
6.対処2
ImageMagick 6.5.6-10 Q8については、再起動を行う前に、「Movable Type 5で「サーバーに Image::Magickか、Image::Magickの動作に必要な他のモジュールがインストールされていません。」となる事象について」に記したとおり、環境変数MAGICK_HOMEを追加することで、次のようにコマンドプロンプトから動作可能になるケースがありました。
7.対処3
今回は実施していませんが、ネット上ではppmコマンドでPerlMagickをインストールすることで解決しているという情報があります。
8.まとめ
ActivePerl 5.10.1 build 1006であればImageMagick 6.5.9-1との組み合わせが良好と思われます。
現在の最新バージョンであるActivePerl 5.10.1 build 1007は試していませんが、ImageMagickが正常に動作しない場合、次の順番で対処を行ってみてください。
- ImageMagickのバージョンは、ImageMagickに同梱されているPerlMagickのバージョンをActivePerlのバージョンに合わせ、かつ新しいものを選択(参考:ImageMagick と Image::Magick(PerlMagick) のバージョン対応)
- ImageMagickインストール後にPC再起動
- MAGICK_HOMEの設定+PC再起動
- ImageMagickでPerlMagickを同時にインストールせずに、ppmでインストール+PC再起動
今回の実施環境では、解決できない事象には遭遇しませんでしたが、ActivePerlやImageMagickのバージョンによっては解決しない場合があるかもしれません。
最後に、今回の実施結果を簡単にまとめておきます。
| インストール直後 | 再起動 | MAGICK_HOME設定 | MAGICK_HOME設定後の再起動 | |
|---|---|---|---|---|
| 6.5.5 | NG | NG | NG | NG |
| 6.5.6 (1回目) | NG | - | MS-DOS OK/MT NG | MT OK ※再起動のみでOKとなった可能性あり |
| 6.5.6 (2回目) | MS-DOS OK/MT NG | MT OK | - | - |
| 6.5.9 | OK | - | - | - |
XML::Simpleを使ったXMLデータへのアクセス方法
XML::Simpleで取得したデータへのアクセス方法です。
XML::Simpleを使ったXML要素へのアクセス方法をネットで調べたところ、取得したデータをData::Dumperで出力している例がほとんどで、情報収集に苦労したので、各XML要素への具体的なアクセス方法を掲載しておきます。
といってすべてのケースが網羅できている訳ではありません。予めご了承ください。
1.サンプルXML
サンプルXMLです。これはExcelから出力するXML形式のスプレッドシートを想定しています。文字エンコーディングはUTF-8です。
<?xml version="1.0" encoding="UTF-8"?>
<Workbook>
<Worksheet>
<Table>
<Row>
<Cell><Data Type="String">番号</Data></Cell>
<Cell><Data Type="String">種別</Data></Cell>
<Cell><Data Type="String">ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">1-1</Data></Cell>
<Cell><Data Type="String">A</Data></Cell>
<Cell><Data Type="String">10ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">1-2</Data></Cell>
<Cell><Data Type="String">B</Data></Cell>
<Cell><Data Type="String">20ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">1-3</Data></Cell>
<Cell><Data Type="String">C</Data></Cell>
<Cell><Data Type="String">30ページ</Data></Cell>
</Row>
</Table>
</Worksheet>
<Worksheet>
<Table>
<Row>
<Cell><Data Type="String">番号</Data></Cell>
<Cell><Data Type="String">種別</Data></Cell>
<Cell><Data Type="String">ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">2-1</Data></Cell>
<Cell><Data Type="String">D</Data></Cell>
<Cell><Data Type="String">10ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">2-2</Data></Cell>
<Cell><Data Type="String">E</Data></Cell>
<Cell><Data Type="String">20ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">2-3</Data></Cell>
<Cell><Data Type="String">F</Data></Cell>
<Cell><Data Type="String">30ページ</Data></Cell>
</Row>
</Table>
</Worksheet>
<Worksheet>
<Table>
<Row>
<Cell><Data Type="String">番号</Data></Cell>
<Cell><Data Type="String">種別</Data></Cell>
<Cell><Data Type="String">ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">3-1</Data></Cell>
<Cell><Data Type="String">G</Data></Cell>
<Cell><Data Type="String">10ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">3-2</Data></Cell>
<Cell><Data Type="String">H</Data></Cell>
<Cell><Data Type="String">20ページ</Data></Cell>
</Row>
<Row>
<Cell><Data Type="String">3-3</Data></Cell>
<Cell><Data Type="String">I</Data></Cell>
<Cell><Data Type="String">30ページ</Data></Cell>
</Row>
</Table>
</Worksheet>
</Workbook>
2.Data::Dumperでの出力
1項のXMLデータのData::Dumperで出力を掲載します。3項以降で示すコードとの比較に利用してください。
$VAR1 = {
'Table' => {
'Row' => [
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => "\x{756a}\x{53f7}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{7a2e}\x{5225}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '1-1'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'A'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "10\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '1-2'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'B'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "20\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '1-3'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'C'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "30\x{30da}\x{30fc}\x{30b8}"
}
}
]
}
]
}
};
$VAR2 = {
'Table' => {
'Row' => [
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => "\x{756a}\x{53f7}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{7a2e}\x{5225}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '2-1'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'D'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "10\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '2-2'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'E'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "20\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '2-3'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'F'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "30\x{30da}\x{30fc}\x{30b8}"
}
}
]
}
]
}
};
$VAR3 = {
'Table' => {
'Row' => [
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => "\x{756a}\x{53f7}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{7a2e}\x{5225}"
}
},
{
'Data' => {
'Type' => 'String',
'content' => "\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '3-1'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'G'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "10\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '3-2'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'H'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "20\x{30da}\x{30fc}\x{30b8}"
}
}
]
},
{
'Cell' => [
{
'Data' => {
'Type' => 'String',
'content' => '3-3'
}
},
{
'Data' => {
'Type' => 'String',
'content' => 'I'
}
},
{
'Data' => {
'Type' => 'String',
'content' => "30\x{30da}\x{30fc}\x{30b8}"
}
}
]
}
]
}
};
3.サンプル
1項のXMLデータ(text.xml)を出力するサンプルコードです。説明のために、最下層のデータに一気にアクセスせず、順番に取得するようにしています。XMLファイルの取得方法など、より適切なコードがあればアドバイスお待ちしています。
#!/usr/bin/perl
use strict;
use utf8;
binmode STDOUT => ":utf8";
use XML::Simple;
open(FILE, "./test.xml");
undef $/;
my $xml = new XML::Simple();
my $xmldata = $xml->XMLin(<FILE>);
close(FILE);
my @worksheets = @{$xmldata->{Worksheet}};
for my $worksheet (@worksheets) {
my @rows = @{%{$worksheet}->{Table}->{Row}};
for my $row (@rows) {
my @cells = @{%{$row}->{Cell}};
for my $cell (@cells) {
print %{$cell}->{Data}->{content};
}
}
}
以下、コードの簡単な解説です。
4.Worksheetの取得方法
Worksheetにハッシュでアクセスし、配列にします。
my @worksheets = @{$xmldata->{Worksheet}};
5.Rowの取得方法
4項で取得した@worksheetsをfor文でまわします。$worksheetをハッシュにして、取得したRowを配列にします。
for my $worksheet (@worksheets) {
my @rows = @{%{$worksheet}->{Table}->{Row}};
…中略…
}
6.Cellの取得方法
5項で取得した@rowsをfor文でまわします。$rowをハッシュにして、取得したCellを配列にします。
for my $row (@rows) {
my @cells = @{%{$row}->{Cell}};
…中略…
}
7.Dataの出力方法
6項で取得した@cellsをfor文でまわします。$cellをハッシュにして、最後に{content}を与えます。
for my $cell (@cells) {
print %{$cell}->{Data}->{content};
}
8.参考サイト
参考サイトは以下です。ありがとうございました。
XML::Simpleで取得したデータが引き起こす文字化けの対処方法
XML::Simpleを使ったPerlスクリプトの文字化けではまってしまったので、その備忘録です。
1.発生事象
Perlスクリプトで次のXMLファイルを読み込みます。ファイルの文字コードはUTF-8です。
<?xml version="1.0" encoding="utf-8"?>
<list>
<name>ほげ</name>
</list>
Perlスクリプト(CGI)のサンプルは次の通りです。ファイルの文字コードはUTF-8です。
#!/usr/bin/perl
use strict;
use CGI;
use XML::Simple;
my $q = new CGI;
print $q->header(-charset=>'utf-8');
print $q->start_html;
my $xml = XMLin('foo.xml');
my $name = $xml->{name};
print <<EOF;
<form method="post" action="hoge.cgi">
名前:<input type="text" id="hoge" name="hoge" value="$name" />
<input type="submit" name="submit" value="送信" />
</form>
EOF
print $q->end_html;
このCGIをブラウザで表示すると、次のように、スクリプトに記述した全角文字の文字化けが発生します。
![]()
XMLファイルから取得する文字が、次のように全角文字でなければ文字化けは発生しません。
<?xml version="1.0" encoding="utf-8"?>
<list>
<name>hoge</name>
</list>
![]()
2.原因
XMLinで日本語を含む文字列を取得する場合、XML宣言のencoding属性に指定した文字コードの内部文字列に変換されます。よって外部に出力する場合、バイト文字列に変換(エンコード)する必要があるようです。
Data::Dumperで$nameを出力すると、「ほげ」は次のようになっていました。
$VAR1 = "\x{307b}\x{3052}";
試しに、次のようなサンプルであれば、
my $name = 'ほげ';
Data::Dumperで$nameを出力すると、
$VAR1 = 'ほげ';
となりました。
推測ですが、文字化けが発生したのは、内部文字列とバイト文字列が混在していたため、Perl自体がバイト文字列である「名前」「送信」をさらにエンコードを行い、文字化けが発生したものと思われます(解釈が間違っていたらご指摘ください)。
3.対処
スクリプトの$nameをUTF-8にエンコードすることで解決しました。
#!/usr/bin/perl
use strict;
use CGI;
use XML::Simple;
use Encode;
my $q = new CGI;
print $q->header(-charset=>'utf-8');
print $q->start_html;
my $xml = XMLin('foo.xml');
my $name = encode('UTF-8', $xml->{name});
print <<EOF;
<form method="post" action="hoge.cgi">
名前:<input type="text" id="hoge" name="hoge" value="$name" />
<input type="submit" name="submit" value="送信" />
</form>
EOF
print $q->end_html;
![]()
4.その他
出力時だけでなく、スクリプトに記述した全角文字と比較をする際も、予めエンコードしておく必要があります。
my $name = encode('UTF-8', $xml->{name});
if ($name eq 'ほげ') { ... }
以上です。色々調べてこれが最適解と思ったのですが、より適切な解決方法がありましたらコメントください。
2010.6.18 追記
下記の記事で、utf8プラグマの情報頂きました。ありがとうございました。
5.参考サイト
参考サイトは下記です。ありがとうございました。