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 7 7 100.0
total 125 131 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 9     9   40631 use strict;
  9         14  
  9         286  
11 9     9   44 use warnings;
  9         11  
  9         469  
12              
13             package HTML::FromANSI::Tiny;
14             # git description: v0.102-2-ge6a677d
15             $HTML::FromANSI::Tiny::VERSION = '0.103';
16             BEGIN {
17 9     9   7668 $HTML::FromANSI::Tiny::AUTHORITY = 'cpan:RWSTAUNER';
18             }
19             # ABSTRACT: Easily convert colored command line output to HTML
20              
21             our @COLORS = map { "#$_" }
22             qw(
23             000 f33 2c2 bb0 55c d3d 0cc bbb
24             555 f66 6d6 dd6 99f f6f 6dd fff
25             );
26              
27              
28             sub new {
29 35     35 1 15680 my $class = shift;
30 5         32 my $self = {
31             class_prefix => '',
32             selector_prefix => '',
33             tag => 'span',
34             # It seems particularly unlikely that somebody would want these in their HTML.
35             remove_escapes => 1,
36 35 100       197 @_ == 1 ? %{ $_[0] } : @_,
37             };
38              
39             require Parse::ANSIColor::Tiny
40 35 100       7836 if !$self->{ansi_parser};
41             require HTML::Entities
42 35 100       25609 if !$self->{html_encode};
43              
44 35         52985 bless $self, $class;
45             }
46              
47              
48             sub ansi_parser {
49 49     49 1 2982 my ($self) = @_;
50 49   66     411 return $self->{ansi_parser} ||= do {
51             # hash slice
52 33         285 my (@fields, %copy) = qw(
53             auto_reverse
54             foreground background
55             remove_escapes
56             );
57 33         144 @copy{ @fields } = @$self{ @fields };
58 33         371 Parse::ANSIColor::Tiny->new(%copy);
59             };
60             }
61              
62              
63             sub css {
64 9     9 1 3708 my ($self) = @_;
65 9         25 my $prefix = $self->{selector_prefix} . '.' . $self->{class_prefix};
66              
67 9         17 my $styles = $self->_css_class_attr;
68              
69 324         620 my @css = (
70 9         165 map { "${prefix}$_ { " . $self->_css_attr_string($styles->{$_}) . " }" }
71             sort keys %$styles
72             );
73              
74 9 50       131 return wantarray ? @css : join('', @css);
75             }
76              
77             sub _css_class_attr {
78 11     11   14 my ($self) = @_;
79 11   66     34 return $self->{_all_styles} ||= do {
80              
81 10         20 my $parser = $self->ansi_parser;
82 10         194 my $styles = {
83             bold => { 'font-weight' => 'bold' },
84             dark => { 'opacity' => '0.7' },
85             underline => { 'text-decoration' => 'underline' },
86             concealed => { 'visibility' => 'hidden' },
87             };
88             {
89 10         15 my $i = 0;
  10         12  
90 10         26 foreach my $fg ( $parser->foreground_colors ){
91 160         511 $styles->{$fg} = { color => $COLORS[$i++] };
92             }
93 10         25 $i = 0;
94 10         24 foreach my $bg ( $parser->background_colors ){
95 160         550 $styles->{$bg} = { 'background-color' => $COLORS[$i++] };
96             }
97             }
98              
99             # return
100             +{
101 10 100       194 %$styles,
102 10         80 %{ $self->{styles} || {} },
103             };
104             };
105             }
106              
107             sub _css_attr_string {
108 336     336   337 my ($self, $attr) = @_;
109 336         498 return join ' ', map { "$_: $attr->{$_};" } keys %$attr;
  336         1152  
110             }
111              
112              
113             sub html {
114 31     31 1 15315 my ($self, $text) = @_;
115 31 100       153 $text = $self->ansi_parser->parse($text)
116             unless ref($text) eq 'ARRAY';
117              
118 31         5235 my $tag = $self->{tag};
119 31         52 my $prefix = $self->{class_prefix};
120             # Preload if needed; Don't load if not.
121 31 100       87 my $styles = $self->{inline_style} ? $self->_css_class_attr : {};
122              
123 31         41 local $_;
124 86         134 my @html = map {
125 31         51 my ($attr, $text) = @$_;
126 86         174 my $h = $self->html_encode($text);
127              
128             $self->{no_plain_tags} && !@$attr
129             ? $h
130 86 100 100     1226 : do {
131 12         25 sprintf q[<%s %s="%s">%s], $tag,
132             ($self->{inline_style}
133 70         298 ? (style => join ' ', map { $self->_css_attr_string($styles->{$_}) } @$attr)
134 77 100       296 : (class => join ' ', map { $prefix . $_ } @$attr)
135             ), $h, $tag;
136             }
137              
138             } @$text;
139              
140 31 100       285 return wantarray ? @html : join('', @html);
141             }
142              
143              
144             sub html_encode {
145 86     86 1 105 my ($self, $text) = @_;
146 86 100       195 return $self->{html_encode}->($text)
147             if $self->{html_encode};
148 83         198 return HTML::Entities::encode_entities($text);
149             }
150              
151              
152             sub style_tag {
153 1     1 1 832 my ($self) = @_;
154 1         4 my @style = ('');
155 1 50       21 return wantarray ? @style : join('', @style);
156             }
157              
158              
159             our @EXPORT_OK = qw( html_from_ansi );
160 2     2 1 17 sub html_from_ansi { __PACKAGE__->new->html(@_) }
161              
162             sub import {
163 1     1   10 my $class = shift;
164 1 50       5 return unless @_;
165              
166 1         3 my $caller = caller;
167 9     9   50 no strict 'refs'; ## no critic (NoStrict)
  9         12  
  9         1124  
168              
169 1         2 foreach my $arg ( @_ ){
170 1         7 die "'$arg' is not exported by $class"
171 1 50       3 unless grep { $arg eq $_ } @EXPORT_OK;
172 1         1 *{"${caller}::$arg"} = *{"${class}::$arg"}{CODE};
  1         1980  
  1         6  
173             }
174             }
175              
176             1;
177              
178             __END__