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; # meme base acronyms (BLB => bad-luck-brian.jpg)
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';
29 open FILE, $sz_fname or die $!;
31 while (my $line = <FILE>) {
34 my ($width, $height, $fname) = split(/ /, $line, 3);
35 $sizes{$fname} = [$width, $height];
37 # Find a potential short form (acronym for multiword, no extension otherwise)
40 # remove article for the purpose of the shortening; we don't care if it's
41 # in the middle of a word because we only care about initials anyway
42 # FIXME this actually fails in the case of XXXthe-XXX, let's care about that
43 # when we actually come across it
47 $acro = join('', map { uc(substr($_, 0, 1)) } split(/-/, $the ));
49 $acro = (split(/\./, $the))[0]
51 if (!defined $acros{$acro}) {
52 $acros{$acro} = $fname;
54 print STDERR "Trying to redefined acronym $acro from $acros{$acro} to ${fname}\n";
58 my %revacros = reverse %acros;
63 # params: img, width, height, font-size, text
65 my $svg_template=<<SVG;
66 <?xml version='1.0' encoding='UTF-8'?>
68 xmlns='http://www.w3.org/2000/svg'
69 xmlns:xlink='http://www.w3.org/1999/xlink' version='1.1'
70 viewBox='0 0 %2\$d %3\$d'>
71 <style type="text/css">text{font-family:'Impact';font-size:%4\$dpx;fill:white;stroke:black;stroke-width:2px;text-anchor:middle}</style>
72 <image xlink:href='%1\$s' x='0' y='0'
73 width='%2\$d' height='%3\$d'/>
79 return sprintf($svg_template,
80 $p{img}, $p{width}, $p{height}, $p{fs}, $p{text});
83 # params: text, y-pos, font-size
84 # the font-size presented here is optional, and should only be
85 # present when this text line has a different font-size than
88 my $txt_template=<<TXT;
89 <text x='50%%' y='%2\$d%%'%3\$s
96 return sprintf($txt_template, $text, $p{y}, $p{linefs});
99 # routine to actually prepare the SVG.
100 # params: img, sep, text
104 unless (defined $sizes{$p{img}}) {
105 return undef unless defined $acros{$p{img}};
106 $p{img} = $acros{$p{img}};
109 ($p{width}, $p{height}) = @{$sizes{$p{img}}};
111 $p{sep} = qr/\Q$p{sep}\E/;
113 # font size specification is default:line/per/line/override
114 # Non-numeric values are skipped
116 my @fss; # array of font-size overrides
118 ($p{fs}, $fs_override) = split(/:/, $p{fs},2);
119 if (defined $p{fs} and $p{fs} =~ $p{sep}) {
120 # there's a / in the default part. is this because there is no override part?
121 if (!defined $fs_override) {
122 $fs_override = $p{fs};
127 if (defined $fs_override) {
128 foreach (split($p{sep}, $fs_override, -1)) {
129 push @fss, looks_like_number($_) ? $_ : '';
133 my @lines; # text lines
134 foreach (split($p{sep}, $p{text}, -1)) {
139 $divisions = @lines if @lines > $divisions;
141 # if the user specified a single font-size, use that, otherwise
142 # compute a default one based on the number of divisions
143 if (defined $p{fs} and $p{fs}) {
144 $divisions = int($p{height}/$p{fs} + 0.5);
146 $p{fs} = int($p{height}/$divisions + 0.5);
147 if ($p{fs} > $p{width}/10) {
148 $p{fs} = int($p{width}/10 + 0.5) if $p{fs} > $p{width}/10;
149 $divisions = int($p{height}/$p{fs} + 0.5);
153 # formatted lines: each element is a ref to an array with the following elements:
154 # * line text (undef for empty line),
155 # * font size (undef for empty line or default font),
156 # * y increment (always defined)
159 my $total_height = 0; # total height of real lines
160 my $fillers = 0; # number of fillers
161 for (my $i = 0; $i < @lines; ++$i) {
162 my $line = $lines[$i];
165 if (defined $line and $line eq '') {
169 # let's have an actually defined font-size for purposes of height
171 if (!defined $fs or $fs eq '') {
175 $lh = int(100*$fs/$p{height} + 0.5);
176 $total_height += $lh;
178 # undefine $fs if not needed
179 if (!defined $line or $fs == $p{fs}) {
182 push @fmt_lines, [$line, $fs, $lh];
184 my $filler_size = $fillers? int((98 - $total_height)/$fillers) : 0;
187 foreach (@fmt_lines) {
188 $_->[2] = $filler_size unless defined $_->[2];
194 # iterate over both @lines and @fss. I'm sure there's a more perlish
196 foreach (@fmt_lines) {
202 next unless defined $line; #fillers just increment y
204 if (not defined $fs) {
207 # Damn, apparently attribute font-size does not ovverride style
208 # $p{linefs} = " font-size='$fs'";
209 $p{linefs} = " style='font-size:${fs}px'";
212 $p{text} .= fill_txt($line, %p);
220 while (my $q = new CGI::Fast) {
225 $p{img} = $q->param('m');
226 $p{sep} = $q->param('s');
227 @fs = $q->param('fs');
230 # TODO specify font-size from CLI
231 ($p{img}, $p{sep}, @t) = @ARGV;
235 $p{fs} = join($p{sep}, @fs);
236 $p{text} = join($p{sep}, @t);
238 $p{img} ||= (keys %sizes)[0];
239 $p{text}||= 'TOP TEST//BOTTOM TEST';
241 my $svg = make_svg(%p);
245 -type => 'image/svg+xml',
257 print $q->header(-status=>404),
258 $q->start_html("Unknown meme base"),
259 $q->h1("Unknown meme base!");
260 print "<p>Sorry, <tt>'" . encode_entities($p{img}) . "'</tt> is not a known meme base. ".
261 "You want one of the following instead:</p><ul>";
262 foreach (keys %sizes) {
263 print "<li><tt>" . encode_entities($_) . "</tt>";
264 print " (<tt>" . encode_entities($revacros{$_}) . "</tt>)" if defined $revacros{$_};
268 # foreach (keys %ENV) {
269 # print "<p>$_=$ENV{$_}</p>"
271 print $q->end_html();
274 foreach (keys %sizes) {
276 print STDERR " ($revacros{$_})" if defined $revacros{$_};