#!/usr/local/bin/perl # $Id: graph-maker.cgi,v 1.11 2007/06/21 17:49:11 68user Exp $ # # Written by 68user # http://X68000.q-e-d.net/~68user/ use strict; use Jcode; use HTML::Template; use GD; use GD::Graph; use GD::Graph::colour; use GD::Graph::lines; use GD::Graph::linespoints; use GD::Graph::bars; use GD::Graph::hbars; use GD::Graph::points; use GD::Graph::area; use GD::Graph::pie; my %gd_defs = ( graph_type => { depend_check_only => 1, affect => { 'lines,linespoints,points,pie' => { cumulate => 0, }, 'bars,hbars,area,points,pie' => { line_width => 0, }, 'lines,bars,hbars,area,pie' => { marker_size => 0, }, 'lines,linespoints,bars,hbars,area,points' => { value_font => 0, pie_height => 0, label => 0, labelclr => 0, start_angle => 0, suppress_angle => 0, }, 'lines,linespoints,area,points,pie' => { shadowclr => 0, shadow_depth => 0, bar_spacing => 0, }, 'pie' => { x_axis_font => 0, x_tick_length => 0, x_long_ticks => 0, y_axis_font => 0, y_tick_length => 0, y_long_ticks => 0, x_ticks => 0, zero_axis => 0, zero_axis_only => 0, x_label => 0, x_label_font => 0, x_label_skip => 0, x_label_offset => 0, x_label_position => 0, x_labels_vertical => 0, y_label => 0, y_label_font => 0, y_label_skip => 0, y_label_offset => 0, y_label_position => 0, y_tick_number => 0, no_axes => 0, box_axis => 0, legendclr => 0, legend_placement => 0, legend_spacing => 0, legend_marker_width => 0, legend_marker_height => 0, axis_space => 0, y_max_value => 0, y_min_value => 0, x_plot_values => 0, y_plot_values => 0, show_values => 0, values_vertical => 0, values_space => 0, }, }, }, title => { desc => 'グラフタイトル', default_value => 'this is title', type => 'string', input_size => 10, comment => 'グラフ上部に記述するタイトル文字列', }, title_font => { desc => 'タイトルフォント', shortdesc => 'フォント', type => 'font', default_value => 'gdLargeFont', }, width => { desc => '画像の幅', shortdesc => '幅', type => 'digit', default_value => 400, min_value => 50, max_value => 1000, input_size => 6, comment => '生成するグラフ画像の幅 (グラフの幅ではなく画像の幅)', }, height => { desc => '画像の高さ', shortdesc => '高さ', type => 'digit', default_value => 200, min_value => 50, max_value => 1000, input_size => 6, comment => '生成するグラフ画像の高さ (グラフの高さではなく画像の高さ)', }, t_margin => { desc => '上マージン', type => 'digit', default_value => 0, input_size => 3, }, b_margin => { desc => '下マージン', type => 'digit', default_value => 0, input_size => 3, }, l_margin => { desc => '左マージン', type => 'digit', default_value => 0, input_size => 3, }, r_margin => { desc => '右マージン', type => 'digit', default_value => 0, input_size => 3, }, x_label => { desc => 'X軸ラベル', shortdesc => 'ラベル', type => 'string', default_value => 'X label', input_size => 8, }, y_label => { desc => 'Y軸ラベル', shortdesc => 'ラベル', type => 'string', default_value => 'Y label', input_size => 8, }, x_label_font => { desc => 'X軸ラベルフォント', shortdesc => 'フォント', type => 'font', default_value => 'gdSmallFont', }, y_label_font => { desc => 'Y軸ラベルフォント', shortdesc => 'フォント', type => 'font', default_value => 'gdSmallFont', }, x_axis_font => { desc => 'X軸目盛りフォント', shortdesc => 'フォント', type => 'font', default_value => 'gdSmallFont', comment => 'この例ではグラフ下部の 10, 20, 30 などのフォント', }, y_axis_font => { desc => 'Y軸目盛りフォント', shortdesc => 'フォント', type => 'font', default_value => 'gdSmallFont', comment => 'この例ではグラフ左の 0, 500, 1000 などのフォント', }, x_ticks => { desc => 'X軸目盛り有無', type => 'option', option_values => { 1 => { desc => 'あり', is_default => 1, }, 0 => { desc => 'なし', }, }, affect => { 0 => { x_long_ticks => 0, x_tick_length => 0, }, }, }, x_long_ticks => { desc => 'X軸目盛り線延長', shortdesc => '線延長', type => 'option', option_values => { 1 => { desc => 'する', }, 0 => { desc => 'しない', is_default => 1, }, }, }, y_long_ticks => { desc => 'Y軸目盛り線延長', shortdesc => '線延長', type => 'option', option_values => { 1 => { desc => 'する', }, 0 => { desc => 'しない', is_default => 1, }, }, }, # x_tick_number => { # desc => 'X軸目盛り個数', # type => 'digit', # default_value => 5, # input_size => 3, # }, x_tick_length => { desc => 'X軸目盛り線長', shortdesc => '線長', type => 'digit', default_value => 4, input_size => 2, comment => 'グラフ下部の目盛り部分から上に少し突き出ている線の長さ', }, y_tick_number => { desc => 'Y軸目盛り個数', type => 'digit', default_value => 5, input_size => 2, comment => 'グラフ左の目盛り部分から右に少し突き出ている線の長さ', }, y_tick_length => { desc => 'Y軸目盛り線長', shortdesc => '線長', type => 'digit', default_value => 4, input_size => 2, }, x_label_skip => { desc => 'X軸目盛りスキップ幅', shortdesc => 'スキップ幅', type => 'digit', default_value => 1, input_size => 2, }, x_label_offset => { desc => 'X軸目盛りスキップオフセット', shortdesc => 'スキップ開始オフセット', type => 'digit', default_value => 0, input_size => 2, }, y_label_skip => { desc => 'Y軸目盛りスキップ幅', shortdesc => 'スキップ幅', type => 'digit', default_value => 1, input_size => 2, }, y_label_offset => { desc => 'Y軸目盛りスキップオフセット', shortdesc => 'スキップ開始オフセット', type => 'digit', default_value => 0, input_size => 2, }, x_label_position => { desc => 'X軸ラベル位置', shortdesc => '位置', type => 'realdigit', subdesc => '0〜1.0 の実数', default_value => '0.75', input_size => 5, }, y_label_position => { desc => 'Y軸ラベル位置', shortdesc => '位置', type => 'realdigit', subdesc => '0〜1.0 の実数', default_value => 0.5, input_size => 5, }, x_labels_vertical => { desc => 'X軸ラベル縦横配置', shortdesc => '縦横配置', type => 'option', option_values => { 1 => { desc => '縦', }, 0 => { desc => '横', is_default => 1, }, }, }, box_axis => { desc => '軸囲みタイプ', type => 'option', option_values => { 1 => { desc => '上下左右を囲む', is_default => 1, }, 0 => { desc => '左と下だけ囲む', }, }, }, show_values => { desc => '値表示', type => 'option', option_values => { 1 => { desc => 'あり', is_default => 1, }, 0 => { desc => 'なし', }, }, affect => { 0 => { values_vertical => 0, values_space => 0, }, }, comment => 'グラフの点のそばに値を表示する。', }, values_vertical => { desc => '縦横配置', type => 'option', option_values => { 0 => { desc => '横', is_default => 1, }, 1 => { desc => '縦', }, }, comment => 'true の場合、グラフの点のそばの値表示を縦向きにする。', }, values_space => { desc => '値スペース', type => 'digit', default_value => 4, input_size => 3, comment => 'グラフの点と、そのそばに表示する値の文字列の間隔。ピクセル単位。', }, no_axes => { desc => '軸の有無', type => 'option', option_values => { 1 => { desc => 'なし', }, 'undef' => { desc => 'あり', is_default => 1, }, }, affect => { 1 => { box_axis => 0, }, }, comment => 'グラフの四方を囲む枠線の有無', }, axis_space => { desc => '目盛りフォント/グラフ間余白', type => 'digit', default_value => 4, input_size => 2, comment => '10・20 などの目盛りの文字列と、グラフ枠線との間隔。ピクセル数。', }, zero_axis => { desc => 'Y軸ゼロ目盛り', shortdesc => 'ゼロ目盛り', type => 'option', option_values => { 1 => { desc => 'あり', }, 0 => { desc => 'なし', is_default => 1, }, }, comment => 'Y の値が 0 である位置に線を引く。つまり Y=0 の横棒が引かれる。', }, zero_axis_only => { desc => 'Y軸ゼロ目盛り*のみ*', shortdesc => 'ゼロ目盛りのみ', type => 'option', option_values => { 1 => { desc => 'する', }, 0 => { desc => 'しない', is_default => 1, }, }, affect => { 1 => { zero_axis => 0, }, }, comment => 'Y の値が 0 である位置のみに線を引く。つまり Y=0 の横棒を引き、それ以外の横棒を引かない。', }, bgclr => { desc => 'bgclr(??)', type => 'color', default_value => 'pink', comment => 'よくわからん', }, fgclr => { desc => '軸/目盛り/枠の色', type => 'color', default_value => 'black', }, boxclr => { desc => 'グラフ背景色', type => 'color', default_value => 'white', }, accentclr => { desc => '棒/領域/円グラフ枠色', type => 'color', default_value => 'orange', }, axislabelclr => { desc => '目盛りラベル色', type => 'color', default_value => 'dblue', }, legendclr => { desc => '凡例色', type => 'color', default_value => 'dblue', }, value_font => { desc => '値フォント', type => 'font', default_value => 'gdTinyFont', }, pie_height => { desc => '円グラフ立体高さ', shortdesc => '立体高さ', type => 'digit', input_size => 2, default_value => 30, }, label => { desc => 'ラベル', type => 'string', input_size => 12, default_value => 'this is label', }, labelclr => { desc => 'ラベルの色', type => 'color', default_value => 'black', }, start_angle => { desc => '描画開始角度', subdesc => '0〜360。0:真下 90:左 180:上 270:右', type => 'digit', input_size => 3, default_value => 0, }, suppress_angle => { desc => '値描画省略角度', subdesc => '0〜360。データ割合が少なくてこれ以下の角度なら、値を描画しない', type => 'digit', input_size => 3, default_value => 0, }, shadow_depth => { desc => '影の深さ', type => 'digit', input_size => 2, default_value => 0, }, shadowclr => { desc => '影の色', type => 'color', default_value => 'gray', }, bar_spacing => { desc => '棒グラフ間の空白', type => 'digit', input_size => 2, default_value => 0, }, cumulate => { desc => '積算', type => 'option', option_values => { 1 => { desc => 'する', }, 0 => { desc => 'しない', is_default => 1, }, }, }, line_width => { desc => '線の太さ', type => 'digit', input_size => 2, default_value => 1, }, legend_placement => { desc => '凡例位置', shortdesc => '位置', type => 'option', option_values => { BL => { desc => '下部左' }, BC => { desc => '下部中央', is_default => 1 }, BR => { desc => '下部右' }, RT => { desc => '右上部' }, RC => { desc => '右中央' }, RB => { desc => '右下部' }, }, }, legend_spacing => { desc => '凡例余白', shortdesc => '余白', type => 'digit', input_size => 2, default_value => 4, }, legend_marker_width => { desc => '凡例マーカー幅', shortdesc => 'マーカー幅', type => 'digit', input_size => 2, default_value => 12, }, legend_marker_height => { desc => '凡例マーカー高さ', shortdesc => 'マーカー高さ', type => 'digit', input_size => 2, default_value => 8, }, marker_size => { desc => 'マーカーサイズ', type => 'digit', input_size => 2, default_value => 4, }, y_max_value => { desc => 'Y軸最大値', shortdesc => '最大値', type => 'digit', input_size => 5, default_value => 'undef', comment => 'Y値の最大値以上の値を指定すること (GD の仕様)', }, y_min_value => { desc => 'Y軸最小値', shortdesc => '最小値', type => 'digit', input_size => 5, default_value => 'undef', comment => 'Y値の最小値以下の値を指定すること (GD の仕様)', }, x_plot_values => { desc => 'X軸目盛り値', type => 'option', option_values => { 1 => { desc => '描画する', is_default => 1, }, 0 => { desc => '描画しない', }, }, affect => { 0 => { x_long_ticks => 0, x_tick_length => 0, x_axis_font => 0, x_ticks => 0, x_label_skip => 0, x_label_offset => 0, x_labels_vertical => 0, }, }, }, y_plot_values => { desc => 'Y軸目盛り値', type => 'option', option_values => { 1 => { desc => '描画する', is_default => 1, }, 0 => { desc => '描画しない', }, }, affect => { 0 => { y_axis_font => 0, }, }, }, ); my ($mode, $graph_type, $ref_line_types, $ref_data, $ref_legends, %FORM) = &parse_args; if ( $mode eq 'make_graph' ){ &make_graph(%FORM); } elsif ( $mode eq 'blank' ){ print "Content-type: text/html\n\n"; } else { &make_html(); } exit 0; #------------------------------------------------------- sub make_html { my $rownum = 6; my $colnum = 3; my @row_list; for ( my $row=1 ; $row<=$rownum ; $row++ ){ my @data_column_list; for ( my $col=1 ; $col<=$colnum ; $col++ ){ my %hash; $hash{'column_number'} = $col; $hash{'row_number'} = $row; $hash{'value'} = $col == 1 ? $row*10 : int(rand()*1000)-300; $hash{'nouse_in_pie'} = $col > 2 ? 1 : 0; push(@data_column_list, \%hash); } push(@row_list, { data_column_list => \@data_column_list, }); } my $tmpl_str = < GD::Graph グラフ作成マシーン

GD::Graph グラフ作成マシーン

GD::Graph によるグラフ生成 へ戻る
@{[ &make_form_common() ]}
グラフ種類
X軸
項目名
データ
," value="" size=4 class="nouse_in_pie" onChange="this.form.submit()">
凡例 " class="nouse_in_pie" value="Legend" size=8 onChange="this.form.submit()">
線タイプ

@{[ &make_form('basic') ]} @{[ &make_form('type_depend') ]}
@{[ &make_form('bottom') ]}
機能解説
@{[ &make_form('left') ]} @{[ &make_form('right') ]}
END ; my $tmpl = HTML::Template->new(scalarref => \$tmpl_str); $tmpl->param(column_list => [ map { my %h=(column_number => $_); \%h } (1..$colnum-1) ]); $tmpl->param(row_list => \@row_list); print "Content-type: text/html; charset=EUC-JP\n\n"; print $tmpl->output; } #------------------------------------------------------- sub make_graph { my $graph; if ( $graph_type eq 'lines' ){ $graph = GD::Graph::lines ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'linespoints' ){ $graph = GD::Graph::linespoints ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'bars' ){ $graph = GD::Graph::bars ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'hbars' ){ $graph = GD::Graph::hbars ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'points' ){ $graph = GD::Graph::points ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'area' ){ $graph = GD::Graph::area ->new($FORM{'width'}, $FORM{'height'}); } if ( $graph_type eq 'pie' ){ $graph = GD::Graph::pie ->new($FORM{'width'}, $FORM{'height'}); } $graph->set( title => $FORM{'title'}, x_label => $FORM{'x_label'}, y_label => $FORM{'y_label'}, x_ticks => $FORM{'x_ticks'}, x_label_skip => $FORM{'x_label_skip'}, x_tick_offset => $FORM{'x_tick_offset'}, y_label_skip => $FORM{'y_label_skip'}, y_tick_offset => $FORM{'y_tick_offset'}, x_label_position => $FORM{'x_label_position'}, y_label_position => $FORM{'y_label_position'}, x_labels_vertical => $FORM{'x_labels_vertical'}, box_axis => $FORM{'box_axis'}, no_axes => $FORM{'no_axes'}, t_margin => $FORM{'t_margin'}, b_margin => $FORM{'b_margin'}, l_margin => $FORM{'l_margin'}, r_margin => $FORM{'r_margin'}, x_long_ticks => $FORM{'x_long_ticks'}, x_tick_length => $FORM{'x_tick_length'}, # x_tick_number => $FORM{'x_tick_number'}, # x_tick_number => 'auto', y_long_ticks => $FORM{'y_long_ticks'}, y_tick_length => $FORM{'y_tick_length'}, y_tick_number => $FORM{'y_tick_number'}, bgclr => $FORM{'bgclr'}, fgclr => $FORM{'fgclr'}, boxclr => $FORM{'boxclr'}, accentclr => $FORM{'accentclr'}, axislabelclr => $FORM{'axislabelclr'}, legendclr => $FORM{'legendclr'}, axis_space => $FORM{'axis_space'}, pie_height => $FORM{'pie_height'}, start_angle => $FORM{'start_angle'}, suppress_angle => $FORM{'suppress_angle'}, shadow_depth => $FORM{'shadow_depth'}, shadowclr => $FORM{'shadowclr'}, bar_spacing => $FORM{'bar_spacing'}, zero_axis => $FORM{'zero_axis'}, zero_axis_only => $FORM{'zero_axis_only'}, cumulate => $FORM{'cumulate'}, line_width => $FORM{'line_width'}, line_types => $ref_line_types, legend_placement => $FORM{'legend_placement'}, marker_size => $FORM{'marker_size'}, legend_spacing => $FORM{'legend_spacing'}, legend_marker_width => $FORM{'legend_marker_width'}, legend_marker_height => $FORM{'legend_marker_height'}, y_max_value => $FORM{'y_max_value'}, y_min_value => $FORM{'y_min_value'}, x_plot_values => $FORM{'x_plot_values'}, y_plot_values => $FORM{'y_plot_values'}, values_vertical => $FORM{'values_vertical'}, values_space => $FORM{'values_space'}, # y_number_format => "%04d", ); if ( $graph_type eq 'pie' ){ $graph->set( label => $FORM{'label'}, labelclr => $FORM{'labelclr'}, ); } # my $font_file ='/usr/X11R6/lib/X11/fonts/TrueType/sazanami-gothic.ttf'; # $graph->set_title_font($font_file, 18); $graph->set_title_font(get_font($FORM{'title_font'})); # 意味のないフォントを設定すると GD が落ちることに注意。 # 例えば pie にはラベルがないので set_x_label_font をセットすると落ちる。 if ( $graph_type eq 'pie' ){ $graph->set_value_font(get_font($FORM{'value_font'})); } else { $graph->set_x_label_font(get_font($FORM{'x_label_font'})); $graph->set_y_label_font(get_font($FORM{'y_label_font'})); $graph->set_x_axis_font(get_font($FORM{'x_axis_font'})); $graph->set_y_axis_font(get_font($FORM{'y_axis_font'})); # 凡例がすべて空文字の場合は GD が落ちるので、事前チェック。 foreach my $legend ( @$ref_legends ){ if ( length($legend) > 0 ){ $graph->set_legend(@$ref_legends); last; } } } if ( $FORM{'show_values'} ){ $graph->set(show_values => $ref_data); } my $image = $graph->plot($ref_data) or die "Cannot create image"; binmode STDOUT; print "Content-type: image/png\n\n"; print $image->png(); } #------------------------------------------------------------------------------------ sub parse_args { my $query_string; my $l_mode = ''; my $l_graph_type = ''; my %l_FORM; my @l_data; if ( $ENV{'REQUEST_METHOD'} eq 'POST') { read(STDIN, $query_string, $ENV{CONTENT_LENGTH}); } else { $query_string = $ENV{QUERY_STRING}; } my %line_type_hash; my %data_hash; my %legend_hash; foreach ( split(/&/, $query_string) ) { my ($name, $value) = split(/=/, $_); $value =~ tr/+/ /; $value =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; # name="1,2" という渡し方をしているるので、URL デコードが必要。 $name =~ s/%([0-9a-fA-F][0-9a-fA-F])/pack("C", hex($1))/eg; if ( $name eq 'mode' ){ $l_mode = $value; } elsif ( $name eq 'graph_type' ){ $l_graph_type = $value; } elsif ( $name =~ m/^line_type(\d+)$/ ){ $line_type_hash{$1} = $value; } elsif ( $name =~ m/^(\d+),(\d+)/ ){ if ( $value eq 'undef' ){ $data_hash{$1}{$2} = undef; } else { $data_hash{$1}{$2} = $value; } } elsif ( $name =~ m/^legend(\d+)/ ){ $legend_hash{$1} = $value; } else { if ( $value eq 'undef' ){ $l_FORM{$name} = undef; } else { $l_FORM{$name} = $value; } } } my @l_line_types = map { $_ = $line_type_hash{$_} } sort { $a<=>$b } keys %line_type_hash; my @l_legends = map { $_ = $legend_hash{$_} } sort { $a<=>$b } keys %legend_hash; my @l_data; foreach my $col (sort { $a <=> $b } keys %data_hash ){ my @data; foreach my $row (sort { $a <=> $b } keys %{$data_hash{$col}} ){ push(@data, $data_hash{$col}{$row}); } push(@l_data, \@data); } return ($l_mode, $l_graph_type, \@l_line_types, \@l_data, \@l_legends, %l_FORM); } sub get_font { my ($font_str) = @_; if ( $font_str eq 'gdGiantFont' ){ return gdLargeFont; } if ( $font_str eq 'gdLargeFont' ){ return gdLargeFont; } if ( $font_str eq 'gdMediumBoldFont' ){ return gdMediumBoldFont; } if ( $font_str eq 'gdSmallFont' ){ return gdSmallFont; } if ( $font_str eq 'gdTinyFont' ){ return gdTinyFont; } } sub get_font_list { my @fonts = ('gdGiantFont', 'gdLargeFont', 'gdMediumBoldFont', 'gdSmallFont', 'gdTinyFont', ); return @fonts; } sub get_colorname_list { return GD::Graph::colour::colour_list(); } sub get_htmlrgb_by_colorname { my ($colorname) = @_; my ($r, $g, $b) = GD::Graph::colour::_rgb($colorname); return sprintf("#%02x%02x%02x", $r, $g, $b); } # "#FF00BB" という文字列を渡し、補色を求める。ただしグレーの補色はグレーになるので、 # 明度差を適当に sub get_complementary_htmlrgb { my ($rgb) = @_; my ($r, $g, $b) = $rgb =~ m/^#(..)(..)(..)/; my ($comp_r, $comp_g, $comp_b) = (255-hex($r), 255-hex($g), 255-hex($b)); if ( abs(( $comp_r + $comp_g + $comp_b ) - ( hex($r) + hex($g) + hex($b) )) < 400 ){ if ( $comp_r + $comp_g + $comp_b > (255*3)/2 ){ # RGB 合計が 255段階×RGB3色の半分より大きければ、黒。 $comp_r = $comp_g = $comp_b = 0; } else { # そうでなければ、白。 $comp_r = $comp_g = $comp_b = 255; } } return sprintf("#%02x%02x%02x", $comp_r, $comp_g, $comp_b); } #------------------------------------------------------------------------------------ sub make_form_common { my @depend_info; foreach my $def (sort keys %gd_defs){ if ( defined $gd_defs{$def}->{affect} ){ my %hash; $hash{'element_name'} = $def; my @depend_info_per_element; foreach my $joined_conds_to_enable ( keys %{$gd_defs{$def}->{affect}} ){ my %hash2; my @conds_to_enable; foreach (split(/,/, $joined_conds_to_enable)){ push(@conds_to_enable, {cond_value => $_}); } my @affected_elements; foreach (keys %{$gd_defs{$def}->{affect}->{$joined_conds_to_enable}}){ push(@affected_elements, {affected_element_name => $_}); } $hash2{'conds_to_enable'} = \@conds_to_enable; $hash2{'affected_elements'} = \@affected_elements; push(@depend_info_per_element, \%hash2); } $hash{'depend_info_per_element'} = \@depend_info_per_element; push(@depend_info, \%hash); } } my $tmpl_str = < END ; my $tmpl = HTML::Template->new(scalarref => \$tmpl_str, loop_context_vars => 1); $tmpl->param(depend_info => \@depend_info); return $tmpl->output; } #------------------------------------------------------------------------------------------ sub make_form { my ($form_position) = @_; my $form = ''; my %def_map = ( type_depend => { caption => 'グラフ種類固有情報', content => [ [ 'group:円グラフ', 'value_font', 'pie_height', 'label', 'labelclr', 'start_angle', 'suppress_angle', 'BR', ], [ 'group:線グラフ', 'line_width', 'BR', ], [ 'group:棒グラフ', 'shadowclr', 'shadow_depth', 'bar_spacing', 'BR', ], ], }, bottom => { caption => 'X軸関連', content => [ [ 'group:目盛り', 'axis_space', 'BR', ], [ 'group:X軸ラベル', 'x_label', 'x_label_font', 'x_label_position', 'BR', ], [ 'group:X軸目盛り', 'x_axis_font', 'x_tick_length', 'x_long_ticks', 'x_ticks', 'x_label_skip', 'x_label_offset', 'x_labels_vertical', 'x_plot_values', 'BR', ], ], }, left => { caption => 'Y軸関連', content => [ [ 'group:Y軸ラベル', 'y_label', 'y_label_font', 'y_label_position', 'BR', ], [ 'group:Y軸目盛り', 'y_axis_font', 'y_tick_length', 'y_long_ticks', 'zero_axis', 'zero_axis_only', 'y_label_skip', 'y_label_offset', 'y_plot_values', 'BR', ], [ 'group:Y軸値', 'y_max_value', 'y_min_value', ], ], }, basic => { caption => '基本', content => [ [ 'group:画像', 'width', 'height', 'BR', ], [ 'group:タイトル', 'title', 'title_font', 'BR', ], ], }, right => { caption => 'X・Y軸共通', content => [ [ 'group:値', 'show_values', 'values_vertical', 'values_space', 'BR', ], [ 'group:軸', 'no_axes', 'box_axis', 'BR', ], ['group:マージン', 't_margin', 'b_margin', 'l_margin', 'r_margin', 'BR', ], [ 'group:色', 'bgclr', 'fgclr', 'boxclr', 'accentclr', 'axislabelclr', 'legendclr', 'BR', ], [ 'group:凡例', 'legend_placement', 'legend_spacing', 'legend_marker_width', 'legend_marker_height', ], [ 'group:その他', 'cumulate', 'marker_size', 'y_tick_number', ], ], }, ); # %def_map 記述漏れ対策。 my %defs_for_check = %gd_defs; my @current_defs = @{$def_map{$form_position}->{'content'}}; my $caption = $def_map{$form_position}->{'caption'}; my @defs_ordered; foreach my $def (@current_defs){ if ( ref($def) eq 'ARRAY' ){ foreach my $def2 (@$def){ push(@defs_ordered, $def2); delete $defs_for_check{$def2}; } } else { push(@defs_ordered, $def); delete $defs_for_check{$def}; } } # @def_map 記述漏れの定義情報を追加。 if ( scalar(keys %defs_for_check) > 0 ){ # push(@defs_ordered, 'group:未整理'); foreach my $def (keys %defs_for_check){ # push(@defs_ordered, $def); } } $form .= qq(
$caption
\n); foreach my $def (@defs_ordered){ if ( $def eq 'BR' ){ $form .= "
\n"; next; } if ( $def =~ m/^group:(.*)/ ){ $form .= "▼$1\n"; next; } if ( defined $gd_defs{$def}->{'depend_check_only'} && $gd_defs{$def}->{'depend_check_only'} ){ # 依存関係チェックのみなのでスキップ next; } my $desc = $gd_defs{$def}->{'desc'}; my $shortdesc = $gd_defs{$def}->{'shortdesc'}; my $subdesc = $gd_defs{$def}->{'subdesc'}; my $type = $gd_defs{$def}->{'type'}; my $comment = $gd_defs{$def}->{'comment'}; my $input_name = $def; my $input_type; my $input_size = $gd_defs{$def}->{'input_size'}; my @option_value_list; my @option_desc_list; if ( $type eq 'string' || $type eq 'digit' || $type eq 'realdigit' ){ $input_type="TEXT"; } elsif ( $type eq 'font' || $type eq 'option' || $type eq 'color' ){ $input_type="SELECT"; if ( $type eq 'font' ){ @option_value_list = &get_font_list(); @option_desc_list = &get_font_list(); } elsif ( $type eq 'option' ){ @option_value_list = keys %{$gd_defs{$def}->{'option_values'}}; foreach (@option_value_list){ push(@option_desc_list, $gd_defs{$def}->{'option_values'}->{$_}->{'desc'}); } } elsif ( $type eq 'color' ){ @option_value_list = &get_colorname_list(); @option_desc_list = &get_colorname_list(); } } my $input_default_value = defined $gd_defs{$def}->{'default_value'} ? $gd_defs{$def}->{'default_value'} : ''; $form.= sprintf(qq(%s), $def, "$desc $def", "$comment", defined $shortdesc ? $shortdesc : $desc, ); if ( $input_type eq 'SELECT' ){ $form .= qq(\n); } else { $form .= sprintf(qq(\n), $input_type, $input_name, $input_default_value, defined $input_size ? "size='$input_size'" : ''); } $form .= qq(\n); if ( defined $subdesc ){ $form .= qq(($subdesc)\n); } } $form .= qq(
\n); return $form; }