File Coverage

blib/lib/Text/ANSITable.pm
Criterion Covered Total %
statement 527 859 61.3
branch 136 382 35.6
condition 75 234 32.0
subroutine 41 58 70.6
pod 25 31 80.6
total 804 1564 51.4


line stmt bran cond sub pod time code
1              
2             use 5.010001;
3 2     2   174579 use Carp;
  2         25  
4 2     2   11 use Log::ger;
  2         2  
  2         115  
5 2     2   2825 use Moo;
  2         83  
  2         8  
6 2     2   1243 use experimental 'smartmatch';
  2         18393  
  2         7  
7 2     2   3189  
  2         5630  
  2         10  
8             use ColorThemeUtil::ANSI qw(item_color_to_ansi);
9 2     2   884 #use List::Util qw(first);
  2         713  
  2         88  
10             use Scalar::Util 'looks_like_number';
11 2     2   12 require Win32::Console::ANSI if $^O =~ /Win/;
  2         3  
  2         19472  
12              
13             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
14             our $DATE = '2022-02-14'; # DATE
15             our $DIST = 'Text-ANSITable'; # DIST
16             our $VERSION = '0.608'; # VERSION
17              
18             # see Module::Features for more details on this
19             our %FEATURES = (
20             set_v => {
21             TextTable => 1,
22             },
23              
24             features => {
25             PerlTrove => {
26             "Development Status" => "5 - Production/Stable",
27             "Environment" => "Console",
28             # Framework
29             "Intended Audience" => ["Developers"],
30             "License" => "OSI Approved :: Artistic License",
31             # Natural Language
32             # Operating System
33             "Programming Language" => "Perl",
34             "Topic" => ["Software Development :: Libraries :: Perl Modules", "Utilities"],
35             # Typing
36             },
37              
38             TextTable => {
39             can_align_cell_containing_wide_character => 1,
40             can_align_cell_containing_color_code => 1,
41             can_align_cell_containing_newline => 1,
42             can_use_box_character => 1,
43             can_customize_border => 1,
44             can_halign => 1,
45             can_halign_individual_row => 1,
46             can_halign_individual_column => 1,
47             can_halign_individual_cell => 1,
48             can_valign => 1,
49             can_valign_individual_row => 1,
50             can_valign_individual_column => 1,
51             can_valign_individual_cell => 1,
52             can_rowspan => 0,
53             can_colspan => 0,
54             can_color => 1,
55             can_color_theme => 1,
56             can_set_cell_height => 1,
57             can_set_cell_height_of_individual_row => 1,
58             can_set_cell_width => 1,
59             can_set_cell_width_of_individual_column => 1,
60             speed => 'slow',
61             can_hpad => 1,
62             can_hpad_individual_row => 1,
63             can_hpad_individual_column => 1,
64             can_hpad_individual_cell => 1,
65             can_vpad => 1,
66             can_vpad_individual_row => 1,
67             can_vpad_individual_column => 1,
68             can_vpad_individual_cell => 1,
69             },
70             },
71             );
72              
73             my $ATTRS = [qw(
74              
75             use_color color_depth use_box_chars use_utf8 columns rows
76             column_filter row_filter show_row_separator show_header
77             show_header cell_width cell_height cell_pad cell_lpad
78             cell_rpad cell_vpad cell_tpad cell_bpad cell_fgcolor
79             cell_bgcolor cell_align cell_valign header_align header_valign
80             header_vpad header_tpad header_bpad header_fgcolor
81             header_bgcolor
82              
83             )];
84             my $STYLES = $ATTRS;
85             my $COLUMN_STYLES = [qw(
86              
87             type width align valign pad lpad rpad formats fgcolor
88             bgcolor wrap
89              
90             )];
91             my $ROW_STYLES = [qw(
92              
93             height align valign vpad tpad bpad fgcolor bgcolor
94              
95             )];
96             my $CELL_STYLES = [qw(
97              
98             align valign formats fgcolor bgcolor
99              
100             )];
101              
102             has border_style => (
103             is => 'rw',
104             trigger => sub {
105             require Module::Load::Util;
106             my ($self, $val) = @_;
107             $self->{border_style_obj} =
108             Module::Load::Util::instantiate_class_with_optional_args(
109             {ns_prefixes=>['BorderStyle::Text::ANSITable', 'BorderStyle', 'BorderStyle::Text::ANSITable::OldCompat']}, $val);
110             },
111             );
112              
113             has color_theme => (
114             is => 'rw',
115             trigger => sub {
116             require Module::Load::Util;
117             my ($self, $val) = @_;
118             $self->{color_theme_obj} =
119             Module::Load::Util::instantiate_class_with_optional_args(
120             {ns_prefixes=>['ColorTheme::Text::ANSITable', 'ColorTheme', 'ColorTheme::Text::ANSITable::OldCompat']}, $val);
121             },
122             );
123              
124             has columns => (
125             is => 'rw',
126             default => sub { [] },
127             trigger => sub {
128             my $self = shift;
129              
130             # check that column names are unique
131             my %seen;
132             for (@{$_[0]}) { die "Duplicate column name '$_'" if $seen{$_}++ }
133              
134             $self->{_columns_set}++;
135             },
136             );
137             has rows => (
138             is => 'rw',
139             default => sub { [] },
140             trigger => sub {
141             my ($self, $rows) = @_;
142             $self->_set_default_cols($rows->[0]);
143             },
144             );
145             has column_filter => (
146             is => 'rw',
147             );
148             has column_wrap => (
149             is => 'rw',
150             );
151             has row_filter => (
152             is => 'rw',
153             );
154             has _row_separators => ( # [index after which sep should be drawn, ...] sorted
155             is => 'rw',
156             default => sub { [] },
157             );
158             has show_row_separator => (
159             is => 'rw',
160             default => sub { 2 },
161             );
162             has show_header => (
163             is => 'rw',
164             default => sub { 1 },
165             );
166              
167             has _column_styles => ( # store per-column styles
168             is => 'rw',
169             default => sub { [] },
170             );
171             has _row_styles => ( # store per-row styles
172             is => 'rw',
173             default => sub { [] },
174             );
175             has _cell_styles => ( # store per-cell styles
176             is => 'rw',
177             default => sub { [] },
178             );
179              
180             # each element of _cond_*styles is a two-element [$cond, ], where $cond is code
181             # (str|coderef) and the second element is a hashref containing styles.
182              
183             has _cond_column_styles => ( # store conditional column styles
184             is => 'rw',
185             default => sub { [] },
186             );
187             has _cond_row_styles => ( # store conditional row styles
188             is => 'rw',
189             default => sub { [] },
190             );
191             has _cond_cell_styles => ( # store conditional cell styles
192             is => 'rw',
193             default => sub { [] },
194             );
195              
196             has cell_width => (
197             is => 'rw',
198             );
199             has cell_height => (
200             is => 'rw',
201             );
202             has cell_pad => (
203             is => 'rw',
204             default => sub { 1 },
205             );
206             has cell_lpad => (
207             is => 'rw',
208             );
209             has cell_rpad => (
210             is => 'rw',
211             );
212             has cell_vpad => (
213             is => 'rw',
214             default => sub { 0 },
215             );
216             has cell_tpad => (
217             is => 'rw',
218             );
219             has cell_bpad => (
220             is => 'rw',
221             );
222             has cell_fgcolor => (
223             is => 'rw',
224             );
225             has cell_bgcolor => (
226             is => 'rw',
227             );
228             has cell_align => (
229             is => 'rw',
230             );
231             has cell_valign => (
232             is => 'rw',
233             );
234              
235             has header_align => (
236             is => 'rw',
237             );
238             has header_valign => (
239             is => 'rw',
240             );
241             has header_vpad => (
242             is => 'rw',
243             );
244             has header_tpad => (
245             is => 'rw',
246             );
247             has header_bpad => (
248             is => 'rw',
249             );
250             has header_fgcolor => (
251             is => 'rw',
252             );
253             has header_bgcolor => (
254             is => 'rw',
255             );
256              
257             with 'Term::App::Role::Attrs';
258              
259             my ($self, $item, $args, $is_bg) = @_;
260             item_color_to_ansi(
261 8     8   8964 ($self->{color_theme_obj}->get_item_color($item, $args) // undef), # because sometimes get_item_color() might return an empty list
262             $is_bg)
263 8   50     36 // '';
      50        
264             }
265              
266             my ($self, $args) = @_;
267              
268             if ($ENV{ANSITABLE_STYLE_SETS}) {
269 3     3 0 29 require JSON::MaybeXS;
270             my $sets = JSON::MaybeXS::decode_json($ENV{ANSITABLE_STYLE_SETS});
271 3 50       13 croak "ANSITABLE_STYLE_SETS must be an array"
272 0         0 unless ref($sets) eq 'ARRAY';
273 0         0 for my $set (@$sets) {
274 0 0       0 if (ref($set) eq 'ARRAY') {
275             $self->apply_style_set($set->[0], $set->[1]);
276 0         0 } else {
277 0 0       0 $self->apply_style_set($set);
278 0         0 }
279             }
280 0         0 }
281              
282             if ($ENV{ANSITABLE_STYLE}) {
283             require JSON::MaybeXS;
284             my $s = JSON::MaybeXS::decode_json($ENV{ANSITABLE_STYLE});
285 3 50       10 for my $k (keys %$s) {
286 0         0 my $v = $s->{$k};
287 0         0 croak "Unknown table style '$k' in ANSITABLE_STYLE environment, ".
288 0         0 "please use one of [".join(", ", @$STYLES)."]"
289 0         0 unless $k ~~ $STYLES;
290 0 0       0 $self->{$k} = $v;
291             }
292             }
293 0         0  
294             # pick a default border style
295             unless ($self->{border_style}) {
296             my $bs;
297              
298 3 50       9 my $use_utf8 = $self->use_utf8;
299 3         5  
300             # even though Term::Detect::Software decides that linux virtual console
301 3         48 # does not support unicode, it actually can display some uni characters
302             # like single borders, so we use it as the default here instead of
303             # singleo_ascii (linux vc doesn't seem to support box_chars).
304             my $emu_eng = $self->detect_terminal->{emulator_engine} // '';
305             my $linux_vc = $emu_eng eq 'linux' && !defined($ENV{UTF8});
306             if ($linux_vc) {
307 3   50     34446 $use_utf8 = 1;
308 3   33     72 $bs = 'UTF8::SingleLineOuterOnly';
309 3 50       53 }
310 0         0 # use statement modifier style to avoid block and make local work
311 0         0 local $self->{use_utf8} = 1 if $linux_vc;
312              
313             # we only default to utf8 border if user has set something like
314 3 50       22 # binmode(STDOUT, ":utf8") to avoid 'Wide character in print' warning.
315             unless (defined $ENV{UTF8}) {
316             require PerlIO;
317             my @layers = PerlIO::get_layers(STDOUT);
318 3 50       22 $use_utf8 = 0 unless 'utf8' ~~ @layers;
319 3         46 }
320 3         44  
321 3 50       31 if (defined $ENV{ANSITABLE_BORDER_STYLE}) {
322             $bs = $ENV{ANSITABLE_BORDER_STYLE};
323             } elsif (defined $ENV{BORDER_STYLE}) {
324 3 50       215 $bs = $ENV{BORDER_STYLE};
    50          
    50          
    50          
325 0         0 } elsif ($use_utf8) {
326             $bs //= 'UTF8::BrickOuterOnly';
327 0         0 } elsif ($self->use_box_chars) {
328             $bs = 'BoxChar::SingleLineOuterOnly';
329 0   0     0 } else {
330             $bs = 'ASCII::SingleLineOuterOnly';
331 0         0 }
332              
333 3         2233 $self->border_style($bs);
334             }
335              
336 3         82 # pick a default color theme
337             unless ($self->{color_theme}) {
338             my $ct;
339             if (defined $ENV{ANSITABLE_COLOR_THEME}) {
340 3 50       17300 $ct = $ENV{ANSITABLE_COLOR_THEME};
341 3         5 } elsif ($self->use_color) {
342 3 50       80 my $bg = $self->detect_terminal->{default_bgcolor} // '';
    50          
343 0         0 if ($self->color_depth >= 2**24) {
344             $ct = 'Standard::Gradation' .
345 0   0     0 ($bg eq 'ffffff' ? 'WhiteBG' : '');
346 0 0       0 } else {
347 0 0       0 $ct = 'Standard::NoGradation' .
348             ($bg eq 'ffffff' ? 'WhiteBG' : '');;
349             }
350 0 0       0 } else {
351             $ct = 'NoColor';
352             }
353             $self->color_theme($ct);
354 3         2390 }
355              
356 3         79 unless (defined $self->{wide}) {
357             $self->{wide} = eval { require Text::ANSI::WideUtil; 1 } ? 1:0;
358             }
359 3 50       6768 require Text::ANSI::Util;
360 3 50       6 $self->{_func_add_color_resets} = \&Text::ANSI::Util::ta_add_color_resets;
  3         787  
  3         117839  
361             if ($self->{wide}) {
362 3         1306 require Text::ANSI::WideUtil;
363 3         573 $self->{_func_length_height} = \&Text::ANSI::WideUtil::ta_mbswidth_height;
364 3 50       12 $self->{_func_pad} = \&Text::ANSI::WideUtil::ta_mbpad;
365 3         74 $self->{_func_wrap} = \&Text::ANSI::WideUtil::ta_mbwrap;
366 3         6 } else {
367 3         7 $self->{_func_length_height} = \&Text::ANSI::Util::ta_length_height;
368 3         75 $self->{_func_pad} = \&Text::ANSI::Util::ta_pad;
369             $self->{_func_wrap} = \&Text::ANSI::Util::ta_wrap;
370 0         0 }
371 0         0 }
372 0         0  
373             my ($self, $row) = @_;
374             return if $self->{_columns_set}++;
375             $self->columns([map {"col$_"} 0..@$row-1]) if $row;
376             }
377 1     1   2  
378 1 50       3 my ($self, $row, $styles) = @_;
379 0 0       0 croak "Row must be arrayref" unless ref($row) eq 'ARRAY';
  0         0  
380             push @{ $self->{rows} }, $row;
381             $self->_set_default_cols($row) unless $self->{_columns_set}++;
382             if ($styles) {
383 7     7 1 833 my $i = @{ $self->{rows} }-1;
384 7 100       60 for my $s (keys %$styles) {
385 6         9 $self->set_row_style($i, $s, $styles->{$s});
  6         11  
386 6 100       18 }
387 6 50       11 }
388 0         0 $self;
  0         0  
389 0         0 }
390 0         0  
391             my ($self) = @_;
392             my $idx = ~~@{$self->{rows}}-1;
393 6         11 # ignore duplicate separators
394             push @{ $self->{_row_separators} }, $idx
395             unless @{ $self->{_row_separators} } &&
396             $self->{_row_separators}[-1] == $idx;
397 0     0 1 0 $self;
398 0         0 }
  0         0  
399              
400 0         0 my ($self, $rows, $styles) = @_;
401 0         0 croak "Rows must be arrayref" unless ref($rows) eq 'ARRAY';
402 0 0 0     0 $self->add_row($_, $styles) for @$rows;
403 0         0 $self;
404             }
405              
406             my $self = shift;
407 2     2 1 45 my $colname = shift;
408 2 100       13  
409 1         3 return $colname if looks_like_number($colname);
410 1         2 my $cols = $self->{columns};
411             for my $i (0..@$cols-1) {
412             return $i if $cols->[$i] eq $colname;
413             }
414 36     36   43 croak "Unknown column name '$colname'";
415 36         44 }
416              
417 36 100       110 my ($self, $rownum, $col) = @_;
418 10         16  
419 10         24 $col = $self->_colnum($col);
420 14 100       43  
421             $self->{rows}[$rownum][$col];
422 1         69 }
423              
424             my ($self, $rownum, $col, $val) = @_;
425              
426 6     6 1 1588 $col = $self->_colnum($col);
427              
428 6         14 my $oldval = $self->{rows}[$rownum][$col];
429             $self->{rows}[$rownum][$col] = $val;
430 5         18 $oldval;
431             }
432              
433             my ($self, $col, $style) = @_;
434 1     1 1 3  
435             $col = $self->_colnum($col);
436 1         3 $self->{_column_styles}[$col]{$style};
437             }
438 1         4  
439 1         2 my $self = shift;
440 1         4 my $col = shift;
441              
442             $col = $self->_colnum($col);
443              
444 0     0 1 0 my %sets = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_;
445              
446 0         0 for my $style (keys %sets) {
447 0         0 my $val = $sets{$style};
448             croak "Unknown per-column style '$style', please use one of [".
449             join(", ", @$COLUMN_STYLES) . "]" unless $style ~~ $COLUMN_STYLES;
450             $self->{_column_styles}[$col]{$style} = $val;
451 0     0 1 0 }
452 0         0 }
453              
454 0         0 my $self = shift;
455             $self->{_cond_column_styles};
456 0 0       0 }
  0         0  
457              
458 0         0 #sub set_cond_column_style {
459 0         0 # my ($self, $styles) = @_;
460 0 0       0 # $self->{_cond_column_styles} = $styles;
461             #}
462 0         0  
463             my $self = shift;
464             my $cond = shift;
465             if (ref($cond) ne 'CODE') {
466             croak "cond must be a coderef";
467 0     0 1 0 }
468 0         0  
469             my $styles;
470             if (ref($_[0]) eq 'HASH') {
471             $styles = shift;
472             } else {
473             $styles = { @_ };
474             }
475              
476             for my $style (keys %$styles) {
477 0     0 1 0 croak "Unknown per-column style '$style', please use one of [".
478 0         0 join(", ", @$COLUMN_STYLES) . "]" unless $style ~~ $COLUMN_STYLES;
479 0 0       0 }
480 0         0  
481             push @{ $self->{_cond_column_styles} }, [$cond, $styles];
482             }
483 0         0  
484 0 0       0 #sub clear_cond_column_styles {
485 0         0 # my $self = shift;
486             # $self->{_cond_column_styles} = [];
487 0         0 #}
488              
489             my ($self, $col, $style) = @_;
490 0         0  
491 0 0       0 $col = $self->_colnum($col);
492              
493             # the result of calculation is cached here
494             if (defined $self->{_draw}{eff_column_styles}[$col]) {
495 0         0 return $self->{_draw}{eff_column_styles}[$col]{$style};
  0         0  
496             }
497              
498             my $cols = $self->{columns};
499             my %styles;
500              
501             # apply conditional styles
502             COND:
503             for my $ei (0..@{ $self->{_cond_column_styles} }-1) {
504 17     17 1 45 my $e = $self->{_cond_column_styles}[$ei];
505             local $_ = $col;
506 17         32 my $res = $e->[0]->(
507             $self,
508             col => $col,
509 17 100       36 colname => $cols->[$col],
510 16         120 );
511             next COND unless $res;
512             if (ref($res) eq 'HASH') {
513 1         2 $styles{$_} = $res->{$_} for keys %$res;
514 1         1 }
515             $styles{$_} = $e->[1]{$_} for keys %{ $e->[1] };
516             }
517              
518 1         2 # apply per-column styles
  1         7  
519 0         0 my $rss = $self->{_column_styles}[$col];
520 0         0 if ($rss) {
521 0         0 $styles{$_} = $rss->{$_} for keys %$rss;
522             }
523              
524             $self->{_draw}{eff_column_styles}[$col] = \%styles;
525              
526 0 0       0 $styles{$style};
527 0 0       0 }
528 0         0  
529             my ($self, $row, $style) = @_;
530 0         0  
  0         0  
531             $self->{_row_styles}[$row]{$style};
532             }
533              
534 1         4 my $self = shift;
535 1 50       3 my $row = shift;
536 0         0  
537             my %sets = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_;
538              
539 1         3 for my $style (keys %sets) {
540             my $val = $sets{$style};
541 1         6 croak "Unknown per-row style '$style', please use one of [".
542             join(", ", @$ROW_STYLES) . "]" unless $style ~~ $ROW_STYLES;
543             $self->{_row_styles}[$row]{$style} = $val;
544             }
545 0     0 1 0 }
546              
547 0         0 my $self = shift;
548             $self->{_cond_row_styles};
549             }
550              
551 0     0 1 0 #sub set_cond_row_style {
552 0         0 # my ($self, $styles) = @_;
553             # $self->{_cond_row_styles} = $styles;
554 0 0       0 #}
  0         0  
555              
556 0         0 my $self = shift;
557 0         0 my $cond = shift;
558 0 0       0 if (ref($cond) ne 'CODE') {
559             croak "cond must be a coderef";
560 0         0 }
561              
562             my $styles;
563             if (ref($_[0]) eq 'HASH') {
564             $styles = shift;
565 0     0 1 0 } else {
566 0         0 $styles = { @_ };
567             }
568              
569             for my $style (keys %$styles) {
570             croak "Unknown per-row style '$style', please use one of [".
571             join(", ", @$ROW_STYLES) . "]" unless $style ~~ $ROW_STYLES;
572             }
573              
574             push @{ $self->{_cond_row_styles} }, [$cond, $styles];
575 0     0 1 0 }
576 0         0  
577 0 0       0 #sub clear_cond_row_styles {
578 0         0 # my $self = shift;
579             # $self->{_cond_row_styles} = [];
580             #}
581 0         0  
582 0 0       0 my ($self, $row, $style) = @_;
583 0         0  
584             # the result of calculation is cached here
585 0         0 if (defined $self->{_draw}{eff_row_styles}[$row]) {
586             return $self->{_draw}{eff_row_styles}[$row]{$style};
587             }
588 0         0  
589 0 0       0 my $rows = $self->{rows};
590             my %styles;
591              
592             # apply conditional styles
593 0         0 COND:
  0         0  
594             for my $ei (0..@{ $self->{_cond_row_styles} }-1) {
595             my $e = $self->{_cond_row_styles}[$ei];
596             local $_ = $row;
597             my $res = $e->[0]->(
598             $self,
599             row => $row,
600             row_data => $rows->[$row],
601             );
602 20     20 1 39 next COND unless $res;
603             if (ref($res) eq 'HASH') {
604             $styles{$_} = $res->{$_} for keys %$res;
605 20 100       38 }
606 18         79 $styles{$_} = $e->[1]{$_} for keys %{ $e->[1] };
607             }
608              
609 2         4 # apply per-row styles
610 2         4 my $rss = $self->{_row_styles}[$row];
611             if ($rss) {
612             $styles{$_} = $rss->{$_} for keys %$rss;
613             }
614 2         3  
  2         8  
615 0         0 $self->{_draw}{eff_row_styles}[$row] = \%styles;
616 0         0  
617 0         0 $styles{$style};
618             }
619              
620             my ($self, $row, $col, $style) = @_;
621              
622 0 0       0 $col = $self->_colnum($col);
623 0 0       0 $self->{_cell_styles}[$row][$col]{$style};
624 0         0 }
625              
626 0         0 my $self = shift;
  0         0  
627             my $row = shift;
628             my $col = shift;
629              
630 2         6 $col = $self->_colnum($col);
631 2 50       4  
632 0         0 my %sets = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_;
633              
634             for my $style (keys %sets) {
635 2         4 my $val = $sets{$style};
636             croak "Unknown per-cell style '$style', please use one of [".
637 2         11 join(", ", @$CELL_STYLES) . "]" unless $style ~~ $CELL_STYLES;
638             $self->{_cell_styles}[$row][$col]{$style} = $val;
639             }
640             }
641 0     0 1 0  
642             my $self = shift;
643 0         0 $self->{_cond_cell_styles};
644 0         0 }
645              
646             #sub set_cond_cell_style {
647             # my ($self, $styles) = @_;
648 2     2 1 28 # $self->{_cond_cell_styles} = $styles;
649 2         4 #}
650 2         2  
651             my $self = shift;
652 2         7 my $cond = shift;
653             if (ref($cond) ne 'CODE') {
654 2 50       9 croak "cond must be a coderef";
  0         0  
655             }
656 2         6  
657 2         3 my $styles;
658 2 50       14 if (ref($_[0]) eq 'HASH') {
659             $styles = shift;
660 2         8 } else {
661             $styles = { @_ };
662             }
663              
664             for my $style (keys %$styles) {
665 0     0 1 0 croak "Unknown per-cell style '$style', please use one of [".
666 0         0 join(", ", @$CELL_STYLES) . "]" unless $style ~~ $CELL_STYLES;
667             }
668              
669             push @{ $self->{_cond_cell_styles} }, [$cond, $styles];
670             }
671              
672             #sub clear_cond_cell_styles {
673             # my $self = shift;
674             # $self->{_cond_cell_styles} = [];
675 0     0 1 0 #}
676 0         0  
677 0 0       0 my ($self, $row, $col, $style) = @_;
678 0         0  
679             # the result of calculation is cached here
680             if (defined $self->{_draw}{eff_cell_styles}[$row][$col]) {
681 0         0 return $self->{_draw}{eff_cell_styles}[$row][$col]{$style};
682 0 0       0 }
683 0         0  
684             my $rows = $self->{rows};
685 0         0 my %styles;
686              
687             # apply conditional styles
688 0         0 COND:
689 0 0       0 for my $ei (0..@{ $self->{_cond_cell_styles} }-1) {
690             my $e = $self->{_cond_cell_styles}[$ei];
691             local $_ = $rows->[$row][$col];
692             my $res = $e->[0]->(
693 0         0 $self,
  0         0  
694             content => $_,
695             col => $col,
696             row => $row,
697             row_data => $rows->[$row],
698             );
699             next COND unless $res;
700             if (ref($res) eq 'HASH') {
701             $styles{$_} = $res->{$_} for keys %$res;
702 10     10 1 24 }
703             $styles{$_} = $e->[1]{$_} for keys %{ $e->[1] };
704             }
705 10 100       27  
706 8         38 # apply per-cell styles
707             my $css = $self->{_cell_styles}[$row][$col];
708             if ($css) {
709 2         5 $styles{$_} = $css->{$_} for keys %$css;
710 2         3 }
711              
712             $self->{_draw}{eff_cell_styles}[$row][$col] = \%styles;
713              
714 2         3 $styles{$style};
  2         10  
715 0         0 }
716 0         0  
717 0         0 my $self = shift;
718             my $name = shift;
719             $name =~ /\A[A-Za-z0-9_]+(?:::[A-Za-z0-9_]+)*\z/
720             or croak "Invalid style set name, please use alphanums only";
721             {
722             my $name = $name;
723             $name =~ s!::!/!g;
724 0 0       0 require "Text/ANSITable/StyleSet/$name.pm"; ## no critic: Modules::RequireBarewordIncludes
725 0 0       0 }
726 0         0 my %args = ref($_[0]) eq 'HASH' ? %{$_[0]} : @_;
727             my $obj = "Text::ANSITable::StyleSet::$name"->new(%args);
728 0         0 $obj->apply($self);
  0         0  
729             }
730              
731             require Module::List;
732 2         7 my ($self) = @_;
733 2 50       5  
734 2         11 my $mods = Module::List::list_modules(
735             "BorderStyle::", {list_modules=>1, recurse=>1});
736             my @res;
737 2         6 for (sort keys %$mods) {
738             s/\ABorderStyle:://;
739 2         4 push @res, $_;
740             }
741             @res;
742             }
743 0     0 1 0  
744 0         0 require Module::List;
745 0 0       0 my ($self) = @_;
746              
747             my $mods = Module::List::list_modules(
748 0         0 "ColorTheme::", {list_modules=>1, recurse=>1});
  0         0  
749 0         0 my @res;
750 0         0 for (sort keys %$mods) {
751             s/\AColorTheme:://;
752 0 0       0 push @res, $_;
  0         0  
753 0         0 }
754 0         0 @res;
755             }
756              
757             require Module::List;
758 0     0 1 0 require Module::Load;
759 0         0 require Package::MoreUtil;
760              
761 0         0 my ($self, $detail) = @_;
762              
763 0         0 my $prefix = (ref($self) ? ref($self) : $self ) .
764 0         0 '::StyleSet'; # XXX allow override
765 0         0 my $all_sets = $self->{_all_style_sets};
766 0         0  
767             if (!$all_sets) {
768 0         0 my $mods = Module::List::list_modules("$prefix\::",
769             {list_modules=>1, recurse=>1});
770             $all_sets = {};
771             for my $mod (sort keys %$mods) {
772 0     0 1 0 #$log->tracef("Loading style set module '%s' ...", $mod);
773 0         0 Module::Load::load($mod);
774             my $name = $mod; $name =~ s/\A\Q$prefix\:://;
775 0         0 my $summary = $mod->summary;
776             # we don't have meta, so dig it ourselves
777 0         0 my %ct = Package::MoreUtil::list_package_contents($mod);
778 0         0 my $args = [sort grep {!/\W/ && !/\A(new|summary|apply)\z/}
779 0         0 keys %ct];
780 0         0 $all_sets->{$name} = {name=>$name, summary=>$summary, args=>$args};
781             }
782 0         0 $self->{_all_style_sets} = $all_sets;
783             }
784              
785             if ($detail) {
786 0     0 1 0 return $all_sets;
787 0         0 } else {
788 0         0 return (sort keys %$all_sets);
789             }
790 0         0 }
791              
792 0 0       0 # read environment variables for style, this will only be done once per object
793             my $self = shift;
794 0         0  
795             return if $self->{_read_style_envs}++;
796 0 0       0  
797 0         0 if ($ENV{ANSITABLE_COLUMN_STYLES}) {
798             require JSON::MaybeXS;
799 0         0 my $ss = JSON::MaybeXS::decode_json($ENV{ANSITABLE_COLUMN_STYLES});
800 0         0 croak "ANSITABLE_COLUMN_STYLES must be a hash"
801             unless ref($ss) eq 'HASH';
802 0         0 for my $col (keys %$ss) {
803 0         0 my $ci = $self->_colnum($col);
  0         0  
804 0         0 my $s = $ss->{$col};
805             for my $k (keys %$s) {
806 0         0 my $v = $s->{$k};
807 0   0     0 croak "Unknown column style '$k' (for column $col) in ".
  0         0  
808             "ANSITABLE_COLUMN_STYLES environment, ".
809 0         0 "please use one of [".join(", ", @$COLUMN_STYLES)."]"
810             unless $k ~~ $COLUMN_STYLES;
811 0         0 $self->{_column_styles}[$ci]{$k} //= $v;
812             }
813             }
814 0 0       0 }
815 0         0  
816             if ($ENV{ANSITABLE_ROW_STYLES}) {
817 0         0 require JSON::MaybeXS;
818             my $ss = JSON::MaybeXS::decode_json($ENV{ANSITABLE_ROW_STYLES});
819             croak "ANSITABLE_ROW_STYLES must be a hash"
820             unless ref($ss) eq 'HASH';
821             for my $row (keys %$ss) {
822             my $s = $ss->{$row};
823 1     1   2 for my $k (keys %$s) {
824             my $v = $s->{$k};
825 1 50       5 croak "Unknown row style '$k' (for row $row) in ".
826             "ANSITABLE_ROW_STYLES environment, ".
827 1 50       5 "please use one of [".join(", ", @$ROW_STYLES)."]"
828 0         0 unless $k ~~ $ROW_STYLES;
829 0         0 $self->{_row_styles}[$row]{$k} //= $v;
830 0 0       0 }
831             }
832 0         0 }
833 0         0  
834 0         0 if ($ENV{ANSITABLE_CELL_STYLES}) {
835 0         0 require JSON::MaybeXS;
836 0         0 my $ss = JSON::MaybeXS::decode_json($ENV{ANSITABLE_CELL_STYLES});
837 0 0       0 croak "ANSITABLE_CELL_STYLES must be a hash"
838             unless ref($ss) eq 'HASH';
839             for my $cell (keys %$ss) {
840             croak "Invalid cell specification in ANSITABLE_CELL_STYLES: ".
841 0   0     0 "$cell, please use 'row,col'"
842             unless $cell =~ /^(.+),(.+)$/;
843             my $row = $1;
844             my $col = $2;
845             my $ci = $self->_colnum($col);
846 1 50       3 my $s = $ss->{$cell};
847 0         0 for my $k (keys %$s) {
848 0         0 my $v = $s->{$k};
849 0 0       0 croak "Unknown cell style '$k' (for cell $row,$col) in ".
850             "ANSITABLE_CELL_STYLES environment, ".
851 0         0 "please use one of [".join(", ", @$CELL_STYLES)."]"
852 0         0 unless $k ~~ $CELL_STYLES;
853 0         0 $self->{_cell_styles}[$row][$ci]{$k} //= $v;
854 0         0 }
855 0 0       0 }
856             }
857             }
858              
859 0   0     0 # determine which columns to show (due to column_filter)
860             my $self = shift;
861              
862             my $cols = $self->{columns};
863             my $cf = $self->{column_filter};
864 1 50       4  
865 0         0 my $fcols;
866 0         0 if (ref($cf) eq 'CODE') {
867 0 0       0 $fcols = [grep {$cf->($_)} @$cols];
868             } elsif (ref($cf) eq 'ARRAY') {
869 0         0 $fcols = [grep {defined} map {looks_like_number($_) ?
870 0 0       0 $cols->[$_] : $_} @$cf];
871             } else {
872             $fcols = $cols;
873 0         0 }
874 0         0 $self->{_draw}{fcols} = $fcols;
875 0         0 }
876 0         0  
877 0         0 # calculate widths/heights of header, store width settings, column [lr]pads
878 0         0 my $self = shift;
879 0 0       0  
880             my $cols = $self->{columns};
881             my $fcols = $self->{_draw}{fcols};
882              
883 0   0     0 my $fcol_widths = []; # index = [colnum]
884             my $header_height = 1;
885             my $fcol_lpads = []; # index = [colnum]
886             my $fcol_rpads = []; # ditto
887             my $fcol_setwidths = []; # index = [colnum], from cell_width/col width
888             my $frow_setheights = []; # index = [frownum], from cell_height/row height
889              
890             my %seen;
891 1     1   2 my $lpad = $self->{cell_lpad} // $self->{cell_pad}; # tbl-lvl leftp
892             my $rpad = $self->{cell_rpad} // $self->{cell_pad}; # tbl-lvl rightp
893 1         2 for my $i (0..@$cols-1) {
894 1         2 next unless $cols->[$i] ~~ $fcols;
895             next if $seen{$cols->[$i]}++;
896 1         2  
897 1 50       4 $fcol_setwidths->[$i] = $self->get_eff_column_style($i, 'width') //
    50          
898 0         0 $self->{cell_width};
  0         0  
899             my $wh = $self->_opt_calc_cell_width_height(undef, $i, $cols->[$i]);
900 0 0       0 $fcol_widths->[$i] = $wh->[0];
  0         0  
  0         0  
901             $header_height = $wh->[1]
902             if !defined($header_height) || $header_height < $wh->[1];
903 1         27 $fcol_lpads->[$i] = $self->get_eff_column_style($i, 'lpad') //
904             $self->get_eff_column_style($i, 'pad') // $lpad;
905 1         4 $fcol_rpads->[$i] = $self->get_eff_column_style($i, 'rpad') //
906             $self->get_eff_column_style($i, 'pad') // $rpad;
907             }
908              
909             $self->{_draw}{header_height} = $header_height;
910 1     1   16 $self->{_draw}{fcol_lpads} = $fcol_lpads;
911             $self->{_draw}{fcol_rpads} = $fcol_rpads;
912 1         4 $self->{_draw}{fcol_setwidths} = $fcol_setwidths;
913 1         2 $self->{_draw}{frow_setheights} = $frow_setheights;
914             $self->{_draw}{fcol_widths} = $fcol_widths;
915 1         2 }
916 1         2  
917 1         2 # determine which rows to show, calculate vertical paddings of data rows, store
918 1         1 # height settings
919 1         2 my $self = shift;
920 1         2  
921             my $rows = $self->{rows};
922 1         2 my $rf = $self->{row_filter};
923 1   33     7 my $frow_setheights = $self->{_draw}{frow_setheights};
924 1   33     5  
925 1         8 my $frow_tpads = []; # index = [frownum]
926 1 50       5 my $frow_bpads = []; # ditto
927 1 50       6 my $frows = [];
928             my $frow_separators = [];
929             my $frow_orig_indices = []; # needed when accessing original row data
930 1   33     8  
931 1         9 my $tpad = $self->{cell_tpad} // $self->{cell_vpad}; # tbl-lvl top pad
932 1         2 my $bpad = $self->{cell_bpad} // $self->{cell_vpad}; # tbl-lvl botom pad
933 1 50 33     11 my $i = -1;
934             my $j = -1;
935 1   33     4 for my $row (@$rows) {
      33        
936             $i++;
937 1   33     6 if (ref($rf) eq 'CODE') {
      33        
938             next unless $rf->($row, $i);
939             } elsif ($rf) {
940             next unless $i ~~ $rf;
941 1         2 }
942 1         2 $j++;
943 1         3 push @$frow_setheights, $self->get_eff_row_style($i, 'height') //
944 1         2 $self->{cell_height};
945 1         1 push @$frows, [@$row]; # 1-level clone, for storing formatted values
946 1         3 push @$frow_separators, $j if $i ~~ $self->{_row_separators};
947             push @$frow_tpads, $self->get_eff_row_style($i, 'tpad') //
948             $self->get_eff_row_style($i, 'vpad') // $tpad;
949             push @$frow_bpads, $self->get_eff_row_style($i, 'bpad') //
950             $self->get_eff_row_style($i, 'vpad') // $bpad;
951             push @$frow_orig_indices, $i;
952 1     1   2 }
953              
954 1         2 $self->{_draw}{frows} = $frows;
955 1         2 $self->{_draw}{frow_separators} = $frow_separators;
956 1         2 $self->{_draw}{frow_tpads} = $frow_tpads;
957             $self->{_draw}{frow_bpads} = $frow_bpads;
958 1         2 $self->{_draw}{frow_orig_indices} = $frow_orig_indices;
959 1         2 }
960 1         1  
961 1         2 # detect column type from data/header name. assign default column align, valign,
962 1         2 # fgcolor, bgcolor, formats.
963             my $self = shift;
964 1   33     5  
965 1   33     8 my $cols = $self->{columns};
966 1         2 my $rows = $self->{rows};
967 1         2  
968 1         3 my $fcol_detect = [];
969 2         3 my %seen;
970 2 50       6 for my $i (0..@$cols-1) {
    50          
971 0 0       0 my $col = $cols->[$i];
972             my $res = {};
973 0 0       0 $fcol_detect->[$i] = $res;
974              
975 2         3 # optim: skip detecting columns we're not showing
976             next unless $col ~~ $self->{_draw}{fcols};
977 2   33     9  
978 2         5 # but detect from all rows, not just ones we're showing
979 2 50       6 my $type = $self->get_eff_column_style($col, 'type');
980 2   33     4 my $subtype;
      33        
981             DETECT:
982 2   33     6 {
      33        
983             last DETECT if $type;
984 2         5 if ($col =~ /^(can|is|has|does)_|\?$/) {
985             $type = 'bool';
986             last DETECT;
987 1         3 }
988 1         2  
989 1         2 require Parse::VarName;
990 1         2 my @words = map {lc} @{ Parse::VarName::split_varname_words(
991 1         2 varname=>$col) };
992             for (qw/date time ctime mtime utime atime stime/) {
993             if ($_ ~~ @words) {
994             $type = 'date';
995             last DETECT;
996             }
997 1     1   2 }
998              
999 1         2 my $pass = 1;
1000 1         2 for my $j (0..@$rows) {
1001             my $v = $rows->[$j][$i];
1002 1         2 next unless defined($v);
1003 1         2 do { $pass=0; last } unless looks_like_number($v);
1004 1         8 }
1005 1         3 if ($pass) {
1006 1         2 $type = 'num';
1007 1         2 if ($col =~ /(pct|percent(?:age))\b|\%/) {
1008             $subtype = 'pct';
1009             }
1010 1 50       5 last DETECT;
1011             }
1012             $type = 'str';
1013 1         3 } # DETECT
1014 1         2  
1015             $res->{type} = $type;
1016             if ($type eq 'bool') {
1017 1 50       2 $res->{align} = 'center';
  1         2  
1018 1 50       7 $res->{valign} = 'center';
1019 0         0 $res->{fgcolor} = $self->{color_theme_obj}->get_item_color('bool_data');
1020 0         0 $res->{formats} = [[bool => {style => $self->{use_utf8} ?
1021             "check_cross" : "Y_N"}]];
1022             } elsif ($type eq 'date') {
1023 1         374 $res->{align} = 'middle';
1024 1         336 $res->{fgcolor} = $self->{color_theme_obj}->get_item_color('date_data');
  1         29  
  1         3  
1025             $res->{formats} = [['date' => {}]];
1026 1         3 } elsif ($type =~ /\A(num|float|int)\z/) {
1027 7 50       15 $res->{align} = 'right';
1028 0         0 $res->{fgcolor} = $self->{color_theme_obj}->get_item_color('num_data');
1029 0         0 if (($subtype//"") eq 'pct') {
1030             $res->{formats} = [[num => {style=>'percent'}]];
1031             }
1032             } else {
1033 1         2 $res->{fgcolor} = $self->{color_theme_obj}->get_item_color('str_data');
1034 1         4 $res->{wrap} = $ENV{WRAP} // 1;
1035 1         3 }
1036 1 50       2 }
1037 1 50       5  
  1         1  
  1         3  
1038             #use Data::Dump; print "D:fcol_detect: "; dd $fcol_detect;
1039 1 50       2 $self->{_draw}{fcol_detect} = $fcol_detect;
1040 0         0 }
1041 0 0       0  
1042 0         0 # calculate width and height of a cell, but skip calculating (to save some
1043             # cycles) if width is already set by frow_setheights / fcol_setwidths.
1044 0         0 my ($self, $frownum, $col, $text) = @_;
1045              
1046 1         3 $col = $self->_colnum($col);
1047             my $setw = $self->{_draw}{fcol_setwidths}[$col];
1048             my $calcw = !defined($setw) || $setw < 0;
1049 1         3 my $seth = defined($frownum) ?
1050 1 50       18 $self->{_draw}{frow_setheights}[$frownum] : undef;
    50          
    50          
1051 0         0 my $calch = !defined($seth) || $seth < 0;
1052 0         0  
1053 0         0 my $wh;
1054             if ($calcw) {
1055 0 0       0 $wh = $self->{_func_length_height}->($text);
1056             $wh->[0] = -$setw if defined($setw) && $setw<0 && $wh->[0] < -$setw;
1057 0         0 $wh->[1] = $seth if !$calch;
1058 0         0 $wh->[1] = -$seth if defined($seth) && $seth<0 && $wh->[1] < -$seth;
1059 0         0 } elsif ($calch) {
1060             my $h = 1; $h++ while $text =~ /\n/go;
1061 0         0 $h = -$seth if defined($seth) && $seth<0 && $h < -$seth;
1062 0         0 $wh = [$setw, $h];
1063 0 0 0     0 } else {
1064 0         0 $wh = [$setw, $seth];
1065             }
1066             #say "D:_opt_calc_cell_width_height(", $frownum//"undef", ", $col) = $wh->[0], $wh->[1]";
1067 1         13 $wh;
1068 1   50     28 }
1069              
1070             my $self = shift;
1071              
1072             my $cols = $self->{columns};
1073 1         3 my $frows = $self->{_draw}{frows};
1074             my $fcols = $self->{_draw}{fcols};
1075             my $fcol_detect = $self->{_draw}{fcol_detect};
1076              
1077             my %seen;
1078             for my $i (0..@$cols-1) {
1079 3     3   9 next unless $cols->[$i] ~~ $fcols;
1080             next if $seen{$cols->[$i]}++;
1081 3         6 my @fmts = @{ $self->get_eff_column_style($i, 'formats') //
1082 3         7 $fcol_detect->[$i]{formats} // [] };
1083 3   33     9 if (@fmts) {
1084             require Data::Unixish::Apply;
1085 3 100       7 my $res = Data::Unixish::Apply::apply(
1086 3   33     7 in => [map {$frows->[$_][$i]} 0..@$frows-1],
1087             functions => \@fmts,
1088 3         3 );
1089 3 50       5 croak "Can't format column $cols->[$i]: $res->[0] - $res->[1]"
    0          
1090 3         10 unless $res->[0] == 200;
1091 3 0 33     154 $res = $res->[2];
      33        
1092 3 50       7 for (0..@$frows-1) { $frows->[$_][$i] = $res->[$_] // "" }
1093 3 0 33     7 } else {
      33        
1094             # change null to ''
1095 0         0 for (0..@$frows-1) { $frows->[$_][$i] //= "" }
  0         0  
1096 0 0 0     0 }
      0        
1097 0         0 }
1098             }
1099 0         0  
1100             my $self = shift;
1101              
1102 3         5 my $cols = $self->{columns};
1103             my $rows = $self->{rows};
1104             my $fcols = $self->{_draw}{fcols};
1105             my $frows = $self->{_draw}{frows};
1106 1     1   2 my $frow_orig_indices = $self->{_draw}{frow_orig_indices};
1107              
1108 1         2 for my $i (0..@$frows-1) {
1109 1         3 my %seen;
1110 1         2 my $origi = $frow_orig_indices->[$i];
1111 1         2 for my $j (0..@$cols-1) {
1112             next unless $cols->[$j] ~~ $fcols;
1113 1         2 next if $seen{$cols->[$j]}++;
1114 1         7  
1115 1 50       7 my $fmts = $self->get_eff_cell_style($origi, $j, 'formats');
1116 1 50       4 if (defined $fmts) {
1117 1         2 require Data::Unixish::Apply;
1118 1   33     3 my $res = Data::Unixish::Apply::apply(
      50        
1119 1 50       3 in => [ $frows->[$i][$j] ],
1120 0         0 functions => $fmts,
1121             );
1122 0         0 croak "Can't format cell ($origi, $cols->[$j]): ".
  0         0  
1123             "$res->[0] - $res->[1]" unless $res->[0] == 200;
1124             $frows->[$i][$j] = $res->[2][0] // "";
1125 0 0       0 }
1126             } # col
1127 0         0 }
1128 0   0     0 }
  0         0  
1129              
1130             my $self = shift;
1131 1   50     7  
  2         8  
1132             my $cols = $self->{columns};
1133             my $fcols = $self->{_draw}{fcols};
1134             my $frows = $self->{_draw}{frows};
1135              
1136             my $frow_heights = [];
1137 1     1   3 my $fcol_widths = $self->{_draw}{fcol_widths};
1138             my $frow_orig_indices = $self->{_draw}{frow_orig_indices};
1139 1         2  
1140 1         2 my $height = $self->{cell_height};
1141 1         2 my $tpad = $self->{cell_tpad} // $self->{cell_vpad}; # tbl-lvl tpad
1142 1         2 my $bpad = $self->{cell_bpad} // $self->{cell_vpad}; # tbl-lvl bpad
1143 1         2 my $cswidths = [map {$self->get_eff_column_style($_, 'width')} 0..@$cols-1];
1144             for my $i (0..@$frows-1) {
1145 1         3 my %seen;
1146 2         4 my $origi = $frow_orig_indices->[$i];
1147 2         5 my $rsheight = $self->get_eff_row_style($origi, 'height');
1148 2         5 for my $j (0..@$cols-1) {
1149 2 50       8 next unless $cols->[$j] ~~ $fcols;
1150 2 50       8 next if $seen{$cols->[$j]}++;
1151              
1152 2         13 my $wh = $self->_opt_calc_cell_width_height($i,$j,$frows->[$i][$j]);
1153 2 50       5  
1154 2         11 $fcol_widths->[$j] = $wh->[0] if $fcol_widths->[$j] < $wh->[0];
1155 2         10 $frow_heights->[$i] = $wh->[1] if !defined($frow_heights->[$i])
1156             || $frow_heights->[$i] < $wh->[1];
1157             } # col
1158             }
1159 2 50       23381 $self->{_draw}{frow_heights} = $frow_heights;
1160             }
1161 2   50     20  
1162             my $self = shift;
1163              
1164             my $cols = $self->{columns};
1165             my $fcols = $self->{_draw}{fcols};
1166             my $frows = $self->{_draw}{frows};
1167             my $fcol_detect = $self->{_draw}{fcol_detect};
1168 1     1   2 my $fcol_setwidths = $self->{_draw}{fcol_setwidths};
1169              
1170 1         4 my %seen;
1171 1         2 for my $i (0..@$cols-1) {
1172 1         2 next unless $cols->[$i] ~~ $fcols;
1173             next if $seen{$cols->[$i]}++;
1174 1         3  
1175 1         2 if (($self->get_eff_column_style($i, 'wrap') // $self->{column_wrap} //
1176 1         3 $fcol_detect->[$i]{wrap}) &&
1177             defined($fcol_setwidths->[$i]) &&
1178 1         2 $fcol_setwidths->[$i]>0) {
1179 1   33     11 for (0..@$frows-1) {
1180 1   33     7 $frows->[$_][$i] = $self->{_func_wrap}->(
1181 1         5 $frows->[$_][$i], $fcol_setwidths->[$i]);
  1         3  
1182 1         6 }
1183 2         4 }
1184 2         4 }
1185 2         6 }
1186 2         5  
1187 2 50       7 my $self = shift;
1188 2 50       8  
1189             my $cols = $self->{columns};
1190 2         6 my $fcols = $self->{_draw}{fcols};
1191             my $frows = $self->{_draw}{frows};
1192 2 50       7 my $fcol_widths = $self->{_draw}{fcol_widths};
1193 2 50 33     12 my $fcol_lpads = $self->{_draw}{fcol_lpads};
1194             my $fcol_rpads = $self->{_draw}{fcol_rpads};
1195             my $frow_tpads = $self->{_draw}{frow_tpads};
1196             my $frow_bpads = $self->{_draw}{frow_bpads};
1197 1         3 my $frow_heights = $self->{_draw}{frow_heights};
1198              
1199             my $w = 0;
1200             $w += 1 if length($self->{border_style_obj}->get_border_char(char=>'v_l'));
1201 1     1   3 my $has_vsep = length($self->{border_style_obj}->get_border_char(char=>'v_i'));
1202             for my $i (0..@$cols-1) {
1203 1         3 next unless $cols->[$i] ~~ $fcols;
1204 1         3 $w += $fcol_lpads->[$i] + $fcol_widths->[$i] + $fcol_rpads->[$i];
1205 1         2 if ($i < @$cols-1) {
1206 1         4 $w += 1 if $has_vsep;
1207 1         3 }
1208             }
1209 1         2 $w += 1 if length($self->{border_style_obj}->get_border_char(char=>'v_r'));
1210 1         8 $self->{_draw}{table_width} = $w;
1211 1 50       7  
1212 1 50       5 my $h = 0;
1213             $h += 1 if length($self->{border_style_obj}->get_border_char(char=>'rd_t')); # top border line
1214 1 50 33     5 $h += $self->{header_tpad} // $self->{header_vpad} //
      33        
      33        
      33        
1215             $self->{cell_tpad} // $self->{cell_vpad};
1216             $h += $self->{_draw}{header_height} // 0;
1217             $h += $self->{header_bpad} // $self->{header_vpad} //
1218 0         0 $self->{cell_bpad} // $self->{cell_vpad};
1219 0         0 $h += 1 if length($self->{border_style_obj}->get_border_char(char=>'rv_l'));
1220             for my $i (0..@$frows-1) {
1221             $h += ($frow_tpads->[$i] // 0) +
1222             ($frow_heights->[$i] // 0) +
1223             ($frow_bpads->[$i] // 0);
1224             $h += 1 if $self->_should_draw_row_separator($i);
1225             }
1226             $h += 1 if length($self->{border_style_obj}->get_border_char(char=>'ru_b'));
1227 1     1   3 $self->{_draw}{table_height} = $h;
1228             }
1229 1         4  
1230 1         2 # if there are text columns with no width set, and the column width is wider
1231 1         2 # than terminal, try to adjust widths so it fit into the terminal, if possible.
1232 1         2 # return 1 if widths (fcol_widths) adjusted.
1233 1         2 my $self = shift;
1234 1         2  
1235 1         3 # try to find wrappable columns that do not have their widths set. currently
1236 1         3 # the algorithm is not proper, it just targets columns which are wider than
1237 1         2 # a hard-coded value (30). it should take into account the longest word in
1238             # the content/header, but this will require another pass at the text to
1239 1         2 # analyze it.
1240 1 50       31  
1241 1         176 my $fcols = $self->{_draw}{fcols};
1242 1         82 my $frows = $self->{_draw}{frows};
1243 1 50       7 my $fcol_setwidths = $self->{_draw}{fcol_setwidths};
1244 1         10 my $fcol_detect = $self->{_draw}{fcol_detect};
1245 1 50       6 my $fcol_widths = $self->{_draw}{fcol_widths};
1246 0 0       0 my %acols;
1247             my %origw;
1248             for my $i (0..@$fcols-1) {
1249 1 50       22 my $ci = $self->_colnum($fcols->[$i]);
1250 1         59 next if defined($fcol_setwidths->[$ci]) && $fcol_setwidths->[$ci]>0;
1251             next if $fcol_widths->[$ci] < 30;
1252 1         3 next unless $self->get_eff_column_style($ci, 'wrap') //
1253 1 50       17 $self->{column_wrap} // $fcol_detect->[$ci]{wrap};
1254             $acols{$ci}++;
1255 1   33     80 $origw{$ci} = $fcol_widths->[$ci];
      33        
      33        
1256 1   50     5 }
1257             return 0 unless %acols;
1258 1   33     15  
      33        
      33        
1259 1 50       22 # only do this if table width exceeds terminal width
1260 1         59 my $termw = $self->term_width;
1261 2   50     19 return 0 unless $termw > 0;
      50        
      50        
1262             my $excess = $self->{_draw}{table_width} - $termw;
1263             return 0 unless $excess > 0;
1264 2 50       12  
1265             # reduce text columns proportionally
1266 1 50       23 my $w = 0; # total width of all to-be-adjusted columns
1267 1         65 $w += $fcol_widths->[$_] for keys %acols;
1268             return 0 unless $w > 0;
1269             my $reduced = 0;
1270             REDUCE:
1271             while (1) {
1272             my $has_reduced;
1273             for my $ci (keys %acols) {
1274 1     1   4 last REDUCE if $reduced >= $excess;
1275             if ($fcol_widths->[$ci] > 30) {
1276             $fcol_widths->[$ci]--;
1277             $reduced++;
1278             $has_reduced++;
1279             }
1280             }
1281             last if !$has_reduced;
1282 1         2 }
1283 1         3  
1284 1         2 # reset widths
1285 1         52 for my $ci (keys %acols) {
1286 1         4 $fcol_setwidths->[$ci] = $fcol_widths->[$ci];
1287 1         2 $fcol_widths->[$ci] = 0; # reset
1288             }
1289 1         5  
1290 1         4 # wrap and set setwidths so it doesn't grow again during recalculate
1291 1 50 33     38 for my $ci (keys %acols) {
1292 1 50       8 next unless $origw{$ci} != $fcol_widths->[$ci];
1293             for (0..@$frows-1) {
1294 0 0 0     0 $frows->[$_][$ci] = $self->{_func_wrap}->(
      0        
1295 0         0 $frows->[$_][$ci], $fcol_setwidths->[$ci]);
1296 0         0 }
1297             }
1298 1 50       4  
1299             # recalculate column widths
1300             $self->_calc_row_widths_heights;
1301 0         0 $self->_calc_table_width_height;
1302 0 0       0 1;
1303 0         0 }
1304 0 0       0  
1305             # filter columns & rows, calculate widths/paddings, format data, put the results
1306             # in _draw (draw data) attribute.
1307 0         0 my $self = shift;
1308 0         0  
1309 0 0       0 $self->{_draw} = {};
1310 0         0  
1311             $self->_read_style_envs;
1312 0         0 $self->_calc_fcols;
1313 0         0 $self->_calc_header_height;
1314 0         0 $self->_calc_frows;
1315 0 0       0 $self->_detect_column_types;
1316 0 0       0 $self->_apply_column_formats;
1317 0         0 $self->_apply_cell_formats;
1318 0         0 $self->_wrap_wrappable_columns;
1319 0         0 $self->_calc_row_widths_heights;
1320             $self->_calc_table_width_height;
1321             $self->_adjust_column_widths;
1322 0 0       0 }
1323              
1324             # push string into the drawing buffer. also updates "cursor" position.
1325             my $self = shift;
1326 0         0 # currently x position is not recorded because this involves doing
1327 0         0 # ta_mbswidth() (or ta_mbswidth_height()) for every string, which is rather
1328 0         0 # expensive. so only the y position is recorded by counting newlines.
1329              
1330             for (@_) {
1331             my $num_nl = 0;
1332 0         0 $num_nl++ while /\r?\n/og;
1333 0 0       0 push @{$self->{_draw}{buf}}, $_;
1334 0         0 $self->{_draw}{y} += $num_nl;
1335 0         0 }
1336             $self;
1337             }
1338              
1339             my $self = shift;
1340             my $c = $self->_color_theme_item_color_to_ansi(@_);
1341 0         0 $self->draw_str($c) if length($c);
1342 0         0 }
1343 0         0  
1344             my $self = shift;
1345             return "" unless $self->use_color;
1346             return "" if $self->{color_theme_obj}->get_struct->{_no_color};
1347             "\e[0m";
1348             }
1349 1     1   3  
1350             my $self = shift;
1351 1         3 my $c = $self->get_color_reset;
1352             $self->draw_str($c) if length($c);
1353 1         4 }
1354 1         3  
1355 1         3 # draw border character(s). drawing border character involves setting border
1356 1         4 # color, aside from drawing the actual characters themselves. arguments are list
1357 1         3 # of (y, x, n) tuples where y and x are the row and col number of border
1358 1         5 # character, n is the number of characters to print. n defaults to 1 if not
1359 1         7 # specified.
1360 1         11 my $self = shift;
1361 1         5 my $args; $args = shift if ref($_[0]) eq 'HASH';
1362 1         10  
1363 1         8 while (my ($name, $n) = splice @_, 0, 2) {
1364             $n //= 1;
1365             if (!$self->{use_color}) {
1366             # save some CPU cycles
1367             } elsif ($args) {
1368 24     24 0 839 $self->draw_theme_color('border',
1369             {table=>$self, border=>[$name, $n], %$args});
1370             } else {
1371             $self->draw_theme_color('border',
1372             {table=>$self, border=>[$name, $n]});
1373 24         40 }
1374 24         26 $self->draw_str($self->{border_style_obj}->get_border_char(char=>$name, repeat=>$n));
1375 24         76 $self->draw_color_reset;
1376 24         27 }
  24         55  
1377 24         37 }
1378              
1379 24         32 my ($self, $i) = @_;
1380              
1381             return $i < @{$self->{_draw}{frows}}-1 &&
1382             (($self->{show_row_separator}==2 && $i~~$self->{_draw}{frow_separators})
1383 0     0 0 0 || $self->{show_row_separator}==1);
1384 0         0 }
1385 0 0       0  
1386             # apply align/valign, apply padding, apply default fgcolor/bgcolor to text,
1387             # truncate to specified cell's width & height
1388             my $self = shift;
1389 18     18 0 20 #say "D: get_cell_lines ".join(", ", map{$_//""} @_);
1390 18 50       276 my ($text, $width, $height, $align, $valign,
1391 0 0       0 $lpad, $rpad, $tpad, $bpad, $color) = @_;
1392 0         0  
1393             my @lines;
1394             push @lines, "" for 1..$tpad;
1395             my @dlines = split(/\r?\n/, $text);
1396 18     18 0 20 @dlines = ("") unless @dlines;
1397 18         34 my ($la, $lb);
1398 18 50       137 $valign //= 'top';
1399             if ($valign =~ /^[Bb]/o) { # bottom
1400             $la = $height-@dlines;
1401             $lb = 0;
1402             } elsif ($valign =~ /^[MmCc]/o) { # middle/center
1403             $la = int(($height-@dlines)/2);
1404             $lb = $height-@dlines-$la;
1405             } else { # top
1406             $la = 0;
1407 9     9 0 13 $lb = $height-@dlines;
1408 9 100       9 }
  9         21  
1409             push @lines, "" for 1..$la;
1410 9         38 push @lines, @dlines;
1411 15   100     40 push @lines, "" for 1..$lb;
1412 15 50       31 push @lines, "" for 1..$bpad;
    0          
1413              
1414             $align //= 'left';
1415 0         0 my $pad = $align =~ /^[Ll]/o ? "right" :
1416             ($align =~ /^[Rr]/o ? "left" : "center");
1417              
1418 0         0 for (@lines) {
1419             $_ = (" "x$lpad) . $self->{_func_pad}->($_, $width, $pad, " ", 1) . (" "x$rpad);
1420             if ($self->{use_color}) {
1421 15         286 # add default color
1422 15         26 s/\e\[0m(?=.)/\e[0m$color/g if length($color);
1423             $_ = $color . $_;
1424             }
1425             }
1426              
1427 4     4   10 \@lines;
1428             }
1429              
1430             my ($self, $i) = @_;
1431 4   66     5  
1432             my $ct = $self->{color_theme};
1433              
1434             my $tmp;
1435             my $fgcolor;
1436             if (defined $self->{header_fgcolor}) {
1437 3     3   12 $fgcolor = item_color_to_ansi($self->{header_fgcolor});
1438             } elsif (defined $self->{cell_fgcolor}) {
1439 3         11 $fgcolor = item_color_to_ansi($self->{cell_fgcolor});
1440             #} elsif (defined $self->{_draw}{fcol_detect}[$i]{fgcolor}) {
1441             # $fgcolor = item_color_to_ansi($self->{_draw}{fcol_detect}[$i]{fgcolor});
1442 3         4 } elsif ($tmp = $self->_color_theme_item_color_to_ansi('header')) {
1443 3         8 $fgcolor = $tmp;
1444 3         9 } elsif ($tmp = $self->_color_theme_item_color_to_ansi('cell')) {
1445 3 50       8 $fgcolor = $tmp;
1446 3         4 } else {
1447 3   50     6 $fgcolor = "";
1448 3 50       22 }
    50          
1449 0         0  
1450 0         0 my $bgcolor;
1451             if (defined $self->{header_bgcolor}) {
1452 0         0 $bgcolor = item_color_to_ansi($self->{header_bgcolor}, 'bg');
1453 0         0 } elsif (defined $self->{cell_bgcolor}) {
1454             $bgcolor = item_color_to_ansi($self->{cell_bgcolor}, 'bg');
1455 3         6 } elsif (defined $self->{_draw}{fcol_detect}[$i]{bgcolor}) {
1456 3         5 $bgcolor = item_color_to_ansi($self->{_draw}{fcol_detect}[$i]{bgcolor}, 'bg');
1457             } elsif ($tmp = $self->_color_theme_item_color_to_ansi('header_bg', undef, 'bg')) {
1458 3         5 $bgcolor = $tmp;
1459 3         7 } elsif ($tmp = $self->_color_theme_item_color_to_ansi('cell_bg', undef, 'bg')) {
1460 3         5 $bgcolor = $tmp;
1461 3         5 } else {
1462             $bgcolor = "";
1463 3   50     6 }
1464 3 0       16  
    50          
1465             my $align =
1466             $self->{header_align} //
1467 3         7 $self->{cell_align} //
1468 3         13 $self->{_draw}{fcol_detect}[$i]{align} //
1469 3 50       140 'left';
1470             my $valign =
1471 0 0       0 $self->{header_valign} //
1472 0         0 $self->{cell_valign} //
1473             $self->{_draw}{fcol_detect}[$i]{valign} //
1474             'top';
1475              
1476 3         12 my $lpad = $self->{_draw}{fcol_lpads}[$i];
1477             my $rpad = $self->{_draw}{fcol_rpads}[$i];
1478             my $tpad = $self->{header_tpad} // $self->{header_vpad} // 0;
1479             my $bpad = $self->{header_bpad} // $self->{header_vpad} // 0;
1480 1     1   3  
1481             #use Data::Dump; print "D:header cell: "; dd {i=>$i, col=>$self->{columns}[$i], fgcolor=>$fgcolor, bgcolor=>$bgcolor};
1482 1         3 my $res = $self->_get_cell_lines(
1483             $self->{columns}[$i], # text
1484 1         1 $self->{_draw}{fcol_widths}[$i], # width
1485             $self->{_draw}{header_height}, # height
1486 1 50       8 $align, $valign, # aligns
    50          
    50          
    50          
1487 0         0 $lpad, $rpad, $tpad, $bpad, # paddings
1488             $fgcolor . $bgcolor);
1489 0         0 #use Data::Dump; print "D:res: "; dd $res;
1490             $res;
1491             }
1492              
1493 0         0 my ($self, $y, $x) = @_;
1494              
1495 0         0 my $ct = $self->{color_theme};
1496             my $oy = $self->{_draw}{frow_orig_indices}[$y];
1497 1         43 my $cell = $self->{_draw}{frows}[$y][$x];
1498             my $args = {table=>$self, rownum=>$y, colnum=>$x, data=>$cell,
1499             orig_data=>$self->{rows}[$oy][$x]};
1500 1         3  
1501 1 50       14 my $tmp;
    50          
    50          
    50          
    50          
1502 0         0 my $fgcolor;
1503             if (defined ($tmp = $self->get_eff_cell_style($oy, $x, 'fgcolor'))) {
1504 0         0 $fgcolor = item_color_to_ansi($tmp);
1505             } elsif (defined ($tmp = $self->get_eff_row_style($oy, 'fgcolor'))) {
1506 0         0 $fgcolor = item_color_to_ansi($tmp);
1507             } elsif (defined ($tmp = $self->get_eff_column_style($x, 'fgcolor'))) {
1508 0         0 $fgcolor = item_color_to_ansi($tmp);
1509             } elsif (defined ($tmp = $self->{cell_fgcolor})) {
1510 0         0 $fgcolor = item_color_to_ansi($tmp);
1511             } elsif (defined ($tmp = $self->{_draw}{fcol_detect}[$x]{fgcolor})) {
1512 1         16 $fgcolor = item_color_to_ansi($tmp);
1513             } elsif ($tmp = $self->_color_theme_item_color_to_ansi('cell', $args)) {
1514             $fgcolor = $tmp;
1515             } else {
1516             $fgcolor = "";
1517             }
1518              
1519 1   33     15 my $bgcolor;
      33        
      50        
1520             if (defined ($tmp = $self->get_eff_cell_style($oy, $x, 'bgcolor'))) {
1521             $bgcolor = item_color_to_ansi($tmp, 'bg');
1522             } elsif (defined ($tmp = $self->get_eff_row_style($oy, 'bgcolor'))) {
1523             $bgcolor = item_color_to_ansi($tmp, 'bg');
1524 1   33     16 } elsif (defined ($tmp = $self->get_eff_column_style($x, 'bgcolor'))) {
      33        
      50        
1525             $bgcolor = item_color_to_ansi($tmp, 'bg');
1526 1         4 } elsif (defined ($tmp = $self->{cell_bgcolor})) {
1527 1         2 $bgcolor = item_color_to_ansi($tmp, 'bg');
1528 1   33     8 } elsif (defined ($tmp = $self->{_draw}{fcol_detect}[$x]{bgcolor})) {
      50        
1529 1   33     21 $bgcolor = item_color_to_ansi($tmp, 'bg');
      50        
1530             } elsif ($tmp = $self->_color_theme_item_color_to_ansi('cell_bg', $args, 'bg')) {
1531             $bgcolor = $tmp;
1532             } else {
1533             $bgcolor = "";
1534             }
1535              
1536 1         9 my $align =
1537             $self->get_eff_cell_style($oy, $x, 'align') //
1538             $self->get_eff_row_style($oy, 'align') //
1539             $self->get_eff_column_style($x, 'align') //
1540 1         6 $self->{cell_align} //
1541             $self->{_draw}{fcol_detect}[$x]{align} //
1542             'left';
1543             my $valign =
1544 2     2   4 $self->get_eff_cell_style($oy, $x, 'valign') //
1545             $self->get_eff_row_style($oy, 'valign') //
1546 2         4 $self->get_eff_column_style($x, 'valign') //
1547 2         3 $self->{cell_valign} //
1548 2         5 $self->{_draw}{fcol_detect}[$x]{valign} //
1549             'top';
1550 2         12 #say "D:y=$y, x=$x, align=$align, valign=$valign";
1551              
1552 2         3 my $lpad = $self->{_draw}{fcol_lpads}[$x];
1553             my $rpad = $self->{_draw}{fcol_rpads}[$x];
1554 2 50       5 my $tpad = $self->{_draw}{frow_tpads}[$y];
    50          
    50          
    50          
    50          
    50          
1555 0         0 my $bpad = $self->{_draw}{frow_bpads}[$y];
1556              
1557 0         0 my $res = $self->_get_cell_lines(
1558             $cell, # text
1559 0         0 $self->{_draw}{fcol_widths}[$x], # width
1560             $self->{_draw}{frow_heights}[$y], # height
1561 0         0 $align, $valign, # aligns
1562             $lpad, $rpad, $tpad, $bpad, # paddings
1563 0         0 $fgcolor . $bgcolor);
1564             $res;
1565 0         0 }
1566              
1567 2         39 my ($self) = @_;
1568              
1569             $self->_prepare_draw;
1570 2         4  
1571 2 50       13 $self->{_draw}{buf} = []; # output buffer
    50          
    50          
    50          
    50          
    50          
1572 0         0 $self->{_draw}{y} = 0; # current line
1573              
1574 0         0 my $cols = $self->{columns};
1575             my $fcols = $self->{_draw}{fcols};
1576 0         0 my $frows = $self->{_draw}{frows};
1577             my $frow_heights = $self->{_draw}{frow_heights};
1578 0         0 my $frow_tpads = $self->{_draw}{frow_tpads};
1579             my $frow_bpads = $self->{_draw}{frow_bpads};
1580 0         0 my $fcol_lpads = $self->{_draw}{fcol_lpads};
1581             my $fcol_rpads = $self->{_draw}{fcol_rpads};
1582 0         0 my $fcol_widths = $self->{_draw}{fcol_widths};
1583              
1584 2         35 # draw border top line
1585             {
1586             last unless length($self->{border_style_obj}->get_border_char(char=>'rd_t'));
1587             my @b;
1588             push @b, 'rd_t', 1;
1589             for my $i (0..@$fcols-1) {
1590             my $ci = $self->_colnum($fcols->[$i]);
1591             push @b, 'h_t',
1592             $fcol_lpads->[$ci] + $fcol_widths->[$ci] + $fcol_rpads->[$ci];
1593 2   33     5 push @b, 'hd_t', 1 if $i < @$fcols-1;
      33        
      33        
      33        
      50        
1594             }
1595             push @b, 'ld_t', 1;
1596             $self->draw_border_char(@b);
1597             $self->draw_str("\n");
1598             }
1599              
1600 2   33     7 # draw header
      33        
      33        
      33        
      50        
1601             if ($self->{show_header}) {
1602             my %seen;
1603 2         5 my $hcell_lines = []; # index = [fcolnum]
1604 2         4 if (@$fcols) {
1605 2         3 for my $i (0..@$fcols-1) {
1606 2         4 my $ci = $self->_colnum($fcols->[$i]);
1607             if (defined($seen{$i})) {
1608             $hcell_lines->[$i] = $hcell_lines->[$seen{$i}];
1609             }
1610             $seen{$i} = $ci;
1611 2         8 $hcell_lines->[$i] = $self->_get_header_cell_lines($ci);
1612             }
1613             } else {
1614             # so we can still draw header
1615 2         10 $hcell_lines->[0] = [""];
1616             }
1617             #use Data::Dump; print "D:hcell_lines: "; dd $hcell_lines;
1618             for my $l (0..@{ $hcell_lines->[0] }-1) {
1619 1     1 1 11 $self->draw_border_char('v_l');
1620             for my $i (0..@$fcols-1) {
1621 1         10 $self->draw_str($hcell_lines->[$i][$l]);
1622             $self->draw_color_reset;
1623 1         4 $self->draw_border_char('v_i') unless $i == @$fcols-1;
1624 1         3 }
1625             $self->draw_border_char('v_r');
1626 1         3 $self->draw_str("\n");
1627 1         2 }
1628 1         3 }
1629 1         2  
1630 1         1 # draw header-data row separator
1631 1         2 if ($self->{show_header} && length($self->{border_style_obj}->get_border_char(char=>'rv_l'))) {
1632 1         2 my @b;
1633 1         3 push @b, 'rv_l', 1;
1634 1         3 for my $i (0..@$fcols-1) {
1635             my $ci = $self->_colnum($fcols->[$i]);
1636             push @b, 'h_i',
1637             $fcol_lpads->[$ci] + $fcol_widths->[$ci] + $fcol_rpads->[$ci];
1638 1 50       1 push @b, 'hv_i', 1 unless $i==@$fcols-1;
  1         23  
1639 1         86 }
1640 1         7 push @b, 'lv_r', 1;
1641 1         5 $self->draw_border_char(@b);
1642 1         4 $self->draw_str("\n");
1643 1         4 }
1644              
1645 1 50       4 # draw data rows
1646             {
1647 1         3 for my $r (0..@$frows-1) {
1648 1         6 #$self->draw_str("r$r");
1649 1         3 my $dcell_lines = []; # index = [fcolnum]
1650             my %seen;
1651             if (@$fcols) {
1652             for my $i (0..@$fcols-1) {
1653 1 50       3 my $ci = $self->_colnum($fcols->[$i]);
1654 1         3 if (defined($seen{$i})) {
1655 1         2 $dcell_lines->[$i] = $dcell_lines->[$seen{$i}];
1656 1 50       4 }
1657 1         3 $seen{$i} = $ci;
1658 1         4 $dcell_lines->[$i] = $self->_get_data_cell_lines($r, $ci);
1659 1 50       4 }
1660 0         0 } else {
1661             # so we can still print row
1662 1         4 $dcell_lines->[0] = [" "];
1663 1         4 }
1664             #use Data::Dump; print "TMP: dcell_lines: "; dd $dcell_lines;
1665             for my $l (0..@{ $dcell_lines->[0] }-1) {
1666             $self->draw_border_char({rownum=>$r}, 'v_l');
1667 0         0 for my $i (0..@$fcols-1) {
1668             $self->draw_str($dcell_lines->[$i][$l]);
1669             $self->draw_color_reset;
1670 1         8 $self->draw_border_char({rownum=>$r}, 'v_i')
  1         6  
1671 1         4 unless $i == @$fcols-1;
1672 1         5 }
1673 1         4 $self->draw_border_char({rownum=>$r}, 'v_r');
1674 1         3 $self->draw_str("\n");
1675 1 50       4 }
1676              
1677 1         4 # draw separators between row
1678 1         3 if ($self->_should_draw_row_separator($r)) {
1679             my @b;
1680             push @b, 'rv_l', 1;
1681             for my $i (0..@$fcols-1) {
1682             my $ci = $self->_colnum($fcols->[$i]);
1683 1 50 33     31 push @b, 'h_i',
1684 1         72 $fcol_lpads->[$ci] + $fcol_widths->[$ci] +
1685 1         5 $fcol_rpads->[$ci];
1686 1         4 push @b, ($i==@$fcols-1 ? 'lv_r' : 'hv_i'), 1;
1687 1         4 }
1688 1         4 $self->draw_border_char({rownum=>$r}, @b);
1689             $self->draw_str("\n");
1690 1 50       5 }
1691             } # for frow
1692 1         7 }
1693 1         6  
1694 1         3 # draw border bottom line
1695             {
1696             last unless length($self->{border_style_obj}->get_border_char(char=>'ru_b'));
1697             my @b;
1698             push @b, 'ru_b', 1;
1699 1         12 for my $i (0..@$fcols-1) {
1700             my $ci = $self->_colnum($fcols->[$i]);
1701 2         6 push @b, 'h_b',
1702 2         3 $fcol_lpads->[$ci] + $fcol_widths->[$ci] + $fcol_rpads->[$ci];
1703 2 50       5 push @b, 'hu_b', 1 unless $i == @$fcols-1;
1704 2         4 }
1705 2         6 push @b, 'lu_b', 1;
1706 2 50       6 $self->draw_border_char(@b);
1707 0         0 $self->draw_str("\n");
1708             }
1709 2         5  
1710 2         8 join "", @{$self->{_draw}{buf}};
1711             }
1712              
1713             1;
1714 0         0 # ABSTRACT: Create nice formatted tables using extended ASCII and ANSI colors
1715              
1716              
1717 2         3 =pod
  2         6  
1718 2         9  
1719 2         7 =encoding UTF-8
1720 2         7  
1721 2         5 =head1 NAME
1722 2 50       6  
1723             Text::ANSITable - Create nice formatted tables using extended ASCII and ANSI colors
1724              
1725 2         8 =head1 VERSION
1726 2         5  
1727             This document describes version 0.608 of Text::ANSITable (from Perl distribution Text-ANSITable), released on 2022-02-14.
1728              
1729             =head1 SYNOPSIS
1730 2 50       4  
1731 0         0 use 5.010;
1732 0         0 use Text::ANSITable;
1733 0         0  
1734 0         0 # don't forget this if you want to output utf8 characters
1735 0         0 binmode(STDOUT, ":utf8");
1736              
1737             my $t = Text::ANSITable->new;
1738 0 0       0  
1739             # set styles
1740 0         0 $t->border_style('UTF8::SingleLineBold'); # if not, a nice default is picked
1741 0         0 $t->color_theme('Standard::NoGradation'); # if not, a nice default is picked
1742              
1743             # fill data
1744             $t->columns(["name" , "color" , "price"]);
1745             $t->add_row(["chiki" , "yellow", 2000]);
1746             $t->add_row(["lays" , "green" , 7000]);
1747             $t->add_row(["tao kae noi", "blue" , 18500]);
1748 1 50       2  
  1         2  
  1         18  
1749 1         56 # draw it!
1750 1         11 print $t->draw;
1751 1         5  
1752 1         3 Samples of output:
1753 1         13  
1754             =head1 DESCRIPTION
1755 1 50       6  
1756             This module is yet another text table formatter module like L<Text::ASCIITable>
1757 1         3 or L<Text::SimpleTable>, with the following differences:
1758 1         3  
1759 1         3 =over
1760              
1761             =item * Colors and color themes
1762 1         6  
  1         8  
1763             ANSI color codes will be used by default (even 256 and 24bit colors), but will
1764             degrade to lower color depth and black/white according to terminal support.
1765              
1766             =item * Box-drawing characters
1767              
1768             Box-drawing characters will be used by default, but will degrade to using normal
1769             ASCII characters if terminal does not support them.
1770              
1771             =item * Unicode and wide character support
1772              
1773             Border styles using Unicode characters (double lines, bold/heavy lines, brick
1774             style, etc). Columns containing wide characters stay aligned. (Note: support for
1775             wide characters requires L<Text::ANSI::WideUtil> which is currently set as an
1776             optional prereq, so you'll need to install it explicitly or set your CPAN client
1777             to install 'recommends' prereq).
1778              
1779             =back
1780              
1781             Compared to Text::ASCIITable, it uses C<lower_case> method/attr names instead of
1782             C<CamelCase>, and it uses arrayref for C<columns> and C<add_row>. When
1783             specifying border styles, the order of characters are slightly different. More
1784             fine-grained options to customize appearance.
1785              
1786             =for Pod::Coverage ^(BUILD|draw_.+|get_color_reset)$
1787              
1788             =begin HTML
1789              
1790             <p><img src="http://blogs.perl.org/users/steven_haryanto/ansitable1.png" /></p>
1791              
1792             <p><img src="http://blogs.perl.org/users/steven_haryanto/ansitable2.png" /></p>
1793              
1794             <p><img src="http://blogs.perl.org/users/steven_haryanto/ansitable3.png" /></p>
1795              
1796             <p><img src="http://blogs.perl.org/users/steven_haryanto/ansitable4.png" /></p>
1797              
1798             <p><img src="http://blogs.perl.org/users/steven_haryanto/ansitable5.png" /></p>
1799              
1800             =end HTML
1801              
1802             =head1 DECLARED FEATURES
1803              
1804             Features declared by this module:
1805              
1806             =head2 From feature set PerlTrove
1807              
1808             Features from feature set L<PerlTrove|Module::Features::PerlTrove> declared by this module:
1809              
1810             =over
1811              
1812             =item * Development Status
1813              
1814             Value: "5 - ProductionE<sol>Stable".
1815              
1816             =item * Environment
1817              
1818             Value: "Console".
1819              
1820             =item * Intended Audience
1821              
1822             Value: ["Developers"].
1823              
1824             =item * License
1825              
1826             Value: "OSI Approved :: Artistic License".
1827              
1828             =item * Programming Language
1829              
1830             Value: "Perl".
1831              
1832             =item * Topic
1833              
1834             Value: ["Software Development :: Libraries :: Perl Modules","Utilities"].
1835              
1836             =back
1837              
1838             =head2 From feature set TextTable
1839              
1840             Features from feature set L<TextTable|Module::Features::TextTable> declared by this module:
1841              
1842             =over
1843              
1844             =item * can_align_cell_containing_color_code
1845              
1846             Value: yes.
1847              
1848             =item * can_align_cell_containing_newline
1849              
1850             Value: yes.
1851              
1852             =item * can_align_cell_containing_wide_character
1853              
1854             Value: yes.
1855              
1856             =item * can_color
1857              
1858             Can produce colored table.
1859              
1860             Value: yes.
1861              
1862             =item * can_color_theme
1863              
1864             Allow choosing colors from a named set of palettes.
1865              
1866             Value: yes.
1867              
1868             =item * can_colspan
1869              
1870             Value: no.
1871              
1872             =item * can_customize_border
1873              
1874             Let user customize border character in some way, e.g. selecting from several available borders, disable border.
1875              
1876             Value: yes.
1877              
1878             =item * can_halign
1879              
1880             Provide a way for user to specify horizontal alignment (leftE<sol>middleE<sol>right) of cells.
1881              
1882             Value: yes.
1883              
1884             =item * can_halign_individual_cell
1885              
1886             Provide a way for user to specify different horizontal alignment (leftE<sol>middleE<sol>right) for individual cells.
1887              
1888             Value: yes.
1889              
1890             =item * can_halign_individual_column
1891              
1892             Provide a way for user to specify different horizontal alignment (leftE<sol>middleE<sol>right) for individual columns.
1893              
1894             Value: yes.
1895              
1896             =item * can_halign_individual_row
1897              
1898             Provide a way for user to specify different horizontal alignment (leftE<sol>middleE<sol>right) for individual rows.
1899              
1900             Value: yes.
1901              
1902             =item * can_hpad
1903              
1904             Provide a way for user to specify horizontal padding of cells.
1905              
1906             Value: yes.
1907              
1908             =item * can_hpad_individual_cell
1909              
1910             Provide a way for user to specify different horizontal padding of individual cells.
1911              
1912             Value: yes.
1913              
1914             =item * can_hpad_individual_column
1915              
1916             Provide a way for user to specify different horizontal padding of individual columns.
1917              
1918             Value: yes.
1919              
1920             =item * can_hpad_individual_row
1921              
1922             Provide a way for user to specify different horizontal padding of individual rows.
1923              
1924             Value: yes.
1925              
1926             =item * can_rowspan
1927              
1928             Value: no.
1929              
1930             =item * can_set_cell_height
1931              
1932             Allow setting height of rows.
1933              
1934             Value: yes.
1935              
1936             =item * can_set_cell_height_of_individual_row
1937              
1938             Allow setting height of individual rows.
1939              
1940             Value: yes.
1941              
1942             =item * can_set_cell_width
1943              
1944             Allow setting height of rows.
1945              
1946             Value: yes.
1947              
1948             =item * can_set_cell_width_of_individual_column
1949              
1950             Allow setting height of individual rows.
1951              
1952             Value: yes.
1953              
1954             =item * can_use_box_character
1955              
1956             Can use terminal box-drawing character when drawing border.
1957              
1958             Value: yes.
1959              
1960             =item * can_valign
1961              
1962             Provide a way for user to specify vertical alignment (topE<sol>middleE<sol>bottom) of cells.
1963              
1964             Value: yes.
1965              
1966             =item * can_valign_individual_cell
1967              
1968             Provide a way for user to specify different vertical alignment (topE<sol>middleE<sol>bottom) for individual cells.
1969              
1970             Value: yes.
1971              
1972             =item * can_valign_individual_column
1973              
1974             Provide a way for user to specify different vertical alignment (topE<sol>middleE<sol>bottom) for individual columns.
1975              
1976             Value: yes.
1977              
1978             =item * can_valign_individual_row
1979              
1980             Provide a way for user to specify different vertical alignment (topE<sol>middleE<sol>bottom) for individual rows.
1981              
1982             Value: yes.
1983              
1984             =item * can_vpad
1985              
1986             Provide a way for user to specify vertical padding of cells.
1987              
1988             Value: yes.
1989              
1990             =item * can_vpad_individual_cell
1991              
1992             Provide a way for user to specify different vertical padding of individual cells.
1993              
1994             Value: yes.
1995              
1996             =item * can_vpad_individual_column
1997              
1998             Provide a way for user to specify different vertical padding of individual columns.
1999              
2000             Value: yes.
2001              
2002             =item * can_vpad_individual_row
2003              
2004             Provide a way for user to specify different vertical padding of individual rows.
2005              
2006             Value: yes.
2007              
2008             =item * speed
2009              
2010             Subjective speed rating, relative to other text table modules.
2011              
2012             Value: "slow".
2013              
2014             =back
2015              
2016             For more details on module features, see L<Module::Features>.
2017              
2018             =head1 REFERRING TO COLUMNS
2019              
2020             Columns can be referred to be integer number (0-based) or name (string). You
2021             should not have integer numbers as column names because that will be confusing.
2022             Example:
2023              
2024             $t->columns(["col1", "col2", "col3"]); # col1=0, col2=1, col3=2
2025             $t->add_row([...]);
2026             ...
2027              
2028             # set visible columns
2029             $t->column_filter([1,2,1]); # col2, col3, col2
2030             $t->column_filter(["col2","col3","col2"]); # same thing
2031              
2032             See also: L</REFERRING TO ROWS>.
2033              
2034             =head1 REFERRING TO ROWS
2035              
2036             Rows are referred to by integer number (0-based).
2037              
2038             $t->columns(["name", "age", "gender"]);
2039             $t->add_row(["marty", ...]); # first row (0)
2040             $t->add_row(["wendy", ...]); # second row (1)
2041             $t->add_row(["charlotte", ...]); # third row (2)
2042              
2043             # set visible rows
2044             $t->row_filter([0,2]); # marty & charlotte
2045              
2046             See also: L</REFERRING TO COLUMNS>.
2047              
2048             =head1 BORDER STYLES
2049              
2050             To list available border styles, just list the C<BorderStyle::*> modules. You
2051             can use the provided method:
2052              
2053             say $_ for $t->list_border_styles;
2054              
2055             Or you can also try out borders using the provided
2056             L<ansitable-list-border-styles> script.
2057              
2058             To choose border style, set the C<border_style> attribute to an available border
2059             style name (which is the BorderStyle::* module name without the prefix) with
2060             optional arguments.
2061              
2062             # during construction
2063             my $t = Text::ANSITable->new(
2064             ...
2065             border_style => "UTF8::SingleLineBold",
2066             ...
2067             );
2068              
2069             # after the object is constructed
2070             $t->border_style("UTF8::SingleLineBold");
2071             $t->border_style("Test::CustomChar=character,x");
2072             $t->border_style(["Test::CustomChar", {character=>"x"}]);
2073              
2074             If no border style is selected explicitly, a nice default will be chosen. You
2075             can also set the C<ANSITABLE_BORDER_STYLE> or C<BORDER_STYLE> environment
2076             variable to set the default.
2077              
2078             To create a new border style, see L<BorderStyle>.
2079              
2080             =head1 COLOR THEMES
2081              
2082             To list available color themes, just list the C<ColorTheme::*> modules (usually
2083             you want to use color themes specifically created for Text::ANSITable in
2084             C<ColorTheme::Text::ANSITable::*> namespace). You can use the provided method:
2085              
2086             say $_ for $t->list_color_themes;
2087              
2088             Or you can also run the provided L<ansitable-list-color-themes> script.
2089              
2090             To choose a color theme, set the C<color_theme> attribute to an available color
2091             theme (which is the ColorTheme::* module name without the prefix) with optional
2092             arguments:
2093              
2094             # during construction
2095             my $t = Text::ANSITable->new(
2096             ...
2097             color_theme => "Standard::NoGradation",
2098             ...
2099             );
2100              
2101             # after the object is constructed
2102             $t->color_theme("Standard::NoGradation");
2103             $t->color_theme(["Lens::Darken", {theme=>"Standard::NoGradation"}]);
2104              
2105             If no color theme is selected explicitly, a nice default will be chosen. You can
2106             also set the C<ANSITABLE_COLOR_THEME> environment variable to set the default.
2107              
2108             To create a new color theme, see L<ColorTheme> and an existing
2109             C<ColorTheme::Text::ANSITable::*> module.
2110              
2111             =head1 COLUMN WIDTHS
2112              
2113             By default column width is set just so it is enough to show the widest data.
2114             This can be customized in the following ways (in order of precedence, from
2115             lowest):
2116              
2117             =over
2118              
2119             =item * table-level C<cell_width> attribute
2120              
2121             This sets width for all columns.
2122              
2123             =item * conditional column styles
2124              
2125             The example below sets column width to 10 for columns whose names matching
2126             C</[acm]time/>, else sets the column width to 20.
2127              
2128             $t->add_cond_column_style(sub { /[acm]time/ }, width => 10);
2129             $t->add_cond_column_style(sub { !/[acm]time/ }, width => 20);
2130              
2131             =item * per-column C<width> style
2132              
2133             $t->set_column_style('colname', width => 20);
2134              
2135             =back
2136              
2137             You can use negative number to mean I<minimum> width.
2138              
2139             =head1 ROW HEIGHTS
2140              
2141             This can be customized in the following ways (in order of precedence, from
2142             lowest):
2143              
2144             =over
2145              
2146             =item * table-level C<cell_height> attribute
2147              
2148             This sets height for all rows.
2149              
2150             =item * conditional row styles
2151              
2152             The example below sets row height to 2 for every odd rows, and 1 for even rows.
2153              
2154             $t->add_cond_row_style(sub { $_ % 2 == 0 }, height => 2);
2155             $t->add_cond_row_style(sub { $_ % 2 }, height => 1);
2156              
2157             =item * per-row C<height> style
2158              
2159             $t->set_row_style(1, height => 2);
2160              
2161             =back
2162              
2163             You can use negative number to mean I<minimum> height.
2164              
2165             =head1 CELL (HORIZONTAL) PADDING
2166              
2167             By default cell (horizontal) padding is 1. This can be customized in the
2168             following ways (in order of precedence, from lowest):
2169              
2170             =over
2171              
2172             =item * table-level C<cell_pad> attribute
2173              
2174             This sets left and right padding for all columns.
2175              
2176             =item * table-level C<cell_lpad> and C<cell_rpad> attributes
2177              
2178             They set left and right padding for all columns, respectively.
2179              
2180             =item * conditional column C<pad> style
2181              
2182             $t->add_cond_column_style($cond, pad => 0);
2183              
2184             =item * conditional column C<lpad>/C<rpad> style
2185              
2186             $t->add_cond_column_style($cond, lpad => 1, rpad => 2);
2187              
2188             =item * per-column C<pad> style
2189              
2190             $t->set_column_style($colname, pad => 0);
2191              
2192             =item * per-column C<lpad>/C<rpad> style
2193              
2194             $t->set_column_style($colname, lpad => 1);
2195             $t->set_column_style($colname, rpad => 2);
2196              
2197             =back
2198              
2199             =head1 ROW VERTICAL PADDING
2200              
2201             Default vertical padding is 0. This can be changed in the following ways (in
2202             order of precedence, from lowest):
2203              
2204             =over
2205              
2206             =item * table-level C<cell_vpad> attribute
2207              
2208             This sets top and bottom padding for all rows.
2209              
2210             =item * table-level C<cell_tpad>/C<cell_bpad> attributes
2211              
2212             They set top/bottom padding separately for all rows.
2213              
2214             =item * conditional row C<vpad> style
2215              
2216             Example:
2217              
2218             $t->add_cond_row_style($cond, vpad => 1);
2219              
2220             =item * per-row C<vpad> style
2221              
2222             Example:
2223              
2224             $t->set_row_style($rownum, vpad => 1);
2225              
2226             When adding row:
2227              
2228             $t->add_row($rownum, {vpad=>1});
2229              
2230             =item * per-row C<tpad>/C<bpad> style
2231              
2232             Example:
2233              
2234             $t->set_row_style($rownum, tpad => 1);
2235             $t->set_row_style($rownum, bpad => 2);
2236              
2237             When adding row:
2238              
2239             $t->add_row($row, {tpad=>1, bpad=>2});
2240              
2241             =back
2242              
2243             =head1 CELL COLORS
2244              
2245             By default data format colors are used, e.g. cyan/green for text (using the
2246             default color scheme, items C<num_data>, C<bool_data>, etc). In absense of that,
2247             C<cell_fgcolor> and C<cell_bgcolor> from the color scheme are used. You can
2248             customize colors in the following ways (ordered by precedence, from lowest):
2249              
2250             =over
2251              
2252             =item * table-level C<cell_fgcolor> and C<cell_bgcolor> attributes
2253              
2254             Sets all cells' colors. Color should be specified using 6-hexdigit RGB which
2255             will be converted to the appropriate terminal color.
2256              
2257             Can also be set to a coderef which will receive ($rownum, $colname) and should
2258             return an RGB color.
2259              
2260             =item * conditional column C<fgcolor> and C<bgcolor> style
2261              
2262             Example:
2263              
2264             $t->add_cond_column_style($cond, fgcolor => 'fa8888', bgcolor => '202020');
2265              
2266             =item * per-column C<fgcolor> and C<bgcolor> styles
2267              
2268             Example:
2269              
2270             $t->set_column_style('colname', fgcolor => 'fa8888');
2271             $t->set_column_style('colname', bgcolor => '202020');
2272              
2273             =item * conditional row C<fgcolor> and C<bgcolor> style
2274              
2275             Example:
2276              
2277             $t->add_cond_row_style($cond, fgcolor => 'fa8888', bgcolor => '202020');
2278              
2279             =item * per-row C<fgcolor> and C<bgcolor> styles
2280              
2281             Example:
2282              
2283             $t->set_row_style($rownum, {fgcolor => 'fa8888', bgcolor => '202020'});
2284              
2285             When adding row/rows:
2286              
2287             $t->add_row($row, {fgcolor=>..., bgcolor=>...});
2288             $t->add_rows($rows, {bgcolor=>...});
2289              
2290             =item * conditional cell C<fgcolor> and C<bgcolor> style
2291              
2292             $t->add_cond_cell_style($cond, fgcolor=>..., bgcolor=>...);
2293              
2294             =item * per-cell C<fgcolor> and C<bgcolor> styles
2295              
2296             Example:
2297              
2298             $t->set_cell_style($rownum, $colname, fgcolor => 'fa8888');
2299             $t->set_cell_style($rownum, $colname, bgcolor => '202020');
2300              
2301             =back
2302              
2303             For flexibility, all colors can be specified as coderef. See L</"COLOR THEMES">
2304             for more details.
2305              
2306             =head1 CELL (HORIZONTAL AND VERTICAL) ALIGNMENT
2307              
2308             By default, numbers are right-aligned, dates and bools are centered, and the
2309             other data types (text including) are left-aligned. All data are top-valigned.
2310             This can be customized in the following ways (in order of precedence, from
2311             lowest):
2312              
2313             =over
2314              
2315             =item * table-level C<cell_align> and C<cell_valign> attribute
2316              
2317             =item * conditional column C<align> and <valign> styles
2318              
2319             $t->add_cond_column_style($cond, align=>..., valign=>...);
2320              
2321             =item * per-column C<align> and C<valign> styles
2322              
2323             Example:
2324              
2325             $t->set_column_style($colname, align => 'middle'); # or left, or right
2326             $t->set_column_style($colname, valign => 'top'); # or bottom, or middle
2327              
2328             =item * conditional row C<align> and <valign> styles
2329              
2330             $t->add_cond_row_style($cond, align=>..., valign=>...);
2331              
2332             =item * per-row C<align> and C<valign> styles
2333              
2334             =item * conditional cell C<align> and <valign> styles
2335              
2336             $t->add_cond_cell_style($cond, align=>..., valign=>...);
2337              
2338             =item * per-cell C<align> and C<valign> styles
2339              
2340             $t->set_cell_style($rownum, $colname, align => 'middle');
2341             $t->set_cell_style($rownum, $colname, valign => 'top');
2342              
2343             =back
2344              
2345             =head1 CELL FORMATS
2346              
2347             The per-column- and per-cell- C<formats> style regulates how to format data. The
2348             value for this style setting will be passed to L<Data::Unixish::Apply>'s
2349             C<apply()>, as the C<functions> argument. So it should be a single string (like
2350             C<date>) or an array (like C<< ['date', ['centerpad', {width=>20}]] >>).
2351              
2352             L<Data::Unixish::Apply> is an optional prerequisite, so you will need to install
2353             it separately if you need this feature.
2354              
2355             To see what functions are available, install L<App::dux> and then run C<dux -l>.
2356             Functions of interest to formatting data include: C<bool>, C<num>, C<sprintf>,
2357             C<sprintfn>, C<wrap>, C<ANSI::*> (in L<Data::Unixish::ANSI> distribution),
2358             (among others).
2359              
2360             =head1 CONDITIONAL STYLES
2361              
2362             As an alternative to setting styles for specific {column,row,cell}, you can also
2363             create conditional styles. You specify a Perl code for the condition, then if
2364             the condition evaluates to true, the corresponding styles are applied to the
2365             corresponding {column,row,cell}.
2366              
2367             To add a conditional style, use the C<add_cond_{column,row,cell}_style> methods.
2368             These methods accept condition code as its first argument and one or more styles
2369             in the subsequent argument(s). For example:
2370              
2371             $t->add_cond_row_style(sub { $_ % 2 }, bgcolor=>'202020');
2372              
2373             The above example will set row bgcolor for odd rows. You can add more
2374             conditional styles:
2375              
2376             $t->add_cond_row_style(sub { $_ % 2 == 0 }, bgcolor=>'404040');
2377              
2378             All the conditions will be evaluated and the applicable styles will be merged
2379             together. For example, if we add a third conditional row style:
2380              
2381             $t->add_cond_row_style(sub { $_ % 10 == 0 }, height=>2, fgcolor=>'ffff00');
2382              
2383             then every tenth row will have its height set to 2, fgcolor set to ffff00, and
2384             bgcolor set to 404040 (from the second conditional).
2385              
2386             Condition coderef will be called with these arguments:
2387              
2388             ($self, %args)
2389              
2390             Available keys in C<%args> for conditional column styles: C<col> (int, column
2391             index), C<colname> (str, column name). Additionally, C<$_> will be set locally
2392             to the column index.
2393              
2394             Available keys in C<%args> for conditional row styles: C<row> (int, row index),
2395             C<row_data> (array). Additionally, C<$_> will be set locally to the row index.
2396              
2397             Available keys in C<%args> for conditional cell styles: C<content> (str), C<col>
2398             (int, column index), C<row> (int, row index). Additionally, C<$_> will be set
2399             locally to the cell content.
2400              
2401             Coderef should return boolean indicating whether style should be applied to a
2402             particular column/row/cell. When returning a true value, coderef can also return
2403             a hashref to return additional styles that will be merged/applied too.
2404              
2405             =head1 STYLE SETS
2406              
2407             A style set is just a collection of style settings that can be applied.
2408             Organizing styles into style sets makes applying the styles simpler and more
2409             reusable.
2410              
2411             More than one style sets can be applied.
2412              
2413             Style set module accepts arguments.
2414              
2415             For example, the L<Text::ANSITable::StyleSet::AltRow> style set defines this:
2416              
2417             has odd_bgcolor => (is => 'rw');
2418             has even_bgcolor => (is => 'rw');
2419             has odd_fgcolor => (is => 'rw');
2420             has even_fgcolor => (is => 'rw');
2421              
2422             sub apply {
2423             my ($self, $table) = @_;
2424              
2425             $table->add_cond_row_style(sub {
2426             my ($t, %args) = @_;
2427             my %styles;
2428             if ($_ % 2) {
2429             $styles{bgcolor} = $self->odd_bgcolor
2430             if defined $self->odd_bgcolor;
2431             $styles{fgcolor} = $self->odd_fgcolor
2432             if defined $self->odd_bgcolor;
2433             } else {
2434             $styles{bgcolor} = $self->even_bgcolor
2435             if defined $self->even_bgcolor;
2436             $styles{fgcolor} = $self->even_fgcolor
2437             if defined $self->even_bgcolor;
2438             }
2439             \%styles;
2440             });
2441             }
2442              
2443             To apply this style set:
2444              
2445             $t->apply_style_set("AltRow", odd_bgcolor=>"003300", even_bgcolor=>"000000");
2446              
2447             To create a new style set, create a module under C<Text::ANSITable::StyleSet::>
2448             like the above example. Please see the other existing style set modules for more
2449             examples.
2450              
2451             =head1 ATTRIBUTES
2452              
2453             =head2 columns
2454              
2455             Array of str. Must be unique.
2456              
2457             Store column names. Note that when drawing, you can omit some columns, reorder
2458             them, or display some more than once (see C<column_filter> attribute).
2459              
2460             Caveat: Since, for convenience, a column can be referred to using its name or
2461             position, weird/unecxpected thing can happen if you name a column with a number
2462             (e.g. 0, 1, 2, ...). So don't do that.
2463              
2464             =head2 rows => ARRAY OF ARRAY OF STR
2465              
2466             Store row data. You can set this attribute directly, or add rows incrementally
2467             using C<add_row()> and C<add_rows()> methods.
2468              
2469             =head2 row_filter => CODE|ARRAY OF INT
2470              
2471             When drawing, only show rows that match this. Can be an array containing indices
2472             of rows which should be shown, or a coderef which will be called for each row
2473             with arguments C<< ($row, $rownum) >> and should return a bool value indicating
2474             whether that row should be displayed.
2475              
2476             Internal note: During drawing, rows will be filtered and put into C<<
2477             $t->{_draw}{frows} >>.
2478              
2479             =head2 column_filter => CODE|ARRAY OF STR
2480              
2481             When drawing, only show columns that match this. Can be an array containing
2482             names of columns that should be displayed (column names can be in different
2483             order or duplicate, column can also be referred to with its numeric index). Can
2484             also be a coderef which will be called with C<< ($colname, $colnum) >> for
2485             every column and should return a bool value indicating whether that column
2486             should be displayed. The coderef version is more limited in that it cannot
2487             reorder the columns or instruct for the same column to be displayed more than
2488             once.
2489              
2490             Internal note: During drawing, column names will be filtered and put into C<<
2491             $t->{_draw}{fcols} >>.
2492              
2493             =head2 column_wrap => BOOL
2494              
2495             Set column wrapping for all columns. Can be overriden by per-column C<wrap>
2496             style. By default column wrapping will only be done for text columns and when
2497             width is explicitly set to a positive value.
2498              
2499             =head2 use_color => BOOL
2500              
2501             Whether to output color. Default is taken from C<NO_COLOR> environment variable,
2502             C<COLOR> environment variable, or detected via C<(-t STDOUT)>. If C<use_color>
2503             is set to 0, an attempt to use a colored color theme (i.e. anything that is not
2504             the C<no_color> theme) will result in an exception.
2505              
2506             (In the future, setting C<use_color> to 0 might opt the module to use
2507             normal/plain string routines instead of the slower ta_* functions from
2508             L<Text::ANSI::Util>; this also means that the module won't handle ANSI escape
2509             codes in the content text.)
2510              
2511             =head2 color_depth => INT
2512              
2513             Terminal's color depth. Either 16, 256, or 2**24 (16777216). Default will be
2514             retrieved from C<COLOR_DEPTH> environment or detected using L<Term::Detect>.
2515              
2516             =head2 use_box_chars => BOOL
2517              
2518             Whether to use box drawing characters. Drawing box drawing characters can be
2519             problematic in some places because it uses ANSI escape codes to switch to (and
2520             back from) line drawing mode (C<"\e(0"> and C<"\e(B">, respectively).
2521              
2522             Default is taken from C<BOX_CHARS> environment variable, or 1. If
2523             C<use_box_chars> is set to 0, an attempt to use a border style that uses box
2524             drawing chararacters will result in an exception.
2525              
2526             =head2 use_utf8 => BOOL
2527              
2528             Whether to use Unicode (UTF8) characters. Default is taken from C<UTF8>
2529             environment variable, or detected using L<Term::Detect>, or guessed via L<LANG>
2530             environment variable. If C<use_utf8> is set to 0, an attempt to select a border
2531             style that uses Unicode characters will result in an exception.
2532              
2533             (In the future, setting C<use_utf8> to 0 might opt the module to use the
2534             non-"mb_*" version of functions from L<Text::ANSI::Util>, e.g. C<ta_wrap()>
2535             instead of C<ta_mbwrap()>, and so on).
2536              
2537             =head2 wide => BOOL
2538              
2539             Whether to support wide characters. The default is to check for the existence of
2540             L<Text::ANSI::WideUtil> (an optional prereq). You can explicitly enable or
2541             disable wide-character support here.
2542              
2543             =head2 border_style => STR
2544              
2545             Border style name to use. This is a module name in the
2546             C<BorderStyle::Text::ANSITable::*>, C<BorderStyle::*>, or
2547             C<BorderStyle::Text::ANSITable::OldCompat::*> namespace, without the prefix.
2548             See the L<BorderStyle> specification on how to create a new border style.
2549              
2550             =head2 color_theme => STR
2551              
2552             Color theme name to use. This is a module name in the
2553             C<ColorTheme::Text::ANSITable::*>, C<ColorTheme::*>, or
2554             C<ColorTheme::Text::ANSITable::OldCompat::*> namespace, without the prefix. See
2555             the L<ColorTheme> and an example existing color theme module like
2556             L<ColorTheme::Text::ANSITable::Standard::Gradation> specification on how to
2557             create a new border style.
2558              
2559             =head2 show_header => BOOL (default: 1)
2560              
2561             When drawing, whether to show header.
2562              
2563             =head2 show_row_separator => INT (default: 2)
2564              
2565             When drawing, whether to show separator lines between rows. The default (2) is
2566             to only show separators drawn using C<add_row_separator()>. If you set this to
2567             1, lines will be drawn after every data row. If you set this attribute to 0, no
2568             lines will be drawn whatsoever.
2569              
2570             =head2 cell_width => INT
2571              
2572             Set width for all cells. Can be overriden by per-column C<width> style.
2573              
2574             =head2 cell_height => INT
2575              
2576             Set height for all cell. Can be overriden by per-row C<height> style.
2577              
2578             =head2 cell_align => STR
2579              
2580             Set (horizontal) alignment for all cells. Either C<left>, C<middle>, or
2581             C<right>. Can be overriden by per-column/per-row/per-cell C<align> style.
2582              
2583             =head2 cell_valign => STR
2584              
2585             Set (horizontal) alignment for all cells. Either C<top>, C<middle>, or
2586             C<bottom>. Can be overriden by per-column/per-row/per-cell C<align> style.
2587              
2588             =head2 cell_pad => INT
2589              
2590             Set (horizontal) padding for all cells. Can be overriden by per-column C<pad>
2591             style.
2592              
2593             =head2 cell_lpad => INT
2594              
2595             Set left padding for all cells. Overrides the C<cell_pad> attribute. Can be
2596             overriden by per-column C<lpad> style.
2597              
2598             =head2 cell_rpad => INT
2599              
2600             Set right padding for all cells. Overrides the C<cell_pad> attribute. Can be
2601             overriden by per-column C<rpad> style.
2602              
2603             =head2 cell_vpad => INT
2604              
2605             Set vertical padding for all cells. Can be overriden by per-row C<vpad> style.
2606              
2607             =head2 cell_tpad => INT
2608              
2609             Set top padding for all cells. Overrides the C<cell_vpad> attribute. Can be
2610             overriden by per-row C<tpad> style.
2611              
2612             =head2 cell_bpad => INT
2613              
2614             Set bottom padding for all cells. Overrides the C<cell_vpad> attribute. Can be
2615             overriden by per-row C<bpad> style.
2616              
2617             =head2 cell_fgcolor => RGB|CODE
2618              
2619             Set foreground color for all cells. Value should be 6-hexdigit RGB. Can also be
2620             a coderef that will receive %args (e.g. rownum, col_name, colnum) and should
2621             return an RGB color. Can be overriden by per-cell C<fgcolor> style.
2622              
2623             =head2 cell_bgcolor => RGB|CODE
2624              
2625             Like C<cell_fgcolor> but for background color.
2626              
2627             =head2 header_fgcolor => RGB|CODE
2628              
2629             Set foreground color for all headers. Overrides C<cell_fgcolor> for headers.
2630             Value should be a 6-hexdigit RGB. Can also be a coderef that will receive %args
2631             (e.g. col_name, colnum) and should return an RGB color.
2632              
2633             =head2 header_bgcolor => RGB|CODE
2634              
2635             Like C<header_fgcolor> but for background color.
2636              
2637             =head2 header_align => STR
2638              
2639             =head2 header_valign => STR
2640              
2641             =head2 header_vpad => INT
2642              
2643             =head2 header_tpad => INT
2644              
2645             =head2 header_bpad => INT
2646              
2647             =head1 METHODS
2648              
2649             =head2 $t = Text::ANSITable->new(%attrs) => OBJ
2650              
2651             Constructor.
2652              
2653             =head2 $t->list_border_styles => LIST
2654              
2655             Return the names of available border styles. Border styles will be searched in
2656             C<BorderStyle::*> modules.
2657              
2658             =head2 $t->list_color_themes => LIST
2659              
2660             Return the names of available color themes. Color themes will be searched in
2661             C<ColorTheme::*> modules.
2662              
2663             =head2 $t->list_style_sets => LIST
2664              
2665             Return the names of available style sets. Style set names are retrieved by
2666             listing modules under C<Text::ANSITable::StyleSet::*> namespace.
2667              
2668             =head2 $t->get_border_style($name) => HASH
2669              
2670             Can also be called as a static method: C<<
2671             Text::ANSITable->get_border_style($name) >>.
2672              
2673             =head2 $t->get_color_theme($name) => HASH
2674              
2675             Can also be called as a static method: C<<
2676             Text::ANSITable->get_color_theme($name) >>.
2677              
2678             =head2 $t->add_row(\@row[, \%styles]) => OBJ
2679              
2680             Add a row. Note that row data is not copied, only referenced.
2681              
2682             Can also add per-row styles (which can also be done using C<row_style()>).
2683              
2684             =head2 $t->add_rows(\@rows[, \%styles]) => OBJ
2685              
2686             Add multiple rows. Note that row data is not copied, only referenced.
2687              
2688             Can also add per-row styles (which can also be done using C<row_style()>).
2689              
2690             =head2 $t->add_row_separator() => OBJ
2691              
2692             Add a row separator line.
2693              
2694             =head2 $t->get_cell($rownum, $col) => VAL
2695              
2696             Get cell value at row #C<$rownum> (starts from zero) and column named/numbered
2697             C<$col>.
2698              
2699             =head2 $t->set_cell($rownum, $col, $newval) => VAL
2700              
2701             Set cell value at row #C<$rownum> (starts from zero) and column named/numbered
2702             C<$col>. Return old value.
2703              
2704             =head2 $t->get_column_style($col, $style) => VAL
2705              
2706             Get per-column style for column named/numbered C<$col>.
2707              
2708             =head2 $t->set_column_style($col, $style=>$val[, $style2=>$val2, ...])
2709              
2710             Set per-column style(s) for column named/numbered C<$col>. Available values for
2711             C<$style>: C<align>, C<valign>, C<pad>, C<lpad>, C<rpad>, C<width>, C<formats>,
2712             C<fgcolor>, C<bgcolor>, C<type>, C<wrap>.
2713              
2714             =head2 $t->get_cond_column_styles => ARRAY
2715              
2716             Get all the conditional column styles set so far.
2717              
2718             =head2 $t->add_cond_column_style($cond, $style=>$val[, $style2=>$val2 ...])
2719              
2720             Add a new conditional column style. See L</"CONDITIONAL STYLES"> for more
2721             details on conditional style.
2722              
2723             =for comment | =head2 $t->clear_cond_column_styles | Clear all the conditional column styles.
2724              
2725             =head2 $t->get_eff_column_style($col, $style) => VAL
2726              
2727             Get "effective" column style named C<$style> for a particular column. Effective
2728             column style is calculated from all the conditional column styles and the
2729             per-column styles then merged together. This is the per-column style actually
2730             applied.
2731              
2732             =head2 $t->get_row_style($rownum) => VAL
2733              
2734             Get per-row style for row numbered C<$rownum>.
2735              
2736             =head2 $t->set_row_style($rownum, $style=>$newval[, $style2=>$newval2, ...])
2737              
2738             Set per-row style(s) for row numbered C<$rownum>. Available values for
2739             C<$style>: C<align>, C<valign>, C<height>, C<vpad>, C<tpad>, C<bpad>,
2740             C<fgcolor>, C<bgcolor>.
2741              
2742             =head2 $t->get_cond_row_styles => ARRAY
2743              
2744             Get all the conditional row styles set so far.
2745              
2746             =head2 $t->add_cond_row_style($cond, $style=>$val[, $style2=>$val2 ...])
2747              
2748             Add a new conditional row style. See L</"CONDITIONAL STYLES"> for more details
2749             on conditional style.
2750              
2751             =for comment | =head2 $t->clear_cond_row_styles | Clear all the conditional row styles.
2752              
2753             =head2 $t->get_eff_row_style($rownum, $style) => VAL
2754              
2755             Get "effective" row style named C<$style> for a particular row. Effective row
2756             style is calculated from all the conditional row styles and the per-row styles
2757             then merged together. This is the per-row style actually applied.
2758              
2759             =head2 $t->get_cell_style($rownum, $col, $style) => VAL
2760              
2761             Get per-cell style named C<$style> for a particular cell. Return undef if there
2762             is no per-cell style with that name.
2763              
2764             =head2 $t->set_cell_style($rownum, $col, $style=>$newval[, $style2=>$newval2, ...])
2765              
2766             Set per-cell style(s). Available values for C<$style>: C<align>, C<valign>,
2767             C<formats>, C<fgcolor>, C<bgcolor>.
2768              
2769             =head2 $t->get_cond_cell_styles => ARRAY
2770              
2771             Get all the conditional cell styles set so far.
2772              
2773             =head2 $t->add_cond_cell_style($cond, $style=>$val[, $style2=>$val2 ...])
2774              
2775             Add a new conditional cell style. See L</"CONDITIONAL STYLES"> for more details
2776             on conditional style.
2777              
2778             =for comment | =head2 $t->clear_cond_cell_styles | Clear all the conditional cell styles.
2779              
2780             =head2 $t->get_eff_cell_style($rownum, $col, $style) => VAL
2781              
2782             Get "effective" cell style named C<$style> for a particular cell. Effective cell
2783             style is calculated from all the conditional cell styles and the per-cell styles
2784             then merged together. This is the per-cell style actually applied.
2785              
2786             =head2 $t->apply_style_set($name, %args)
2787              
2788             Apply a style set. See L</"STYLE SETS"> for more details.
2789              
2790             =head2 $t->draw => STR
2791              
2792             Render table.
2793              
2794             =head1 FAQ
2795              
2796             =head2 General
2797              
2798             =head3 I don't see my data!
2799              
2800             This might be caused by you not defining columns first, e.g.:
2801              
2802             my $t = Text::ANSITable->new;
2803             $t->add_row([1,2,3]);
2804             print $t->draw;
2805              
2806             You need to do this first before adding rows:
2807              
2808             $t->columns(["col1", "col2", "col3"]);
2809              
2810             =head3 All the rows are the same!
2811              
2812             my $t = Text::ANSITable->new;
2813             $t->columns(["col"]);
2814             my @row;
2815             for (1..3) {
2816             @row = ($_);
2817             $t->add_row(\@row);
2818             }
2819             print $t->draw;
2820              
2821             will print:
2822              
2823             col
2824             3
2825             3
2826             3
2827              
2828             You need to add row in this way instead of adding the same reference everytime:
2829              
2830             $t->add_row([@row]);
2831              
2832             =head3 Output is too fancy! I just want to generate some plain (Text::ASCIITable-like) output to be copy-pasted to my document.
2833              
2834             $t->use_utf8(0);
2835             $t->use_box_chars(0);
2836             $t->use_color(0);
2837             $t->border_style('ASCII::SingleLine');
2838              
2839             and you're good to go. Alternatively you can set environment UTF8=0,
2840             BOX_CHARS=0, COLOR=0, and ANSITABLE_BORDER_STYLE=ASCII::SingleLine.
2841              
2842             =head3 Why am I getting 'Wide character in print' warning?
2843              
2844             You are probably using a utf8 border style, and you haven't done something like
2845             this to your output:
2846              
2847             binmode(STDOUT, ":utf8");
2848              
2849             =head3 My table looks garbled when viewed through pager like B<less>!
2850              
2851             That's because B<less> by default escapes ANSI color and box_char codes. Try
2852             using C<-R> option of B<less> to display ANSI color codes raw.
2853              
2854             Or, try not using colors and box_char border styles:
2855              
2856             $t->use_color(0);
2857             $t->use_box_chars(0);
2858              
2859             Note that as of this writing, B<less -R> does not interpret box_char codes so
2860             you'll need to avoid using box_char border styles if you want your output to
2861             display properly under B<less>.
2862              
2863             =head3 How do I hide some columns/rows when drawing?
2864              
2865             Use the C<column_filter> and C<row_filter> attributes. For example, given this
2866             table:
2867              
2868             my $t = Text::ANSITable->new;
2869             $t->columns([qw/one two three/]);
2870             $t->add_row([$_, $_, $_]) for 1..10;
2871              
2872             Doing this:
2873              
2874             $t->row_filter([0, 1, 4]);
2875             print $t->draw;
2876              
2877             will show:
2878              
2879             one | two | three
2880             -----+-----+-------
2881             1 | 1 | 1
2882             2 | 2 | 2
2883             5 | 5 | 5
2884              
2885             Doing this:
2886              
2887             $t->row_filter(sub { my ($row, $idx) = @_; $row->[0] % 2 }
2888              
2889             will display:
2890              
2891             one | two | three
2892             -----+-----+-------
2893             1 | 1 | 1
2894             3 | 3 | 3
2895             5 | 5 | 5
2896             7 | 7 | 7
2897             9 | 9 | 9
2898              
2899             Doing this:
2900              
2901             $t->column_filter([qw/two one 0/]);
2902              
2903             will display:
2904              
2905             two | one | one
2906             -----+-----+-----
2907             1 | 1 | 1
2908             2 | 2 | 2
2909             3 | 3 | 3
2910             4 | 4 | 4
2911             5 | 5 | 5
2912             6 | 6 | 6
2913             7 | 7 | 7
2914             8 | 8 | 8
2915             9 | 9 | 9
2916             10 | 10 | 10
2917              
2918             Doing this:
2919              
2920             $t->column_filter(sub { my ($colname, $idx) = @_; $colname =~ /t/ });
2921              
2922             will display:
2923              
2924             two | three
2925             -----+-------
2926             1 | 1
2927             2 | 2
2928             3 | 3
2929             4 | 4
2930             5 | 5
2931             6 | 6
2932             7 | 7
2933             8 | 8
2934             9 | 9
2935             10 | 10
2936              
2937             =head2 Formatting data
2938              
2939             =head3 How do I format data?
2940              
2941             Use the C<formats> per-column style or per-cell style. For example:
2942              
2943             $t->set_column_style('available', formats => [[bool=>{style=>'check_cross'}],
2944             [centerpad=>{width=>10}]]);
2945             $t->set_column_style('amount' , formats => [[num=>{decimal_digits=>2}]]);
2946             $t->set_column_style('size' , formats => [[num=>{style=>'kilo'}]]);
2947              
2948             See L<Data::Unixish::Apply> and L<Data::Unixish> for more details on the
2949             available formatting functions.
2950              
2951             =head3 How does the module determine column data type?
2952              
2953             Currently: if column name has the word C<date> or C<time> in it, the column is
2954             assumed to contain B<date> data. If column name has C<?> in it, the column is
2955             assumed to be B<bool>. If a column contains only numbers (or undefs), it is
2956             B<num>. Otherwise, it is B<str>.
2957              
2958             =head3 How does the module format data types?
2959              
2960             Currently: B<num> will be right aligned and applied C<num_data> color (cyan in
2961             the default theme). B<date> will be centered and applied C<date_data> color
2962             (gold in the default theme). B<bool> will be centered and formatted as
2963             check/cross symbol and applied C<bool_data> color (red/green depending on
2964             whether the data is false/true). B<str> will be applied C<str_data> color (no
2965             color in the default theme).
2966              
2967             Other color themes might use different colors.
2968              
2969             =head3 How do I force column to be of a certain data type?
2970              
2971             For example, you have a column named C<deleted> but want to display it as
2972             B<bool>. You can do:
2973              
2974             $t->set_column_style(deleted => type => 'bool');
2975              
2976             =head3 How do I wrap long text?
2977              
2978             The C<wrap> dux function can be used to wrap text (see: L<Data::Unixish::wrap>).
2979             You'll want to set C<ansi> and C<mb> both to 1 to handle ANSI escape codes and
2980             wide characters in your text (unless you are sure that your text does not
2981             contain those):
2982              
2983             $t->set_column_style('description', formats=>[[wrap => {width=>60, ansi=>1, mb=>1}]]);
2984              
2985             =head3 How do I highlight text with color?
2986              
2987             The C<ansi::highlight> dux function can be used to highlight text (see:
2988             L<Data::Unixish::ANSI::highlight>).
2989              
2990             $t->set_column_style(2, formats => [[highlight => {pattern=>$pat}]]);
2991              
2992             =head3 I want to change the default bool cross/check sign representation!
2993              
2994             By default, bool columns are shown as cross/check sign. This can be changed,
2995             e.g.:
2996              
2997             $t->set_column_style($colname, type => 'bool',
2998             formats => [[bool => {style=>"Y_N"}]]);
2999              
3000             See L<Data::Unixish::bool> for more details.
3001              
3002             =head3 How do I do conditional cell formatting?
3003              
3004             There are several ways.
3005              
3006             First, you can use the C<cond> dux function through C<formats> style. For
3007             example, if the cell contains the string "Cuti", you want to color the cell
3008             yellow. Otherwise, you want to color the cell red:
3009              
3010             $t->set_column_style($colname, formats => [
3011             [cond => {
3012             if => sub { $_ =~ /Cuti/ },
3013             then => ["ansi::color", {color=>"yellow"}],
3014             else => ["ansi::color", {color=>"red"}],
3015             }]
3016             ]);
3017              
3018             Another way is to use the C<add_cond_{cell,row,column}> methods. See
3019             L</"CONDITIONAL STYLES"> for more details. An example:
3020              
3021             $t->add_cond_row_style(sub {
3022             my %args = @_;
3023             $args{colname} =~ /Cuti/ ? {bgcolor=>"ffff00"} : {bgcolor=>"ff0000"};
3024             });
3025              
3026             And another way is to use (or create) style set, which is basically a packaging
3027             of the above ways. An advantage of using style set is, because you do not
3028             specify coderef directly, you can specify it from the environment variable. See
3029             L</"STYLE SETS"> for more details.
3030              
3031             =head2 Border
3032              
3033             =head3 How to hide borders?
3034              
3035             There is currently no C<show_border> attribute. Choose border styles like
3036             C<ASCII::Space>, C<ASCII::None>, C<UTF8::None>:
3037              
3038             $t->border_style("UTF8::None");
3039              
3040             =head3 Why are there 'ASCII::None' as well 'UTF8::None' and 'BoxChar::None' border styles?
3041              
3042             Because of the row separator, that can still be drawn if C<add_row_separator()>
3043             is used. See next question.
3044              
3045             =head3 I want to hide borders, and I do not want row separators to be shown!
3046              
3047             The default is for separator lines to be drawn if drawn using
3048             C<add_row_separator()>, e.g.:
3049              
3050             $t->add_row(['row1']);
3051             $t->add_row(['row2']);
3052             $t->add_row_separator;
3053             $t->add_row(['row3']);
3054              
3055             The result will be:
3056              
3057             row1
3058             row2
3059             --------
3060             row3
3061              
3062             However, if you set C<show_row_separator> to 0, no separator lines will be drawn
3063             whatsoever:
3064              
3065             row1
3066             row2
3067             row3
3068              
3069             =head3 I want to separate each row with a line!
3070              
3071             Set C<show_row_separator> to 1, or alternatively, set
3072             C<ANSITABLE_STYLE='{"show_row_separator":1}>.
3073              
3074             =head2 Color
3075              
3076             =head3 How to disable colors?
3077              
3078             Set C<use_color> attribute or C<COLOR> environment to 0.
3079              
3080             =head3 How to specify colors using names (e.g. red, 'navy blue') instead of RGB?
3081              
3082             Use modules like L<Graphics::ColorNames>.
3083              
3084             =head3 I'm not seeing colors when output is piped (e.g. to a pager)!
3085              
3086             The default is to disable colors when (-t STDOUT) is false. You can force-enable
3087             colors by setting C<use_color> attribute or C<COLOR> environment to 1.
3088              
3089             =head3 How to enable 256 colors? I'm seeing only 16 colors.
3090              
3091             Use terminal emulators that support 256 colors, e.g. Konsole, xterm,
3092             gnome-terminal, PuTTY/pterm (but the last one has minimal Unicode support).
3093             Better yet, use Konsole or Konsole-based emulators which supports 24bit colors.
3094              
3095             =head3 How to enable 24bit colors (true color)?
3096              
3097             Currently only B<Konsole> and the Konsole-based B<Yakuake> terminal emulator
3098             software support 24bit colors.
3099              
3100             =head3 How to force lower color depth? (e.g. I use Konsole but want 16 colors)
3101              
3102             Set C<COLOR_DEPTH> to 16.
3103              
3104             =head3 How to change border gradation color?
3105              
3106             The default color theme applies vertical color gradation to borders from white
3107             (ffffff) to gray (444444). To change this, set C<border1> and C<border2> theme
3108             arguments:
3109              
3110             $t->color_theme_args({border1=>'ff0000', border2=>'00ff00'}); # red to green
3111              
3112             =head3 I'm using terminal emulator with white background, the texts are not very visible!
3113              
3114             Try using the "*_whitebg" themes, as the other themes are geared towards
3115             terminal emulators with black background.
3116              
3117             =head3 How to set different background colors for odd/even rows?
3118              
3119             Aside from doing C<< $t->set_row_style($rownum, bgcolor=>...) >> for each row,
3120             you can also do this:
3121              
3122             $t->cell_bgcolor(sub { my ($self, %args) = @_; $args{rownum} % 2 ? '202020' : undef });
3123              
3124             Or, you can use conditional row styles:
3125              
3126             $t->add_cond_row_style(sub { $_ % 2 }, {bgcolor=>'202020'});
3127              
3128             Or, you can use the L<Text::ANSITable::StyleSet::AltRow> style set:
3129              
3130             $t->apply_style_set(AltRow => {even_bgcolor=>'202020'});
3131              
3132             =head1 ENVIRONMENT
3133              
3134             =head2 COLOR => BOOL
3135              
3136             Can be used to set default value for the C<color> attribute.
3137              
3138             =head2 COLOR_DEPTH => INT
3139              
3140             Can be used to set default value for the C<color_depth> attribute.
3141              
3142             =head2 BOX_CHARS => BOOL
3143              
3144             Can be used to set default value for the C<box_chars> attribute.
3145              
3146             =head2 UTF8 => BOOL
3147              
3148             Can be used to set default value for the C<utf8> attribute.
3149              
3150             =head2 COLUMNS => INT
3151              
3152             Can be used to override terminal width detection.
3153              
3154             =head2 ANSITABLE_BORDER_STYLE => STR
3155              
3156             Can be used to set default value for C<border_style> attribute. Takes precedence
3157             over L<BORDER_STYLE>.
3158              
3159             =head2 BORDER_STYLE => STR
3160              
3161             Can be used to set default value for C<border_style> attribute. See also
3162             C<ANSITABLE_BORDER_STYLE>.
3163              
3164             =head2 ANSITABLE_COLOR_THEME => STR
3165              
3166             Can be used to set default value for C<border_style> attribute.
3167              
3168             =head2 ANSITABLE_STYLE => str(json)
3169              
3170             Can be used to set table's most attributes. Value should be a JSON-encoded hash
3171             of C<< attr => val >> pairs. Example:
3172              
3173             % ANSITABLE_STYLE='{"show_row_separator":1}' ansitable-list-border-styles
3174              
3175             will display table with row separator lines after every row.
3176              
3177             =head2 WRAP => BOOL
3178              
3179             Can be used to set default value for the C<wrap> column style.
3180              
3181             =head2 ANSITABLE_COLUMN_STYLES => str(json)
3182              
3183             Can be used to set per-column styles. Interpreted right before draw(). Value
3184             should be a JSON-encoded hash of C<< col => {style => val, ...} >> pairs.
3185             Example:
3186              
3187             % ANSITABLE_COLUMN_STYLES='{"2":{"type":"num"},"3":{"type":"str"}}' ansitable-list-border-styles
3188              
3189             will display the bool columns as num and str instead.
3190              
3191             =head2 ANSITABLE_ROW_STYLES => str(json)
3192              
3193             Can be used to set per-row styles. Interpreted right before draw(). Value should
3194             be a JSON-encoded a hash of C<< rownum => {style => val, ...} >> pairs.
3195             Example:
3196              
3197             % ANSITABLE_ROW_STYLES='{"0":{"bgcolor":"000080","vpad":1}}' ansitable-list-border-styles
3198              
3199             will display the first row with blue background color and taller height.
3200              
3201             =head2 ANSITABLE_CELL_STYLES => str(json)
3202              
3203             Can be used to set per-cell styles. Interpreted right before draw(). Value
3204             should be a JSON-encoded a hash of C<< "rownum,col" => {style => val, ...} >>
3205             pairs. Example:
3206              
3207             % ANSITABLE_CELL_STYLES='{"1,1":{"bgcolor":"008000"}}' ansitable-list-border-styles
3208              
3209             will display the second-on-the-left, second-on-the-top cell with green
3210             background color.
3211              
3212             =head2 ANSITABLE_STYLE_SETS => str(json)
3213              
3214             Can be used to apply style sets. Value should be a JSON-encoded array. Each
3215             element must be a style set name or a 2-element array containing style set name
3216             and its arguments (C<< [$name, \%args] >>). Example:
3217              
3218             % ANSITABLE_STYLE_SETS='[["AltRow",{"odd_bgcolor":"003300"}]]'
3219              
3220             will display table with row separator lines after every row.
3221              
3222             =head1 HOMEPAGE
3223              
3224             Please visit the project's homepage at L<https://metacpan.org/release/Text-ANSITable>.
3225              
3226             =head1 SOURCE
3227              
3228             Source repository is at L<https://github.com/perlancar/perl-Text-ANSITable>.
3229              
3230             =head1 SEE ALSO
3231              
3232             =head2 Border styles
3233              
3234             For collections of border styles, search for C<BorderStyle::*> modules.
3235              
3236             =head2 Color themes
3237              
3238             For collections of color themes, search for C<ColorTheme::*> modules.
3239              
3240             =head2 Other table-formatting CPAN modules
3241              
3242             L<Text::ASCIITable> is one of the most popular table-formatting modules on CPAN.
3243             There are a couple of "extensions" for Text::ASCIITable:
3244             L<Text::ASCIITable::TW>, L<Text::ASCIITable::Wrap>; Text::ANSITable can be an
3245             alternative for all those modules since it can already handle wide-characters as
3246             well as multiline text in cells.
3247              
3248             L<Text::TabularDisplay>
3249              
3250             L<Text::Table>
3251              
3252             L<Text::SimpleTable>
3253              
3254             L<Text::UnicodeTable::Simple>
3255              
3256             L<Table::Simple>
3257              
3258             L<Acme::CPANModules::TextTable> catalogs text table modules.
3259              
3260             =head2 Front-ends
3261              
3262             L<Text::Table::Any> and its CLI L<texttable> can use Text::ANSITable as one of
3263             the backends.
3264              
3265             =head2 Other related modules
3266              
3267             L<App::TextTableUtils> includes utilities like L<csv2ansitable> or
3268             L<json2ansitable> which can convert a CSV or array-of-array structure to a table
3269             rendered using Text::ANSITable.
3270              
3271             =head2 Other
3272              
3273             Unix command B<column> (e.g. C<column -t>).
3274              
3275             =head1 AUTHOR
3276              
3277             perlancar <perlancar@cpan.org>
3278              
3279             =head1 CONTRIBUTORS
3280              
3281             =for stopwords Mario Zieschang Steven Haryanto
3282              
3283             =over 4
3284              
3285             =item *
3286              
3287             Mario Zieschang <mario@zieschang.info>
3288              
3289             =item *
3290              
3291             Steven Haryanto <stevenharyanto@gmail.com>
3292              
3293             =back
3294              
3295             =head1 CONTRIBUTING
3296              
3297              
3298             To contribute, you can send patches by email/via RT, or send pull requests on
3299             GitHub.
3300              
3301             Most of the time, you don't need to build the distribution yourself. You can
3302             simply modify the code, then test via:
3303              
3304             % prove -l
3305              
3306             If you want to build the distribution (e.g. to try to install it locally on your
3307             system), you can install L<Dist::Zilla>,
3308             L<Dist::Zilla::PluginBundle::Author::PERLANCAR>, and sometimes one or two other
3309             Dist::Zilla plugin and/or Pod::Weaver::Plugin. Any additional steps required
3310             beyond that are considered a bug and can be reported to me.
3311              
3312             =head1 COPYRIGHT AND LICENSE
3313              
3314             This software is copyright (c) 2022, 2021, 2020, 2018, 2017, 2016, 2015, 2014, 2013 by perlancar <perlancar@cpan.org>.
3315              
3316             This is free software; you can redistribute it and/or modify it under
3317             the same terms as the Perl 5 programming language system itself.
3318              
3319             =head1 BUGS
3320              
3321             Please report any bugs or feature requests on the bugtracker website L<https://rt.cpan.org/Public/Dist/Display.html?Name=Text-ANSITable>
3322              
3323             When submitting a bug or request, please include a test-file or a
3324             patch to an existing test-file that illustrates the bug or desired
3325             feature.
3326              
3327             =cut