File Coverage

blib/lib/HTML/FromANSI/Tiny.pm
Criterion Covered Total %
statement 73 73 100.0
branch 24 28 85.7
condition 7 9 77.7
subroutine 14 14 100.0
pod 8 8 100.0
total 126 132 95.4


line stmt bran cond sub pod time code
1             # vim: set ts=2 sts=2 sw=2 expandtab smarttab:
2             #
3             # This file is part of HTML-FromANSI-Tiny
4             #
5             # This software is copyright (c) 2011 by Randy Stauner.
6             #
7             # This is free software; you can redistribute it and/or modify it under
8             # the same terms as the Perl 5 programming language system itself.
9             #
10 12     12   6627640 use strict;
  12         29  
  12         586  
11 12     12   66 use warnings;
  12         22  
  12         18257  
12              
13             package HTML::FromANSI::Tiny;
14             # git description: v0.106-2-g1454a0d
15              
16             our $AUTHORITY = 'cpan:RWSTAUNER';
17             # ABSTRACT: Easily convert colored command line output to HTML
18             $HTML::FromANSI::Tiny::VERSION = '0.107';
19             our @COLORS = map { "#$_" }
20             qw(
21             000 f33 2c2 bb0 55c d3d 0cc bbb
22             555 f66 6d6 dd6 99f f6f 6dd fff
23             );
24              
25             # 256 palette.
26             our @COLORS256 = (
27             # First 16 are the same.
28             @COLORS,
29             # rgbXYZ
30             do {
31             my @c;
32             for my $r ( 0 .. 5 ){
33             for my $g ( 0 .. 5 ){
34             for my $b ( 0 .. 5 ){
35             push @c, '#' . join('', map { sprintf '%02x', $_ * (255/5) } ($r, $g, $b));
36             }
37             }
38             }
39             @c; # return
40             },
41             # "nearly black to nearly white"
42             (map { '#' . join('', (sprintf '%02x', ($_ + 1) * 10) x 3) } (0 .. 23)),
43             );
44              
45             our @ALLCOLORS = (@COLORS, @COLORS256);
46              
47              
48             sub new {
49 41     41 1 65177 my $class = shift;
50             my $self = {
51             class_prefix => '',
52             selector_prefix => '',
53             tag => 'span',
54             # It seems particularly unlikely that somebody would want these in their HTML.
55             remove_escapes => 1,
56 41 100       355 @_ == 1 ? %{ $_[0] } : @_,
  5         28  
57             };
58              
59             require Parse::ANSIColor::Tiny
60 41 100       7063 if !$self->{ansi_parser};
61             require HTML::Entities
62 41 100       89702 if !$self->{html_encode};
63              
64 41         69423 bless $self, $class;
65             }
66              
67              
68             sub ansi_parser {
69 58     58 1 4449 my ($self) = @_;
70 58   66     319 return $self->{ansi_parser} ||= do {
71             # hash slice
72 39         179 my (@fields, %copy) = qw(
73             auto_reverse
74             foreground background
75             remove_escapes
76             );
77 39         225 @copy{ @fields } = @$self{ @fields };
78 39         246 Parse::ANSIColor::Tiny->new(%copy);
79             };
80             }
81              
82              
83             sub attr_to_class {
84 5010     5010 1 11675 $_[1];
85             }
86              
87              
88             sub css {
89 10     10 1 6831 my ($self) = @_;
90 10         57 my $prefix = $self->{selector_prefix} . '.' . $self->{class_prefix};
91              
92 10         36 my $styles = $self->_css_class_attr;
93              
94             my @css = (
95             map {
96 10         3508 sprintf "%s%s { %s }",
97             ${prefix},
98             $self->attr_to_class($_),
99 5480         11197 $self->_css_attr_string($styles->{$_})
100             }
101             sort keys %$styles
102             );
103              
104 10 50       1798 return wantarray ? @css : join('', @css);
105             }
106              
107             sub _css_class_attr {
108 12     12   38 my ($self) = @_;
109 12   66     109 return $self->{_all_styles} ||= do {
110              
111 11         39 my $parser = $self->ansi_parser;
112 11         422 my $styles = {
113             bold => { 'font-weight' => 'bold' },
114             dark => { 'opacity' => '0.7' },
115             underline => { 'text-decoration' => 'underline' },
116             concealed => { 'visibility' => 'hidden' },
117             };
118             {
119 11         29 my $i = 0;
  11         26  
120 11         42 foreach my $fg ( $parser->foreground_colors ){
121 2992         10009 $styles->{$fg} = { color => $ALLCOLORS[$i++] };
122             }
123 11         292 $i = 0;
124 11         60 foreach my $bg ( $parser->background_colors ){
125 2992         13850 $styles->{$bg} = { 'background-color' => $ALLCOLORS[$i++] };
126             }
127             }
128              
129             # return
130             +{
131             %$styles,
132 11 100       1231 %{ $self->{styles} || {} },
  11         2505  
133             };
134             };
135             }
136              
137             sub _css_attr_string {
138 5492     5492   11301 my ($self, $attr) = @_;
139 5492         11471 return join ' ', map { "$_: $attr->{$_};" } keys %$attr;
  5492         24671  
140             }
141              
142              
143             sub html {
144 38     38 1 29757 my ($self, $text) = @_;
145 38 100       192 $text = $self->ansi_parser->parse($text)
146             unless ref($text) eq 'ARRAY';
147              
148 38         8296 my $tag = $self->{tag};
149 38         105 my $prefix = $self->{class_prefix};
150             # Preload if needed; Don't load if not.
151 38 100       169 my $styles = $self->{inline_style} ? $self->_css_class_attr : {};
152              
153 38         70 local $_;
154             my @html = map {
155 38         75 my ($attr, $text) = @$_;
  97         258  
156 97         250 my $h = $self->html_encode($text);
157              
158             $self->{no_plain_tags} && !@$attr
159             ? $h
160 97 100 100     2683 : do {
161             sprintf q[<%s %s="%s">%s], $tag,
162             ($self->{inline_style}
163 12         30 ? (style => join ' ', map { $self->_css_attr_string($styles->{$_}) } @$attr)
164 88 100       338 : (class => join ' ', map { $prefix . $self->attr_to_class($_) } @$attr)
  81         199  
165             ), $h, $tag;
166             }
167              
168             } @$text;
169              
170 38 100       429 return wantarray ? @html : join('', @html);
171             }
172              
173              
174             sub html_encode {
175 97     97 1 226 my ($self, $text) = @_;
176             return $self->{html_encode}->($text)
177 97 100       236 if $self->{html_encode};
178 92         326 return HTML::Entities::encode_entities($text);
179             }
180              
181              
182             sub style_tag {
183 1     1 1 3582 my ($self) = @_;
184 1         5 my @style = ('');
185 1 50       128 return wantarray ? @style : join('', @style);
186             }
187              
188              
189             our @EXPORT_OK = qw( html_from_ansi );
190 2     2 1 504939 sub html_from_ansi { __PACKAGE__->new->html(@_) }
191              
192             sub import {
193 1     1   10 my $class = shift;
194 1 50       5 return unless @_;
195              
196 1         3 my $caller = caller;
197 12     12   142 no strict 'refs'; ## no critic (NoStrict)
  12         37  
  12         2615  
198              
199 1         3 foreach my $arg ( @_ ){
200             die "'$arg' is not exported by $class"
201 1 50       2 unless grep { $arg eq $_ } @EXPORT_OK;
  1         5  
202 1         2 *{"${caller}::$arg"} = *{"${class}::$arg"}{CODE};
  1         1990  
  1         17  
203             }
204             }
205              
206             1;
207              
208             __END__