4 # Copyright (C) 2013 Giuseppe Bilotta
5 # Distributed under the terms of the Artistic License,
6 # see 'Artistic' in the repository root.
16 use Scalar::Util qw(looks_like_number);
18 my %sizes; # meme base image sizes
19 my (%acros, %revacros); # meme base acronyms (BLB => bad-luck-brian.jpg) and reverse
21 # Are we running as CGI or from the command-line?
22 # TODO better detection
23 my $is_cgi = $ENV{'SCRIPT_FILENAME'};
25 my $script_path = $is_cgi ? dirname($ENV{'SCRIPT_FILENAME'}) : dirname($0);
27 my $sz_fname = $script_path . '/meme-sizes.lst';
30 open FILE, $sz_fname or die $!;
32 while (my $line = <FILE>) {
35 my ($width, $height, $fname) = split(/ /, $line, 3);
36 $sizes{$fname} = [$width, $height];
38 # Find a potential short form (acronym for multiword, no extension otherwise)
41 # remove article for the purpose of the shortening; we don't care if it's
42 # in the middle of a word because we only care about initials anyway
43 # FIXME this actually fails in the case of XXXthe-XXX, let's care about that
44 # when we actually come across it
48 $acro = join('', map { uc(substr($_, 0, 1)) } split(/-/, $the ));
50 $acro = (split(/\./, $the))[0]
52 if (!defined $acros{$acro}) {
53 $acros{$acro} = $fname;
54 $revacros{$fname} = $acro;
56 print STDERR "Trying to redefined acronym $acro from $acros{$acro} to ${fname}\n";
64 # params: img, width, height, font-size, text
66 my $svg_template=<<SVG;
67 <?xml version='1.0' encoding='UTF-8'?>
69 xmlns='http://www.w3.org/2000/svg'
70 xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'
71 viewBox='0 0 %2\$d %3\$d'>
72 <style type="text/css">text{font-family:'Impact';font-size:%4\$dpx;fill:white;stroke:black;stroke-width:2px;text-anchor:middle}</style>
73 <image xlink:href='%1\$s' x='0' y='0'
74 width='%2\$d' height='%3\$d'/>
80 return sprintf($svg_template,
81 $p{img}, $p{width}, $p{height}, $p{fs}, $p{text});
84 # params: text, y-pos, font-size
85 # the font-size presented here is optional, and should only be
86 # present when this text line has a different font-size than
89 my $txt_template=<<TXT;
90 <text x='50%%' y='%2\$d%%'%3\$s
97 return sprintf($txt_template, $text, $p{y}, $p{linefs});
100 # routine to actually prepare the SVG.
101 # params: img, sep, text
105 unless (defined $sizes{$p{img}}) {
106 return undef unless defined $acros{$p{img}};
107 $p{img} = $acros{$p{img}};
110 ($p{width}, $p{height}) = @{$sizes{$p{img}}};
112 $p{sep} = qr/\Q$p{sep}\E/;
114 # font size specification is default:line/per/line/override
115 # Non-numeric values are skipped
117 my @fss; # array of font-size overrides
119 ($p{fs}, $fs_override) = split(/:/, $p{fs},2);
120 if (defined $p{fs} and $p{fs} =~ $p{sep}) {
121 # there's a / in the default part. is this because there is no override part?
122 if (!defined $fs_override) {
123 $fs_override = $p{fs};
128 if (defined $fs_override) {
129 foreach (split($p{sep}, $fs_override, -1)) {
130 push @fss, looks_like_number($_) ? $_ : '';
134 my @lines; # text lines
135 foreach (split($p{sep}, $p{text}, -1)) {
140 $divisions = @lines if @lines > $divisions;
142 # if the user specified a single font-size, use that, otherwise
143 # compute a default one based on the number of divisions
144 if (defined $p{fs} and $p{fs}) {
145 $divisions = int($p{height}/$p{fs} + 0.5);
147 $p{fs} = int($p{height}/$divisions + 0.5);
148 if ($p{fs} > $p{width}/10) {
149 $p{fs} = int($p{width}/10 + 0.5) if $p{fs} > $p{width}/10;
150 $divisions = int($p{height}/$p{fs} + 0.5);
154 # formatted lines: each element is a ref to an array with the following elements:
155 # * line text (undef for empty line),
156 # * font size (undef for empty line or default font),
157 # * y increment (always defined)
160 my $total_height = 0; # total height of real lines
161 my $fillers = 0; # number of fillers
162 for (my $i = 0; $i < @lines; ++$i) {
163 my $line = $lines[$i];
166 if (defined $line and $line eq '') {
170 # let's have an actually defined font-size for purposes of height
172 if (!defined $fs or $fs eq '') {
176 $lh = int(100*$fs/$p{height} + 0.5);
177 $total_height += $lh;
179 # undefine $fs if not needed
180 if (!defined $line or $fs == $p{fs}) {
183 push @fmt_lines, [$line, $fs, $lh];
185 my $filler_size = $fillers? int((98 - $total_height)/$fillers) : 0;
188 foreach (@fmt_lines) {
189 $_->[2] = $filler_size unless defined $_->[2];
195 # iterate over both @lines and @fss. I'm sure there's a more perlish
197 foreach (@fmt_lines) {
203 next unless defined $line; #fillers just increment y
205 if (not defined $fs) {
208 # Damn, apparently attribute font-size does not ovverride style
209 # $p{linefs} = " font-size='$fs'";
210 $p{linefs} = " style='font-size:${fs}px'";
213 $p{text} .= fill_txt($line, %p);
223 while (my $q = new CGI::Fast) {
228 $p{img} = $q->param('m');
229 $p{sep} = $q->param('s');
230 @fs = $q->param('fs');
233 # TODO specify font-size from CLI
234 ($p{img}, $p{sep}, @t) = @ARGV;
238 $p{fs} = join($p{sep}, @fs);
239 $p{text} = join($p{sep}, @t);
241 $p{img} ||= (keys %sizes)[0];
242 $p{text}||= 'TOP TEST//BOTTOM TEST';
244 my $svg = make_svg(%p);
248 -type => 'image/svg+xml',
260 print $q->header(-status=>404),
261 $q->start_html("Unknown meme base"),
262 $q->h1("Unknown meme base!");
263 print "<p>Sorry, <tt>'" . encode_entities($p{img}) . "'</tt> is not a known meme base. ".
264 "You want one of the following instead:</p><ul>";
265 foreach (keys %sizes) {
266 print "<li><tt>" . encode_entities($_) . "</tt>";
267 print " (<tt>" . encode_entities($revacros{$_}) . "</tt>)" if defined $revacros{$_};
271 # foreach (keys %ENV) {
272 # print "<p>$_=$ENV{$_}</p>"
274 print $q->end_html();
277 foreach (keys %sizes) {
279 print STDERR " ($revacros{$_})" if defined $revacros{$_};