3 # $Id: auto-dia.pl,v 1.1 2003-02-14 20:29:12 kycl4rk Exp $
7 auto-dia.pl - Automatically create a diagram from a database schema
11 ./auto-dia.pl -d|--db=db_parser [options] schema.sql
15 -o|--output Output file name (default STDOUT)
16 -i|--image Output image type (default PNG)
17 -t|--title Title to give schema
18 -c|--cols Number of columns
22 This script will create a picture of your schema. Only the database
23 driver argument is required. If no output file name is given, then
24 image will be printed to STDOUT, so you should redirect the output
35 my $VERSION = (qw$Revision: 1.1 $)[-1];
37 my ( $out_file, $image_type, $db_driver, $title, $no_columns );
39 'd|db=s' => \$db_driver,
40 'o|output:s' => \$out_file,
41 'i|image:s' => \$image_type,
42 't|title:s' => \$title,
43 'c|columns:i' => \$no_columns,
45 my $file = shift @ARGV or pod2usage( -message => 'No input file' );
47 pod2usage( -message => "No db driver specified" ) unless $db_driver;
48 $image_type = $image_type ? lc $image_type : 'png';
51 my $t = SQL::Translator->new( parser => $db_driver, producer => 'Raw' );
52 my $data = $t->translate( $file ) or die $t->error;
53 my $font = gdTinyFont;
55 my $no_tables = scalar keys %$data;
56 $no_columns ||= sprintf( "%.0f", sqrt( $no_tables ) + .5 );
57 my $no_per_col = sprintf( "%.0f", $no_tables/$no_columns + .5 );
58 warn "no per col = '$no_per_col'\n";
60 my ( @shapes, $max_x, $max_y );
62 my ( $x, $y ) = ( 20, $orig_y );
69 sort { $a->[0] <=> $b->[0] }
70 map { [ $_->{'order'}, $_ ] }
73 my $table_name = $table->{'table_name'};
75 push @shapes, [ 'string', $font, $this_col_x, $y, $table_name ];
77 $y += $font->height + 2;
78 my $below_table_name = $y;
80 my $this_max_x = $this_col_x + ($font->width * length($table_name));
84 sort { $a->[0] <=> $b->[0] }
85 map { [ $_->{'order'}, $_ ] }
86 values %{ $table->{'fields'} };
89 for my $index ( @{ $table->{'indices'} || [] } ) {
90 next unless $index->{'type'} eq 'primary_key';
91 my @fields = @{ $index->{'fields'} || [] } or next;
95 my ( @fld_desc, $max_name );
96 for my $f ( @fields ) {
97 my $name = $f->{'name'} or next;
98 $name .= ' *' if $name eq $pk;
100 my $size = @{ $f->{'size'} || [] }
101 ? '(' . join( ',', @{ $f->{'size'} } ) . ')'
103 my $desc = join( ' ', map { $_ || () } $f->{'data_type'}, $size );
105 my $nlen = length $name;
106 $max_name = $nlen if $nlen > $max_name;
107 push @fld_desc, [ $name, $desc ];
111 for my $fld_desc ( @fld_desc ) {
112 my ( $name, $desc ) = @$fld_desc;
113 my $diff = $max_name - length $name;
114 $name .= ' ' x $diff;
115 $desc = $name . $desc;
117 push @shapes, [ 'string', $font, $this_col_x, $y, $desc ];
118 $y += $font->height + 2;
119 my $length = $this_col_x + ( $font->width * length( $desc ) );
120 $this_max_x = $length if $length > $this_max_x;
124 push @shapes, [ 'line', $this_col_x - 5, $below_table_name,
125 $this_max_x, $below_table_name ];
127 'rectangle', $this_col_x - 5, $top - 5, $this_max_x, $y + 5
129 $max_x = $this_max_x if $this_max_x > $max_x;
132 if ( ++$no_this_col == $no_per_col ) {
136 $this_col_x = $max_x;
137 $max_y = $y if $y > $max_y;
143 # Add the title and signature.
145 my $large_font = gdLargeFont;
146 my $title_len = $large_font->width * length $title;
147 push @shapes, [ 'string', $large_font, $max_x/2 - $title_len/2, 10, $title ];
149 my $sig = "auto-dia.pl $VERSION";
150 push @shapes, [ 'string', $font, $max_x/2 - $title_len/2, 10, $title ];
152 my $gd = GD::Image->new( $max_x + 10, $max_y );
153 unless ( $gd->can( $image_type ) ) {
154 die "GD can't create images of type '$image_type'\n";
156 my $white = $gd->colorAllocate(255,255,255);
157 my $black = $gd->colorAllocate(00,00,00);
158 $gd->interlaced( 'true' );
159 $gd->fill( 0, 0, $white );
160 for my $shape ( @shapes ) {
161 my $method = shift @$shape;
162 $gd->$method( @$shape, $black );
166 open my $fh, ">$out_file" or die "Can't write '$out_file': $!\n";
167 print $fh $gd->$image_type;
169 print "Image written to '$out_file'. Done.\n";
172 print $gd->$image_type;
179 Ken Y. Clark E<lt>kclark@cpan.orgE<gt>