Some refactoring, improve CLI usage
[svg-meme] / meme.fcgi
1 #!/usr/bin/perl -w
2
3 use strict;
4
5 use CGI::Fast;
6
7 use HTML::Entities;
8
9 use File::Basename;
10
11 my %sizes; # meme base image sizes
12 my %acros; # meme base acronyms (BLB => bad-luck-brian.jpg)
13
14 # Are we running as CGI or from the command-line?
15 # TODO better detection
16 my $is_cgi = $ENV{'SCRIPT_FILENAME'};
17
18 my $script_path =  $is_cgi ? dirname($ENV{'SCRIPT_FILENAME'}) : dirname($0);
19
20 my $sz_fname = $script_path . '/meme-sizes.lst';
21
22 open FILE, $sz_fname or die $!;
23
24 while (my $line = <FILE>) {
25         chomp($line);
26         next unless $line;
27         my ($width, $height, $fname) = split(/ /, $line, 3);
28         $sizes{$fname} = [$width, $height];
29
30         # Find a potential short form (acronym for multiword, no extension otherwise)
31         my $acro = '';
32
33         # remove article for the purpose of the shrotening; we don't care if it's
34         # in the middle of a word because we only care about initials anyway
35         # FIXME this actually fails in the case of XXXthe-XXX, let's care about that
36         # when we actually come across it
37         my $the = $fname;
38         $the =~ s/the-//g;
39         if ($the =~ /-/) {
40                 $acro = join('', map { uc(substr($_, 0, 1)) } split(/-/, $the ));
41         } else {
42                 $acro = (split(/\./, $the))[0]
43         }
44         if (!defined $acros{$acro}) {
45                 $acros{$acro} = $fname;
46         } else {
47                 print STDERR "Trying to redefined acronym $acro from $acros{$acro} to ${fname}\n";
48         }
49 }
50
51 my %revacros = reverse %acros;
52
53 close FILE;
54
55
56 # params: img, width, height, text
57
58 my $svg_template=<<SVG;
59 <?xml version='1.0' encoding='UTF-8'?>
60 <svg
61  xmlns='http://www.w3.org/2000/svg'
62  xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'
63  viewBox='0 0 %2\$d %3\$d'>
64 <style type="text/css">text{font-family:'Impact';fill:white;stroke:black;stroke-width:2px;text-anchor:middle}</style>
65 <image xlink:href='%1\$s' x='0' y='0'
66 width='%2\$d' height='%3\$d'/>
67 %4\$s</svg>
68 SVG
69
70 sub fill_svg($$$$) {
71         return sprintf($svg_template, @_);
72 }
73
74 # params: y-pos, font-size, text
75
76 my $txt_template=<<TXT;
77 <text x='50%%' y='%1\$d%%' font-size='%2\$d'
78 >%3\$s</text>
79 TXT
80
81 sub fill_txt($$$) {
82         return sprintf($txt_template, @_);
83 }
84
85 # routine to actually prepare the SVG.
86 # params: img, sep, text
87 sub make_svg($$@) {
88         my $img = shift;
89         my $sep = shift;
90         my @t   = @_;
91
92         unless (defined $img and defined $sizes{$img}) {
93                 return undef unless defined $img and defined $acros{$img};
94                 $img = $acros{$img};
95         }
96
97         my ($width, $height) = @{$sizes{$img}};
98
99         my $divisions = 7;
100         my @lines = ();
101         foreach (@t) {
102                 foreach (split /\Q$sep\E/) {
103                         push @lines, $_;
104                 }
105         }
106
107         $divisions = @lines if @lines > $divisions;
108
109         my $fontsize = int($height/$divisions + 0.5);
110         if ($fontsize > $width/10) {
111                 $fontsize = int($width/10 + 0.5) if $fontsize > $width/10;
112                 $divisions = int($height/$fontsize + 0.5);
113         }
114
115         my $offset = int(100/$divisions + 0.5);
116         my $fillers = grep { $_ eq '' } @lines;
117         my $real_lines = @lines - $fillers;
118         my $filler_size = $fillers ? int((98 - $offset*$real_lines)/$fillers) : 0;
119
120         my $dy = 0;
121         my $txt = '';
122         foreach (@lines) {
123                 if ($_ eq '') {
124                         $dy += $filler_size;
125                         next;
126                 }
127                 $dy += $offset;
128                 $txt .= fill_txt($dy, $fontsize, $_);
129         }
130
131         return fill_svg($img, $width, $height, $txt);
132 }
133
134 my ($img, $sep, @t); # image, line separator, text lines
135
136 while (my $q = new CGI::Fast) {
137
138         if ($is_cgi) {
139                 $img = $q->param('m');
140                 $sep = $q->param('s');
141                 @t = $q->param('t');
142         } else {
143                 ($img, $sep, @t) = @ARGV;
144         }
145
146         $img ||= (keys %sizes)[0];
147         $sep ||= '/';
148         @t = ('TOP TEST//BOTTOM TEST') unless @t;
149
150         my $svg = make_svg($img, $sep, @t);
151
152         if (defined $svg) {
153                 print $q->header(
154                         -type => 'image/svg+xml',
155                         -charset => 'UTF-8'
156                 ) if $is_cgi;
157
158                 print $svg;
159
160                 next if $is_cgi;
161                 exit 0;
162         }
163
164         # missing meme
165         if ($is_cgi) {
166                 print $q->header(-status=>404),
167                 $q->start_html("Unknown meme base"),
168                 $q->h1("Unknown meme base!");
169                 print   "<p>Sorry, <tt>'" . encode_entities($img) . "'</tt> is not a known meme base. ".
170                 "You want one of the following instead:</p><ul>";
171                 my %revacros = reverse %acros;
172                 foreach (keys %sizes) {
173                         print "<li><tt>" . encode_entities($_) . "</tt>";
174                         print " (<tt>" . encode_entities($revacros{$_}) . "</tt>)" if defined $revacros{$_};
175                         print "</tt></li>";
176                 }
177                 print "</ul>";
178                 # foreach (keys %ENV) {
179                 #       print "<p>$_=$ENV{$_}</p>"
180                 # }
181                 print $q->end_html();
182                 next;
183         } else {
184                 foreach (keys %sizes) {
185                         print STDERR "* $_";
186                         print STDERR " ($revacros{$_})" if defined $revacros{$_};
187                         print STDERR "\n";
188                 }
189                 exit -1;
190         }
191 }