#!/usr/bin/perl -w # $Id$ # -*- perl -*- vim:set ft=perl sw=4 sts=4: # Usage: relational-schema out.png RELATION:_Primary_Key,.Foreign_Key,Etc # To clarifiy, specifiy the relation name, followed by a colon, followed by # the attributes seperated by commas. Primary keys should be prefixed with # an underscore, and foreign keys with a period. # It is also possible to override or augment the functional dependencies by # adding a second colon and additional specifications. Since this was # unnecessary for the assignment, it is not documented here. use strict; use Image::Imlib2; use vars qw($font $maxwidth $pd $fh); $font = "Verdana/11"; $maxwidth = 550; $pd = 4; # Padding my $out = shift or die "No output file specified.\n"; my @rows = @ARGV; die "No rows.\n" unless (@rows); my $image = new Image::Imlib2->new(1,1); $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts"); $image->load_font($font); ($fh, $fh) = $image->get_text_size("(Ay)"); $image = new Image::Imlib2->new($maxwidth, ($#rows+1)*($fh*2+$pd*12+2)); $image->add_font_path ("/usr/share/fonts/truetype/msttcorefonts"); $image->load_font($font); $image->set_color(255,255,255,255); #$image->fill_rectangle(0,0,$maxwidth,($#rows+1)*($fh*2+$pd*12+2)); sub draw_boxed_text { my ($image, $text, $x, $y, $underline)=@_; $image->load_font($font); my ($w, $h) = $image->get_text_size($text); $image->set_color(255,255,255,255); $image->fill_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2); $image->set_color(0,0,0,255); $image->draw_rectangle($x,$y,$w+2*$pd+2,$fh+2*$pd+2); $image->draw_text($x+$pd,$y+$pd,$text); if(($underline||0)==1) { # Solid underline $image->draw_line($x+$pd,$y+$pd+$fh,$x+$w+$pd,$y+$pd+$fh); } elsif(($underline||0)==2) { # Dashed underline for(my $i=0;$i<$w;$i+=5) { $image->draw_line($x+$pd+$i,$y+$pd+$fh, $x+$pd+($i+2>$w?$w$i+2),$y+$pd+$fh); } } return $w+2*$pd+1; } my $line=0; foreach my $row (@rows) { my ($title,$element,@depends) = split (':',$row); my @elements = split (',',$element); my %elcenters = (); my (@in,@out); my ($toffset)=(2*$pd); while ($title =~ s/^>//) { $toffset += 16*$pd; } my ($offset) = ($toffset); foreach $element (@elements) { my $under=0; if ($element =~ s/^_//) { $under=1; push @out,($element); } elsif ($element =~ s/^\.//) { $under=2; push @in,($element); } else { push @in,($element); warn "Element $element in $title not marked as ". "primary or foreign key.\n" if ($element=~/_ID$/); } my $o=draw_boxed_text($image,$element,$offset,$line+$fh+5*$pd,$under); $elcenters{$element}=($offset+$o/2); $offset+=$o; } my $updown=0; my $shifttitle=0; if((($#depends>=0 and $depends[0] eq 'auto') or $#depends=-1) and $#in>=0 and $#out>=0) { $depends[0]=(join(",",@out)."/".join(",",@in)); } elsif($#depends>=0 and $depends[0] eq 'auto') { shift @depends; } foreach my $depend (@depends) { unless ($depend) { $updown++; next; } $image->set_color(0,(32*($updown>>1))%128,(64*($updown>>1))%256,255); my ($out,$in) = split('/',$depend); my ($near, $far); my @out = split(',', $out); my @in = split(',', $in ); my @temp = sort { $elcenters{$a} <=> $elcenters{$b} } (@out, @in); if($updown%2==1) { $shifttitle=1; $near=$line+$fh+5*$pd-1; $far=$line+$fh+1*$pd-1; } else { $near=$line+2*$fh+7*$pd+2; $far=$line+2*$fh+11*$pd+2; } $image->draw_line($elcenters{$temp[0]}+$pd*($updown>>1), $far,$elcenters{$temp[$#temp]}+$pd*($updown>>1),$far); foreach $out (@out) { $image->draw_line($elcenters{$out}+$pd*($updown>>1), $far,$elcenters{$out}+$pd*($updown>>1),$near); } foreach $in (@in) { # Draw arrowheads my $arrow = Image::Imlib2::Polygon->new(); $arrow->add_point($elcenters{$in}+$pd*($updown>>1),$near); $arrow->add_point($elcenters{$in}+$pd+$pd*($updown>>1), ($near+$far)/2); $arrow->add_point($elcenters{$in}-$pd+$pd*($updown>>1), ($near+$far)/2); $arrow->add_point($elcenters{$in},$near); $arrow->fill(); $image->draw_line($elcenters{$in}+$pd*($updown>>1), $far,$elcenters{$in}+$pd*($updown>>1),$near); $image->draw_polygon($arrow,1); } $updown++; } $image->set_color(0,0,0,255); $image->draw_text($toffset+$pd*2,$line+4*$pd-$shifttitle*(7*$pd/2),$title); $line+=2*$fh+12*$pd+2; } $image->save($out);