File Coverage

blib/lib/Acme/AsciiArt2HtmlTable.pm
Criterion Covered Total %
statement 126 126 100.0
branch 46 48 95.8
condition 5 6 83.3
subroutine 9 9 100.0
pod 1 1 100.0
total 187 190 98.4


element elements'
line stmt bran cond sub pod time code
1             package Acme::AsciiArt2HtmlTable;
2              
3 13     13   390533 use warnings;
  13         32  
  13         434  
4 13     13   71 use strict;
  13         25  
  13         21674  
5              
6             require Exporter;
7              
8             our @ISA = qw(Exporter);
9              
10             our %EXPORT_TAGS = (
11             'all' => [ qw(aa2ht) ],
12             );
13              
14             our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } );
15              
16             our @EXPORT = qw(aa2ht);
17              
18             =head1 NAME
19              
20             Acme::AsciiArt2HtmlTable - Converts Ascii art to an HTML table
21              
22             =head1 VERSION
23              
24             Version 0.01
25              
26             =cut
27              
28             our $VERSION = '0.01';
29              
30             =head1 SYNOPSIS
31              
32             use Acme::AsciiArt2HtmlTable;
33              
34             my $table = "ggggggggrrrrrrrrrrrrrr\n" .
35             "ggggggggrrrrrrrrrrrrrr\n" .
36             "ggggggggrrrrrrrrrrrrrr\n" .
37             "ggggggggrrrrrrrrrrrrrr\n" .
38             "ggggggyyyyrrrrrrrrrrrr\n" .
39             "ggggggyyyyrrrrrrrrrrrr\n" .
40             "gggggyyyyyyrrrrrrrrrrr\n" .
41             "gggggyyyyyyrrrrrrrrrrr\n" .
42             "ggggggyyyyrrrrrrrrrrrr\n" .
43             "ggggggyyyyrrrrrrrrrrrr\n" .
44             "ggggggggrrrrrrrrrrrrrr\n" .
45             "ggggggggrrrrrrrrrrrrrr\n" .
46             "ggggggggrrrrrrrrrrrrrr\n" .
47             "ggggggggrrrrrrrrrrrrrr\n" ;
48              
49             my $html = aa2ht( { td => { width => 3 , height => 3 } } , $table);
50              
51             # $html now holds a table with a color representation of your
52             # ascii art. In this case, the Portuguese flag.
53              
54             =cut
55              
56             our %default_configuration;
57              
58             =head1 FUNCTIONS
59              
60             =head2 aa2ht
61              
62             Gets ascii text and converts it to an HTML table. This is how it works:
63              
64             =over 4
65              
66             =item * each line is a C
67              
68             =item * each letter is a C element
69              
70             =item * each C has background of a specific color, which is
71             defined by the letter that created it
72              
73             =back
74              
75             =cut
76              
77             sub aa2ht {
78              
79             # default configuration
80 36     36 1 326 my %config = _clone_hash( \%default_configuration );
81              
82             =head3 OPTIONS
83              
84             You can pass a reference to a hash before the text you want to
85             convert.
86              
87             =cut
88              
89 36 100       171 if ( ref($_[0]) eq 'HASH' ) {
90 26         41 my $new_config = shift;
91              
92             =head4 id
93              
94             In order to save space in the output, C and C
95             attributes are not in each element, but rather in a C
113            
114              
115             =cut
116              
117 26 100       86 if (defined $new_config->{'id'}) { $config{'id'} = $new_config->{'id'} }
  1         3  
118              
119             =head4 use-default-colors
120              
121             If set to a false value, no default mappings are used.
122              
123             my $html = aa2ht( { 'use-default-colors' => 0 }, $ascii);
124              
125             Behind the curtains, there is still a mapping: the default mapping to
126             white.
127              
128             =cut
129              
130 26 100       79 if ( defined $new_config->{'use-default-colors'} ) {
131 2 50       7 if ( not $new_config->{'use-default-colors'}) {
132 2         7 $config{'colors'} = { 'default' => 'ffffff' } # everything is now white
133             }
134             }
135              
136             =head4 colors
137              
138             You can override color definitions or specify your own.
139              
140             my $html = aa2ht( { 'colors' => { '@' => 'ffddee',
141             'g' => '00ffff' } }, $ascii);
142              
143             =cut
144              
145 26 100       91 if ( ref($new_config->{'colors'}) eq 'HASH' ) {
146 10         15 for (keys %{$new_config->{'colors'}}) {
  10         33  
147 12         744 $config{'colors'}{$_} = $new_config->{'colors'}{$_};
148             }
149             }
150              
151             =head4 randomize-new-colors
152              
153             If set to a true value, letters with no mappings are assigned a
154             random one.
155              
156             my $html = aa2ht( { 'randomize-new-colors' => 1 }, $ascii);
157              
158             You might want to remove the default mappings if you're really
159             interested in a completely random effect:
160              
161             my $html = aa2ht( { 'use-default-colors' => 0,
162             'randomize-new-colors' => 1 }, $ascii);
163              
164             You might also want to keep the white space as a white block:
165              
166             my $html = aa2ht( { 'use-default-colors' => 0,
167             'colors' => { ' ' => 'ffffff'},
168             'randomize-new-colors' => 1 }, $ascii);
169              
170             =cut
171              
172 26 100       77 if ( defined $new_config->{'randomize-new-colors'} ) {
173 1         3 $config{'randomize-new-colors'} = $new_config->{'randomize-new-colors'}
174             }
175              
176             =head4 table
177              
178             With the parameter C you can specify specific values for fields
179             like C, C and C (all these have
180             value "0" by default).
181              
182             my $html = aa2ht( { 'table' => { 'border' => '1' } }, $ascii );
183              
184             These attributes go directly into the C tag. parameter you can specify specific values for C's
185              
186             =head4 tr
187              
188             With the C
189             attributes.
190              
191             These attributes go into a C\n";
240              
241             # table header
242 36         156 $html .= "\n"; ' . ( '' x $width ) . ''; "; "; \n$_\n" } @lines;
243              
244             # prepare the cells
245 36         133 my @lines = map { [ split //, $_ ] } split /\n/, $text;
  61         802  
246              
247             # just to make sure an optimized table has the same width as the normal one
248 36         76 my $opt_fix = '';
249 36 100       103 if ( $config{'optimization'} ) {
250 1         1 my $width = 0;
251 1         4 for my $l ( 0 .. $#lines ) {
252 3 100       5 if ( $width < $#{$lines[$l]} ) {
  3         2209  
253 1         4 $width = $#{$lines[$l]};
  1         8  
254             }
255             }
256 1         9 $opt_fix = '
257             }
258              
259 36         125 for my $line ( 0 .. $#lines ) {
260 61         98 for my $cell ( 0 .. $#{$lines[$line]} ) {
  61         149  
261 1393 100       2540 next if $lines[$line]->[$cell] eq '';
262              
263             # randomizing new colors
264 1389 100       2387 if ( $config{'randomize-new-colors'} ) {
265 4 100       15 if ( not defined $config{'colors'}{ $lines[$line]->[$cell] } ) {
266 1         4 $config{'colors'}{ $lines[$line]->[$cell] } = _random_color();
267             }
268             }
269              
270             # optimization
271 1389         1402 my $optimization = '';
272              
273             # debugging messages were kept for future reference
274              
275             # remember that lines and cells are not the exact values, as
276             # arrays start at index 0 and both lines and cells start at
277             # position 1
278              
279             #my $debug = "line $line, cell $cell, ";
280              
281 1389 100       2333 if ( $config{'optimization'} ) {
282              
283             #$debug .= "\nthis is line $line, cell $cell";
284             # check how many cells we could have on each line from the line we're
285             # in to the last one
286 5         7 my %we_could_have;
287 5         10 for ( $line .. $#lines ) {
288 11         22 $we_could_have{$_} = _count_in_the_beginning(
289             $lines[$line]->[$cell],
290 11         16 @{$lines[$_]}[$cell .. $#{$lines[$_]}]
  11         21  
291             );
292             #$debug .= "\nwe could have $we_could_have{$_} on line $_";
293             }
294              
295             # check, for each line, how many cells an area up to that line would have
296 5         6 my %area;
297             my %area_width;
298 5         10 for ( $line .. $#lines ) {
299 11         33 my $min = _min( @we_could_have{$line .. $_} );
300 11         26 $area{$_} = (1 + $_ - $line) * $min;
301 11         21 $area_width{$_} = $min;
302             #$debug .="\nwe could make an area of $area{$_} up to line $_, with a maximum of $area_width{$_} cells per line";
303             }
304              
305             # check which is the line that maximizes optimization
306 5         15 my $max_area = _max(values %area);
307 5         9 my $best_line = _max(grep { $area{$_} == $max_area } keys %area);
  11         25  
308             #$debug .= "\nour best choice seem to be using line $best_line";
309              
310             # check the are width
311 5         10 my $width = $cell + $area_width{$best_line} - 1;
312              
313             # clean everything in the area we're about to optimize
314             #$debug .= "\nwe want to clean everything from lines $line to $best_line and cells $cell to $width";
315 5         14 for my $l ( $line .. $best_line ) {
316 8         11 for my $c ( $cell .. $width ) {
317 9 100 100     45 next if ( $l == $line and $c == $cell );
318 4         13 $lines[$l]->[$c] = '';
319             }
320             }
321              
322             # optimize
323 5         10 my $rowspan = $best_line - $line + 1;
324 5         7 my $colspan = $area_width{$best_line};
325              
326 5 100       11 if ( $rowspan > 1 ) { $optimization .= " rowspan=\"$rowspan\"" }
  2         5  
327 5 100       19 if ( $colspan > 1 ) { $optimization .= " colspan=\"$colspan\"" }
  1         5  
328              
329             #$debug .= "\n";
330             }
331              
332 1389   66     4694 $lines[$line]->[$cell] = "
333             ( $config{'colors'}{ $lines[$line]->[$cell] } ||
334             $config{'colors'}{'default'} ) .
335             "\">
336              
337             }
338              
339 61         141 $lines[$line] = join "\n", grep /./, @{$lines[$line]};
  61         984  
340              
341 61 100       388 if ($config{'optimization'}) {
342             # this is so empty rows aren't ignored by the browser
343 3         8 $lines[$line] .= "\n
344             }
345              
346             }
347              
348             # the table
349 36         87 $html .= join "\n", map { "
  61         381  
350              
351 36 100       183 if ($config{'optimization'}) {
352             # this is so empty columns aren't ignored by the browser
353 1         3 $html .= "$opt_fix";
354             }
355              
356             # table footer
357 36         688 $html .= "\n
\n";
358              
359             # return the table
360 36         1740 return $html;
361             }
362              
363             =head3 SPECIALS
364              
365             =head4 optimization
366              
367             Table optimization, which is disabled by default, uses the C
368             and C C attributes to save up space.
369              
370             my $html = aa2ht( { 'optimization' => 1 }, $ascii );
371              
372             When the optimization algorithm sees a chance of turning some cells
373             into a big one, it does so. It always chooses the biggest area
374             possible for optimizing.
375              
376             If two different areas suitable for optimization starting from a given
377             cell are available and both present the same area size, the algorithm
378             picks the one that maximizes width.
379              
380             =head4 default color
381              
382             By default, an unmapped character is mapped to the default color,
383             which is black.
384              
385             You can override this color by assigning a different mapping to
386             "default" with the C option.
387              
388             my $html = aa2ht( { 'colors' => { 'default' => 'ffffff' } }, $ascii);
389              
390             This, for instance, makes the default color be white, thus making only
391             the recognized characters show up colored on the table.
392              
393             =head1 MAPPINGS ( LETTER -> COLOR )
394              
395             The following letters are mapped to colors in the following way:
396              
397             l 000000 # black
398             b 0000ff # blue
399             o a52a2a # brown
400             g 00ff00 # green
401             a bebebe # gray
402             e bebebe # grey
403             m ff00ff # magenta
404             o ffa500 # orange
405             p ffc0cb # pink
406             u a020f0 # purple
407             r ff0000 # red
408             w ffffff # white
409             y ffff00 # yellow
410              
411             L 000000 # light black
412             B add8e6 # lighe blue
413             O a52a2a # light brown
414             G 90ee90 # light green
415             A d3d3d3 # light gray
416             E d3d3d3 # light grey
417             M ff00ff # light magenta
418             O ffa500 # light orange
419             P ffb6c1 # light pink
420             U 9370db # light purple
421             R cd5c5c # light red
422             W ffffff # light white
423             Y ffffe0 # light yellow
424              
425             Spaces are mapped to white:
426              
427             ffffff # white
428              
429             By default, everything else is mapped to black
430              
431             default 000000 # black
432              
433             =cut
434              
435             BEGIN {
436              
437             # default configuration
438 13     13   6072 %default_configuration = (
439             id => 'default',
440             table => {
441             'border' => 0,
442             'cellpadding' => 0,
443             'cellspacing' => 0,
444             },
445             tr => {
446             },
447             td => {
448             'width' => '1px',
449             'height' => '1px',
450             },
451             colors=> {
452             ' ' => 'ffffff', # white
453              
454             'l' => '000000', # black
455             'b' => '0000ff', # blue
456             'o' => 'a52a2a', # brown
457             'g' => '00ff00', # green
458             'a' => 'bebebe', # gray
459             'e' => 'bebebe', # grey
460             'm' => 'ff00ff', # magenta
461             'o' => 'ffa500', # orange
462             'p' => 'ffc0cb', # pink
463             'u' => 'a020f0', # purple
464             'r' => 'ff0000', # red
465             'w' => 'ffffff', # white
466             'y' => 'ffff00', # yellow
467              
468             'L' => '000000', # light black
469             'B' => 'add8e6', # light blue
470             'O' => 'a52a2a', # light brown
471             'G' => '90ee90', # light green
472             'A' => 'd3d3d3', # light gray
473             'E' => 'd3d3d3', # light grey
474             'M' => 'ff00ff', # light magenta
475             'O' => 'ffa500', # light orange
476             'P' => 'ffb6c1', # light pink
477             'U' => '9370db', # light purple
478             'R' => 'cd5c5c', # light red
479             'W' => 'ffffff', # light white
480             'Y' => 'ffffe0', # light yellow
481              
482             default => '000000', # black
483             },
484             'randomize-new-colors' => 0,
485             'optimization' => 0,
486             );
487              
488             }
489              
490             # subroutines
491              
492             sub _random_color {
493 1     1   3 my $color = '';
494              
495 1         3 for (1 .. 6) {
496 6         15 $color .= qw/1 2 3 4 5 6 7 8 9 0 a b c d e f/[int rand 16];
497             }
498              
499 1         5 return $color;
500             }
501              
502             sub _clone_hash {
503 180     180   206 my %hash = %{+shift};
  180         1317  
504              
505 180         304 my %new_hash;
506              
507 180         497 for (keys %hash) {
508 1368 100       2113 if (ref($hash{$_})) {
509 144         316 $new_hash{$_} = { _clone_hash ( $hash{$_} ) };
510             }
511             else {
512 1224         2266 $new_hash{$_} = $hash{$_};
513             }
514             }
515              
516 180         1729 return %new_hash;
517             }
518              
519             sub _count_in_the_beginning {
520 11     11   25 my ($cell, @elems) = @_;
521 11         11 my $t = 0;
522 11         18 for (@elems) {
523 20 100       33 if ($cell eq $_) {
524 15         24 $t++;
525             }
526             else {
527 5         8 last;
528             }
529             }
530 11         41 return $t;
531             }
532              
533             sub _min {
534 11     11   13 my $min = shift;
535              
536 11         15 for (@_) {
537 8 100       20 if ( $min > $_ ) { $min = $_ }
  5         10  
538             }
539              
540 11         17 return $min;
541             }
542              
543             sub _max {
544 10     10   11 my $max = shift;
545              
546 10         17 for (@_) {
547 7 100       16 if ( $max < $_ ) { $max = $_ }
  3         7  
548             }
549              
550 10         17 return $max;
551             }
552              
553             =head1 SEE ALSO
554              
555             The examples/ directory.
556              
557             =head1 AUTHOR
558              
559             Jose Castro, C<< >>
560              
561             =head1 CAVEATS
562              
563             If you specify the C or C for C elements and you
564             also ask for optimization... I don't even want to imagine what will
565             happen...
566              
567             =head1 BUGS
568              
569             Please report any bugs or feature requests to
570             C, or through the web interface at
571             L. I will be notified, and then you'll automatically
572             be notified of progress on your bug as I make changes.
573              
574             =head1 COPYRIGHT & LICENSE
575              
576             Copyright 2005 Jose Castro, All Rights Reserved.
577              
578             This program is free software; you can redistribute it and/or modify it
579             under the same terms as Perl itself.
580              
581             =cut
582              
583             1; # End of Acme::AsciiArt2HtmlTable