1# Text::Table - Organize Data in Tables 2package Text::Table; 3$Text::Table::VERSION = '1.134'; 4use strict; 5use warnings; 6 7use 5.008; 8 9use List::Util qw(sum max); 10 11use Text::Aligner qw(align); 12 13use overload ( 14 # Don't stringify when only doing boolean tests, since stringification can 15 # be expensive for large tables: 16 bool => sub { return 1; }, 17 # Stringify when Table instances are used in most other scalar cases: 18 '""' => 'stringify', 19); 20 21### User interface: How to specify columns and column separators 22 23sub _is_sep { 24 my $datum = shift; 25 26 return 27 ( 28 defined($datum) 29 and 30 ( 31 (ref($datum) eq 'SCALAR') 32 or 33 (ref($datum) eq 'HASH' and $datum->{is_sep}) 34 ) 35 ); 36} 37 38sub _get_sep_title_body 39{ 40 my $sep = shift; 41 42 return 43 +( ref($sep) eq 'HASH' ) 44 ? @{ $sep }{qw(title body)} 45 : split( /\n/, ${$sep}, -1 ) ; 46} 47 48sub _parse_sep { 49 my $sep = shift; 50 51 if (!defined($sep)) 52 { 53 $sep = ''; 54 } 55 56 my ($title, $body) = _get_sep_title_body($sep); 57 58 if (!defined($body)) 59 { 60 $body = $title; 61 } 62 63 ($title, $body) = align( 'left', $title, $body); 64 65 return 66 { 67 is_sep => 1, 68 title => $title, 69 body => $body, 70 }; 71} 72 73sub _default_if_empty 74{ 75 my ($ref, $default) = @_; 76 77 if (! (defined($$ref) && length($$ref))) 78 { 79 $$ref = $default; 80 } 81 82 return; 83} 84 85sub _is_align 86{ 87 my $align = shift; 88 89 return $align =~ /\A(?:left|center|right)/; 90} 91 92sub _parse_spec { 93 my $spec = shift; 94 95 if (!defined($spec)) 96 { 97 $spec = ''; 98 } 99 100 my $alispec = qr/^ *(?:left|center|right|num|point|auto)/; 101 my ( $title, $align, $align_title, $align_title_lines, $sample); 102 if ( ref ($spec) eq 'HASH' ) { 103 ( $title, $align, $align_title, $align_title_lines, $sample) = 104 @{$spec}{qw( title align align_title align_title_lines sample )}; 105 } else { 106 my $alispec = qr/&(.*)/; 107 if ( $spec =~ $alispec ) { 108 ($title, $align, $sample) = ($spec =~ /(.*)^$alispec\n?(.*)/sm); 109 } else { 110 $title = $spec; 111 } 112 for my $s ($title, $sample) 113 { 114 if (defined($s)) 115 { 116 chomp($s); 117 } 118 } 119 } 120 121 # Assign default values. 122 foreach my $x ($title, $sample) 123 { 124 if (!defined($x)) 125 { 126 $x = []; 127 } 128 elsif (ref($x) ne 'ARRAY') 129 { 130 $x = [ split /\n/, $x, -1]; 131 } 132 } 133 134 _default_if_empty(\$align, 'auto'); 135 136 unless ( 137 ref $align eq 'Regexp' or 138 $align =~ /^(?:left|center|right|num\(?|point\(?|auto)/ 139 ) { 140 _warn( "Invalid align specification: '$align', using 'auto'"); 141 $align = 'auto'; 142 } 143 144 _default_if_empty(\$align_title, 'left'); 145 146 if ( ! _is_align($align_title) ) { 147 _warn( "Invalid align_title specification: " . 148 "'$align_title', using 'left'", 149 ); 150 $align_title = 'left'; 151 } 152 153 _default_if_empty(\$align_title_lines, $align_title); 154 155 if ( ! _is_align($align_title_lines) ) { 156 _warn( "Invalid align_title_lines specification: " . 157 "'$align_title_lines', using 'left'", 158 ); 159 $align_title_lines = 'left'; 160 } 161 162 return 163 { 164 title => $title, 165 align => $align, 166 align_title => $align_title, 167 align_title_lines => $align_title_lines, 168 sample => $sample, 169 }; 170} 171 172### table creation 173 174sub new 175{ 176 my $tb = bless {}, shift; 177 178 return $tb->_entitle( [ @_ ] ); 179} 180 181sub _blank 182{ 183 my $self = shift; 184 185 if (@_) 186 { 187 $self->{blank} = shift; 188 } 189 190 return $self->{blank}; 191} 192 193sub _cols 194{ 195 my $self = shift; 196 197 if (@_) 198 { 199 $self->{cols} = shift; 200 } 201 202 return $self->{cols}; 203} 204 205sub _forms 206{ 207 my $self = shift; 208 209 if (@_) 210 { 211 $self->{forms} = shift; 212 } 213 214 return $self->{forms}; 215} 216 217sub _lines 218{ 219 my $self = shift; 220 221 if (@_) 222 { 223 $self->{lines} = shift; 224 } 225 226 return $self->{lines}; 227} 228 229sub _spec 230{ 231 my $self = shift; 232 233 if (@_) 234 { 235 $self->{spec} = shift; 236 } 237 238 return $self->{spec}; 239} 240 241sub _titles 242{ 243 my $self = shift; 244 245 if (@_) 246 { 247 $self->{titles} = shift; 248 } 249 250 return $self->{titles}; 251} 252 253sub _entitle { 254 my ($tb, $sep_list) = @_; # will be completely overwritten 255 # find active separators and, well separate them from col specs. 256 # n+1 separators for n cols 257 my ( @seps, @spec); # separators and column specifications 258 my $sep; 259 foreach my $sep_item ( @{$sep_list} ) { 260 if ( _is_sep ($sep_item) ) { 261 $sep = _parse_sep($sep_item); 262 } else { 263 push @seps, $sep; 264 push @spec, _parse_spec($sep_item); 265 undef $sep; 266 } 267 } 268 push @seps, $sep; 269 # build sprintf formats from separators 270 my $title_form = _compile_field_format('title', \@seps); 271 my $body_form = _compile_field_format('body', \@seps); 272 273 # pre_align titles 274 my @titles = map { [ @{ $_->{title} } ] } @spec; 275 276 my $title_height = max(0, map { scalar(@$_) } @titles); 277 278 foreach my $title (@titles) 279 { 280 push @{$title}, ( '') x ( $title_height - @{$title}); 281 } 282 283 foreach my $t_idx (0 .. $#titles) 284 { 285 align($spec[$t_idx]->{align_title_lines}, @{$titles[$t_idx]}); 286 } 287 288 # build data structure 289 $tb->_spec(\@spec); 290 $tb->_cols([ map [], 1 .. @spec]); 291 $tb->_forms([ $title_form, $body_form]); # separators condensed 292 $tb->_titles(\@titles); 293 294 $tb->_clear_cache; 295 296 return $tb; 297} 298 299# sprintf-format for line assembly, using separators 300sub _compile_format { 301 my $seps = shift; # mix of strings and undef (for default) 302 303 for my $idx (0 .. $#$seps) 304 { 305 if (!defined($seps->[$idx])) 306 { 307 $seps->[$idx] = ($idx == 0 or $idx == $#$seps) ? '' : q{ }; 308 } 309 else 310 { 311 # protect against sprintf 312 $seps->[$idx] =~ s/%/%%/g; 313 } 314 } 315 return join '%s', @$seps; 316} 317 318sub _compile_field_format 319{ 320 my ($field, $seps) = @_; 321 322 return _compile_format( 323 [map { defined($_) ? $_->{$field} : undef } @$seps] 324 ); 325} 326 327# reverse format compilation (used by colrange()) 328sub _recover_separators { 329 my $format = shift; 330 my @seps = split /(?<!%)%s/, $format, -1; 331 for my $s (@seps) 332 { 333 $s =~ s/%%/%/g; 334 } 335 return \@seps; 336} 337 338# select some columns, (optionally if in [...]), and add new separators 339# (the other table creator) 340sub select { 341 my $tb = shift; 342 my @args = map $tb->_select_group( $_), @_; 343 # get column selection, checking indices (some have been checked by 344 # _select_group, but not all) 345 my @sel = map $tb->_check_index( $_), grep !_is_sep( $_), @args; 346 # replace indices with column spec to create subtable 347 for my $arg (@args) 348 { 349 if (! _is_sep($arg)) 350 { 351 $arg = $tb->_spec->[ $arg]; 352 } 353 } 354 my $sub = ref( $tb)->new( @args); 355 # sneak in data columns 356 @{ $sub->{ cols}} = map { [ @$_ ] } @{ $tb->_cols}[ @sel]; 357 $sub; 358} 359 360# the first non-separator column in the group is tested if it has any data 361# if so, the group is returned, else nothing 362sub _select_group { 363 my ( $tb, $group) = @_; 364 return $group unless ref $group eq 'ARRAY'; 365 GROUP_LOOP: 366 for my $g ( @$group ) { 367 if (_is_sep($g)) 368 { 369 next GROUP_LOOP; 370 } 371 $tb->_check_index($g); 372 373 if (grep { $_} @{ $tb->_cols->[$g] }) 374 { 375 return @$group; 376 } 377 return; # no more tries after non-sep was found 378 } 379 return; # no column index in group, no select 380} 381 382# check index for validity, return arg if returns at all 383sub _check_index { 384 my $tb = shift; 385 my ( $i) = @_; 386 my $n = $tb->n_cols; 387 my $ok = eval { 388 use warnings FATAL => 'numeric'; 389 -$n <= $i and $i < $n; # in range of column array? 390 }; 391 _warn( "Invalid column index '$_'") if $@ or not $ok; 392 shift; 393} 394 395### data entry 396 397sub _clear_cache { 398 my ($tb) = @_; 399 400 $tb->_blank(undef()); 401 $tb->_lines(undef()); 402 403 return; 404} 405 406# add one data line or split the line into follow-up lines 407sub add { 408 my $tb = shift; 409 410 if (! $tb->n_cols) { 411 $tb->_entitle( [ ('') x @_] ); 412 } 413 414 foreach my $row ( 415 _transpose( 416 [ 417 map { [ defined() ? split( /\n/ ) : '' ] } @_ 418 ] 419 ) 420 ) 421 { 422 $tb->_add(@$row); 423 } 424 $tb->_clear_cache; 425 426 return $tb; 427} 428 429# add one data line 430sub _add { 431 my $tb = shift; 432 433 foreach my $col ( @{ $tb->_cols} ) { 434 push @{$col}, shift(@_); 435 } 436 437 $tb->_clear_cache; 438 439 return $tb; 440} 441 442# add one or more data lines 443sub load { 444 my $tb = shift; 445 foreach my $row ( @_ ) { 446 if (!defined($row)) { 447 $row = ''; 448 } 449 $tb->add( 450 (ref($row) eq 'ARRAY') ? (@$row) : (split ' ',$row) 451 ) 452 } 453 $tb; 454} 455 456sub clear { 457 my $tb = shift; 458 459 foreach my $col (@{ $tb->_cols} ) 460 { 461 $col = []; 462 } 463 464 $tb->_clear_cache; 465 466 return $tb; 467} 468 469### access to output area 470 471## sizes 472 473# number of table columns 474sub n_cols { scalar @{ $_[0]->{ spec}} } 475 476# number of title lines 477sub title_height { $_[ 0]->n_cols and scalar @{ $_[ 0]->_titles->[ 0]} } 478 479# number of data lines 480sub body_height 481{ 482 my ($tb) = @_; 483 484 return ($tb->n_cols && scalar @{ $tb->_cols->[0] }); 485} 486 487# total height 488sub table_height 489{ 490 my $tb = shift; 491 return $tb->title_height + $tb->body_height; 492} 493 494BEGIN { *height = \&table_height; } # alias 495 496# number of characters in each table line. need to build the table to know 497sub width 498{ 499 my ($tb) = @_; 500 501 return $tb->height && (length( ($tb->table(0))[0] ) - 1); 502} 503 504sub _normalize_col_index 505{ 506 my ($tb, $col_index) = @_; 507 508 $col_index ||= 0; 509 510 if ($col_index < 0) 511 { 512 $col_index += $tb->n_cols; 513 } 514 515 if ($col_index < 0) 516 { 517 $col_index = 0; 518 } 519 elsif ($col_index > $tb->n_cols) 520 { 521 $col_index = $tb->n_cols; 522 } 523 524 return $col_index; 525} 526 527# start and width of each column 528sub colrange { 529 my ( $tb, $proto_col_index) = @_; 530 531 my $col_index = $tb->_normalize_col_index($proto_col_index); 532 533 return ( 0, 0) unless $tb->width; # width called, $tb->_blank() exists now 534 my @widths = map { length } @{ $tb->_blank}, ''; 535 @widths = @widths[ 0 .. $col_index]; 536 537 my $width = pop @widths; 538 my $pos = sum(@widths) || 0; 539 540 my $seps_aref = _recover_separators( $tb->_forms->[ 0]); 541 542 my $sep_sum = 0; 543 foreach my $sep (@$seps_aref[ 0 .. $col_index]) 544 { 545 $sep_sum += length($sep); 546 } 547 548 return ( $pos+$sep_sum, $width ) ; 549} 550 551## printable output 552 553# whole table 554sub table { 555 my $tb = shift; 556 557 return $tb->_table_portion( $tb->height, 0, @_); 558} 559 560# only titles 561sub title { 562 my $tb = shift; 563 564 return $tb->_table_portion( $tb->title_height, 0, @_); 565} 566 567# only body 568sub body { 569 my $tb = shift; 570 571 return $tb->_table_portion( $tb->body_height, $tb->title_height, @_); 572} 573 574sub stringify 575{ 576 my ($tb) = @_; 577 578 return (scalar ( $tb->table() )); 579} 580 581### common internals 582 583# common representation of table(), title() and body() 584 585sub _table_portion_as_aref 586{ 587 my $tb = shift; 588 589 my $total = shift; 590 my $offset = shift; 591 592 my ( $from, $n) = ( 0, $total); # if no parameters 593 594 if ( @_ ) { 595 $from = shift; 596 $n = @_ ? shift : 1; # one line if not given 597 } 598 599 ( $from, $n) = _limit_range( $total, $from, $n); 600 601 my $limit = $tb->title_height; # title format below 602 $from += $offset; 603 604 return 605 [ 606 map $tb->_assemble_line( $_ >= $limit, $tb->_table_line( $_), 0), 607 $from .. $from + $n - 1 608 ]; 609} 610 611sub _table_portion 612{ 613 my $tb = shift; 614 615 my $lines_aref = $tb->_table_portion_as_aref(@_); 616 617 return (wantarray ? @$lines_aref : join('', @$lines_aref)); 618} 619 620sub _limit_range 621{ 622 my ( $total, $from, $n) = @_; 623 624 $from ||= 0; 625 $from += $total if $from < 0; 626 $n = $total unless defined $n; 627 628 return ( 0, 0) if $from + $n < 0 or $from >= $total; 629 630 $from = 0 if $from < 0; 631 $n = $total - $from if $n > $total - $from; 632 633 return( $from, $n); 634} 635 636# get table line (formatted, including titles). fill cache if needed 637sub _table_line { 638 my ($tb, $idx) = @_; 639 640 if (! $tb->_lines) 641 { 642 $tb->_lines([ $tb->_build_table_lines ]); 643 } 644 645 return $tb->_lines->[$idx]; 646} 647 648# build array of lines of justified data items 649sub _build_table_lines { 650 my $tb = shift; 651 652 # copy data columns, replacing undef with '' 653 my @cols = 654 map 655 { [ map { defined($_) ? $_ : '' } @$_ ] } 656 @{ $tb->_cols() } ; 657 658 # add set of empty strings for blank line (needed to build horizontal rules) 659 foreach my $col (@cols) 660 { 661 push @$col, ''; 662 } 663 664 # add samples for minimum alignment 665 foreach my $col_idx (0 .. $#cols) 666 { 667 push @{$cols[$col_idx]}, @{$tb->_spec->[$col_idx]->{sample}}; 668 } 669 670 # align to style 671 foreach my $col_idx (0 .. $#cols) 672 { 673 align($tb->_spec->[$col_idx]->{align}, @{$cols[$col_idx]}); 674 } 675 676 # trim off samples, but leave blank line 677 foreach my $col (@cols) 678 { 679 splice( @{$col}, 1 + $tb->body_height ); 680 } 681 682 # include titles 683 foreach my $col_idx (0 .. $#cols) 684 { 685 unshift @{$cols[$col_idx]}, @{$tb->_titles->[$col_idx]}; 686 } 687 688 # align title and body portions of columns 689 # blank line will be there even with no data 690 foreach my $col_idx (0 .. $#cols) 691 { 692 align($tb->_spec->[$col_idx]->{align_title}, @{$cols[$col_idx]}); 693 } 694 695 # deposit a blank line, pulling it off the columns. 696 # *_rule() knows this is done 697 my @blank; 698 699 foreach my $col (@cols) 700 { 701 push @blank, pop(@$col); 702 } 703 704 $tb->_blank(\@blank); 705 706 return _transpose_n( $tb->height, \@cols); # bye-bye, @cols 707} 708 709# destructively transpose a number of lines/cols from an array of arrayrefs 710sub _transpose_n { 711 my ($n, $cols) = @_; 712 713 return map { [ map { shift @$_ } @$cols] } 1 .. $n; 714} 715 716# like _transpose_n, but find the number to transpose from max of given 717sub _transpose 718{ 719 my ($cols) = @_; 720 721 my $m = max ( map { scalar(@$_) } @$cols, []); 722 723 return _transpose_n( $m, $cols); 724} 725 726# make a line from a number of formatted data elements 727sub _assemble_line { 728 my ($tb, $in_body, $line_aref, $replace_spaces) = @_; 729 730 my $format = $tb->_forms->[ !!$in_body]; 731 732 if ($replace_spaces) 733 { 734 $format =~ s/\s/=/g; 735 } 736 737 return sprintf($format, @$line_aref) . "\n"; 738} 739 740sub _text_rule 741{ 742 my ($tb, $rule, $char, $alt) = @_; 743 744 # replace blanks with $char. If $alt is given, replace nonblanks 745 # with $alt 746 if ( defined $alt ) 747 { 748 $rule =~ s/(.)/$1 eq ' ' ? $char : $alt/ge; 749 } 750 else 751 { 752 $rule =~ s/ /$char/g if $char ne ' '; 753 } 754 755 return $rule; 756} 757 758# build a rule line 759sub _rule { 760 my $tb = shift; 761 762 return + (!$tb->width) ? '' : $tb->_positive_width_rule(@_); 763} 764 765sub _positive_width_rule 766{ 767 my ($tb, $in_body, $char, $alt) = @_; 768 769 my $rule = $tb->_assemble_line( $in_body, $tb->_blank, 770 ((ref($char) eq "CODE") ? 1 : 0), 771 ); 772 773 return $tb->_render_rule($rule, $char, $alt); 774} 775 776sub _render_rule 777{ 778 my ($tb, $rule, $char, $alt) = @_; 779 780 if (ref($char) eq "CODE") 781 { 782 return $tb->_render_rule_with_callbacks($rule, $char, $alt); 783 } 784 else 785 { 786 _default_if_empty(\$char, ' '); 787 788 return $tb->_text_rule($rule, $char, $alt); 789 } 790} 791 792sub _get_fixed_len_string 793{ 794 my ($s, $len) = @_; 795 796 $s = substr($s, 0, $len); 797 $s .= ' ' x ($len - length($s)); 798 799 return $s; 800} 801 802sub _render_rule_with_callbacks 803{ 804 my ($tb, $rule, $char, $alt) = @_; 805 806 my %callbacks = 807 ( 808 'char' => { cb => $char, idx => 0, }, 809 'alt' => { cb => $alt, idx => 0, }, 810 ); 811 812 my $calc_substitution = sub { 813 my $s = shift; 814 815 my $len = length($s); 816 817 my $which = (($s =~ /\A /) ? 'char' : 'alt'); 818 my $rec = $callbacks{$which}; 819 820 return _get_fixed_len_string( 821 scalar ($rec->{cb}->($rec->{idx}++, $len)), 822 $len, 823 ); 824 }; 825 826 $rule =~ s/((.)\2*)/$calc_substitution->($1)/ge; 827 828 return $rule; 829} 830 831sub rule { 832 my $tb = shift; 833 return $tb->_rule( 0, @_); 834} 835 836sub body_rule { 837 my $tb = shift; 838 return $tb->_rule( 1, @_); 839} 840 841### warning behavior 842use Carp; 843 844{ 845 my ( $warn, $fatal) = ( 0, 0); 846 847 sub warnings 848 { 849 # Ignore the class/object. 850 my (undef, $toggle) = @_; 851 852 $toggle ||= 'on'; 853 if ( $toggle eq 'off' ) 854 { 855 ($warn, $fatal) = (0, 0); 856 } 857 elsif ( $toggle eq 'fatal' ) 858 { 859 ($warn, $fatal) = (1, 1); 860 } 861 else 862 { 863 ($warn, $fatal) = (1, 0); 864 } 865 return $fatal ? 'fatal' : $warn ? 'on' : 'off'; 866 } 867 868 sub _warn 869 { 870 my $msg = shift; 871 872 return unless $warn; 873 874 if ($fatal) 875 { 876 croak( $msg) 877 } 878 879 carp( $msg); 880 881 return; 882 } 883} 884 885=pod 886 887=encoding utf-8 888 889=head1 NAME 890 891Text::Table - Organize Data in Tables 892 893=head1 VERSION 894 895version 1.134 896 897=head1 SYNOPSIS 898 899 use Text::Table; 900 my $tb = Text::Table->new( 901 "Planet", "Radius\nkm", "Density\ng/cm^3" 902 ); 903 $tb->load( 904 [ "Mercury", 2360, 3.7 ], 905 [ "Venus", 6110, 5.1 ], 906 [ "Earth", 6378, 5.52 ], 907 [ "Jupiter", 71030, 1.3 ], 908 ); 909 print $tb; 910 911This prints a table from the given title and data like this: 912 913 Planet Radius Density 914 km g/cm^3 915 Mercury 2360 3.7 916 Venus 6110 5.1 917 Earth 6378 5.52 918 Jupiter 71030 1.3 919 920Note that two-line titles work, and that the planet names are aligned 921differently than the numbers. 922 923=head1 DESCRIPTION 924 925Organization of data in table form is a time-honored and useful 926method of data representation. While columns of data are trivially 927generated by computer through formatted output, even simple tasks 928like keeping titles aligned with the data columns are not trivial, 929and the one-shot solutions one comes up with tend to be particularly 930hard to maintain. Text::Table allows you to create and maintain 931tables that adapt to alignment requirements as you use them. 932 933=head2 Overview 934 935The process is simple: you create a table (a Text::Table object) by 936describing the columns the table is going to have. Then you load 937lines of data into the table, and finally print the resulting output 938lines. Alignment of data and column titles is handled dynamically 939in dependence on the data present. 940 941=head2 Table Creation 942 943In the simplest case, if all you want is a number of (untitled) columns, 944you create an unspecified table and start adding data to it. The number 945of columns is taken from the first line of data. 946 947To specify a table you specify its columns. A column description 948can contain a title and alignment requirements for the data, both 949optional. Additionally, you can specify how the title is aligned with 950the body of a column, and how the lines of a multiline title are 951aligned among themselves. 952 953The columns are collected in the table in the 954order they are given. On data entry, each column corresponds to 955one data item, and in column selection columns are indexed left to 956right, starting from 0. 957 958Each title can be a multiline string which will be blank-filled to 959the length of the longest partial line. The largest number of title 960lines in a column determines how many title lines the table has as a 961whole, including the case that no column has any titles. 962 963On output, columns are separated by a single blank. You can control 964what goes between columns by specifying separators between (or before, 965or after) columns. Separators don't contain any data and don't count 966in column indexing. They also don't accumulate: in a sequence of only 967separators and no columns, only the last one counts. 968 969=head2 Status Information 970 971The width (in characters), height (in lines), number of columns, and 972similar data about the table is available. 973 974=head2 Data Loading 975 976Table data is entered line-wise, each time specifying data entries 977for all table columns. A bulk loader for many lines at once is also 978available. You can clear the data from the table for re-use (though 979you will more likely just create another table). 980 981Data can contain colorizing escape sequences (as provided by 982C<Term::AnsiColor>) without upsetting the alignment. 983 984=head2 Table Output 985 986The output area of a table is divided in the title and the body. 987 988The title contains the combined titles from the table columns, if 989any. Its content never changes with a given table, but it may be 990spread out differently on the page through alignment with the data. 991 992The body contains the data lines, aligned column-wise as specified, 993and left-aligned with the column title. 994 995Each of these is arranged like a Perl array (counting from 0) and can 996be accessed in portions by specifying a first line and the number 997of following lines. Also like an array, giving a negative first line 998counts from the end of the area. The whole table, the title followed 999by the body, can also be accessed in this manner. 1000 1001The subdivisions are there so you can repeat the title (or parts of 1002it) along with parts of the body on output, whether for screen paging 1003or printout. 1004 1005A rule line is also available, which is the horizontal counterpart to 1006the separator columns you specify with the table. 1007It is basically a table line as it would appear if all data entries 1008in the line were empty, that is, a blank line except for where the 1009column separators have non-blank entries. If you print it between 1010data lines, it will not disrupt the vertical separator structure 1011as a plain blank line would. You can also request a solid rule 1012consisting of any character, and even one with the non-blank column 1013separators replaced by a character of your choice. This way you can 1014get the popular representation of line-crossings like so: 1015 1016 | 1017 ----+--- 1018 | 1019 1020=head2 Warning Control 1021 1022On table creation, some parameters are checked and warnings issued 1023if you allow warnings. You can also turn warnings into fatal errors. 1024 1025=head1 VERSION 1026 1027version 1.134 1028 1029=head1 SPECIFICATIONS 1030 1031=head2 Column Specification 1032 1033Each column specification is a single scalar. Columns can be either proper 1034data columns or column separators. Both can be specified either as 1035(possibly multi-line) strings, or in a more explicit form as hash-refs. 1036In the string form, proper columns are given as plain strings, and 1037separators are given as scalar references to strings. In hash form, 1038separators have a true value in the field C<is_sep> while proper columns 1039don't have this field. 1040 1041=over 4 1042 1043=item Columns as strings 1044 1045A column is given as a column title (any number of lines), 1046optionally followed by alignment requirements. Alignment requirements 1047start with a line that begins with an ampersand "&". However, only the 1048last such line counts as such, so if you have title lines that begin 1049with "&", just append an ampersand on a line by itself as a dummy 1050alignment section if you don't have one anyway. 1051 1052What follows the ampersand on its line is the alignment style (like 1053I<left>, I<right>, ... as described in L<"Alignment">), you want for 1054the data in this column. If nothing follows, the general default I<auto> 1055is used. If you specify an invalid alignment style, it falls back to 1056left alignment. 1057 1058The lines that follow can contain sample data for this column. These 1059are considered for alignment in the column, but never actually appear 1060in the output. The effect is to guarantee a minimum width for the 1061column even if the current data doesn't require it. This helps dampen 1062the oscillations in the appearance of dynamically aligned tables. 1063 1064=item Columns as Hashes 1065 1066The format is 1067 1068 { 1069 title => $title, 1070 align => $align, 1071 sample => $sample, 1072 align_title => $align_title, 1073 align_title_lines => $align_title_lines, 1074 } 1075 1076$title contains the title lines and $sample the sample data. Both can 1077be given as a string or as an array-ref to the list of lines. $align contains 1078the alignment style (without a leading ampersand), usually as a string. 1079You can also give a regular expression here, which specifies regex alignment. 1080A regex can only be specified in the hash form of a column specification. 1081 1082In hash form you can also specify how the title of a column is aligned 1083with its body. To do this, you specify the keyword C<align_title> with 1084C<left>, C<right> or C<center>. Other alignment specifications are not 1085valid here. The default is C<left>. 1086 1087C<align_title> also specifies how the lines of a multiline title are 1088aligned among themselves. If you want a different alignment, you 1089can specify it with the key C<align_title_lines>. Again, only C<left>, 1090C<right> or C<center> are allowed. 1091 1092Do not put other keys than those mentioned above (I<title>, I<align>, 1093I<align_title>, I<align_title_lines>, and I<sample>) into a hash that 1094specifies a column. Most would be ignored, but some would confuse the 1095interpreter (in particular, I<is_sep> has to be avoided). 1096 1097=item Separators as strings 1098 1099A separator must be given as a reference to a string (often a literal, 1100like C<\' | '>), any string that is given directly describes a column. 1101 1102It is usually just a (short) string that will be printed between 1103table columns on all table lines instead of the default single 1104blank. If you specify two separators (on two lines), the first one 1105will be used in the title and the other in the body of the table. 1106 1107=item Separators as Hashes 1108 1109The hash representation of a separator has the format 1110 1111 { 1112 is_sep => 1, 1113 title => $title, 1114 body => $body, 1115 } 1116 1117$title is the separator to be used in the title area and $body 1118the one for the body. If only one is given, it will be used for 1119both. If none is given, a blank is used. If one is shorter than 1120the other, it is blank filled on the right. 1121 1122The value of C<is_sep> must be set to a true value, this is the 1123distinguishing feature of a separator. 1124 1125=back 1126 1127=head2 Alignment 1128 1129The original documentation to L<Text::Aligner> contains all the details 1130on alignment specification, but here is the rundown: 1131 1132The possible alignment specifications are I<left>, I<right>, I<center>, 1133I<num> and I<point> (which are synonyms), and I<auto>. The first 1134three explain themselves. 1135 1136I<num> (and I<point>) align the decimal point in the data, which is 1137assumed to the right if none is present. Strings that aren't 1138numbers are treated the same way, that is, they appear aligned 1139with the integers unless they contain a ".". Instead of the 1140decimal point ".", you can also specify any other string in 1141the form I<num(,)>, for instance. The string in parentheses 1142is aligned in the data. The synonym I<point> for I<num> may be 1143more appropriate in contexts that deal with arbitrary 1144strings, as in I<point(=E<gt>)> (which might be used to align certain 1145bits of Perl code). 1146 1147I<regex alignment> is a more sophisticated form of point alignment. 1148If you specify a regular expression, as delivered by C<qr//>, the start 1149of the match is used as the alignment point. If the regex contains 1150capturing parentheses, the last submatch counts. [The usefulness of 1151this feature is under consideration.] 1152 1153I<auto> alignment combines numeric alignment with left alignment. 1154Data items that look like numbers, and those that don't, form two 1155virtual columns and are aligned accordingly: C<num> for numbers and 1156C<left> for other strings. These columns are left-aligned with 1157each other (i.e. the narrower one is blank-filled) to form the 1158final alignment. 1159 1160This way, a column that happens to have only numbers in the data gets 1161I<num> alignment, a column with no numbers appears I<left>-aligned, 1162and mixed data is presented in a reasonable way. 1163 1164=head2 Column Selection 1165 1166Besides creating tables from scratch, they can be created by 1167selecting columns from an existing table. Tables created this 1168way contain the data from the columns they were built from. 1169 1170This is done by specifying the columns to select by their index 1171(where negative indices count backward from the last column). 1172The same column can be selected more than once and the sequence 1173of columns can be arbitrarily changed. Separators don't travel 1174with columns, but can be specified between the columns at selection 1175time. 1176 1177You can make the selection of one or more columns dependent on 1178the data content of one of them. If you specify some of the columns 1179in angle brackets [...], the whole group is only included in the 1180selection if the first column in the group contains any data that 1181evaluates to boolean true. That way you can de-select parts of a 1182table if it contains no interesting data. Any column separators 1183given in brackets are selected or deselected along with the rest 1184of it. 1185 1186=head1 PUBLIC METHODS 1187 1188=head2 Table Creation 1189 1190=over 4 1191 1192=item new() 1193 1194 my $tb = Text::Table->new( $column, ... ); 1195 1196creates a table with the columns specified. A column can be proper column 1197which contains and displays data, or a separator which tells how to fill 1198the space between columns. The format of the parameters is described under 1199L<"Column Specification">. Specifying an invalid alignment for a column 1200results in a warning if these are allowed. 1201 1202If no columns are specified, the number of columns is taken from the first 1203line of data added to the table. The effect is as if you had specified 1204C<Text::Table-E<gt>new( ( '') x $n)>, where C<$n> is the number of 1205columns. 1206 1207=item select() 1208 1209 my $sub = $tb->select( $column, ...); 1210 1211creates a table from the listed columns of the table $tb, including 1212the data. Columns are specified as integer indices which refer to 1213the data columns of $tb. Columns can be repeated and specified in any 1214order. Negative indices count from the last column. If an invalid 1215index is specified, a warning is issued, if allowed. 1216 1217As with L<"new()">, separators can be interspersed among the column 1218indices and will be used between the columns of the new table. 1219 1220If you enclose some of the arguments (column indices or separators) in 1221angle brackets C<[...]> (technically, you specify them inside an 1222arrayref), they form a group for conditional selection. The group is 1223only included in the resulting table if the first actual column inside 1224the group contains any data that evaluate to a boolean true. This way 1225you can exclude groups of columns that wouldn't contribute anything 1226interesting. Note that separators are selected and de-selected with 1227their group. That way, more than one separator can appear between 1228adjacent columns. They don't add up, but only the rightmost separator 1229is used. A group that contains only separators is never selected. 1230[Another feature whose usefulness is under consideration.] 1231 1232=back 1233 1234=head2 Status Information 1235 1236=over 4 1237 1238=item n_cols() 1239 1240 $tb->n_cols 1241 1242returns the number of columns in the table. 1243 1244=item width() 1245 1246 $tb->width 1247 1248returns the width (in characters) of the table. All table lines have 1249this length (not counting a final "\n" in the line), as well as the 1250separator lines returned by $tb->rule() and $b->body_rule(). 1251The width of a table can potentially be influenced by any data item 1252in it. 1253 1254=item height() 1255 1256 $tb->height 1257 1258returns the total number of lines in a table, including title lines 1259and body lines. For orthogonality, the synonym table_height() also 1260exists. 1261 1262=item table_height() 1263 1264Same as C<< $table->height() >>. 1265 1266=item title_height() 1267 1268 $tb->title_height 1269 1270returns the number of title lines in a table. 1271 1272=item body_height() 1273 1274 $tb->body_height 1275 1276returns the number of lines in the table body. 1277 1278=item colrange() 1279 1280 $tb->colrange( $i) 1281 1282returns the start position and width of the $i-th column (counting from 0) 1283of the table. If $i is negative, counts from the end of the table. If $i 1284is larger than the greatest column index, an imaginary column of width 0 1285is assumed right of the table. 1286 1287=back 1288 1289=head2 Data Loading 1290 1291=over 4 1292 1293=item add() 1294 1295 $tb->add( $col1, ..., $colN) 1296 1297adds a data line to the table, returns the table. 1298 1299C<$col1>, ..., C<$colN> are scalars that 1300correspond to the table columns. Undefined entries are converted to '', 1301and extra data beyond the number of table columns is ignored. 1302 1303Data entries can be multi-line strings. The partial strings all go into 1304the same column. The corresponding fields of other columns remain empty 1305unless there is another multi-line entry in that column that fills the 1306fields. Adding a line with multi-line entries is equivalent to adding 1307multiple lines. 1308 1309Every call to C<add()> increases the body height of the table by the 1310number of effective lines, one in the absence of multiline entries. 1311 1312=item load() 1313 1314 $tb->load( $line, ...) 1315 1316loads the data lines given into the table, returns the table. 1317 1318Every argument to C<load()> represents a data line to be added to the 1319table. The line can be given as an array(ref) containing the data 1320items, or as a string, which is split on whitespace to retrieve the 1321data. If an undefined argument is given, it is treated as an 1322empty line. 1323 1324=item clear() 1325 1326 $tb->clear; 1327 1328deletes all data from the table and resets it to the state after 1329creation. Returns the table. The body height of a table is 0 after 1330C<clear()>. 1331 1332=back 1333 1334=head2 Table Output 1335 1336The three methods C<table()>, C<title()>, and C<body()> are very similar. 1337They access different parts of the printable output lines of a table with 1338similar methods. The details are described with the C<table()> method. 1339 1340=over 4 1341 1342=item table() 1343 1344The C<table()> method returns lines from the entire table, starting 1345with the first title line and ending with the last body line. 1346 1347In array context, the lines are returned separately, in scalar context 1348they are joined together in a single string. 1349 1350 my @lines = $tb->table; 1351 my $line = $tb->table( $line_number); 1352 my @lines = $tb->table( $line_number, $n); 1353 1354The first call returns all the lines in the table. The second call 1355returns one line given by $line_number. The third call returns $n 1356lines, starting with $line_number. If $line_number is negative, it 1357counts from the end of the array. Unlike the C<select()> method, 1358C<table()> (and its sister methods C<title()> and C<body()>) is 1359protected against large negative line numbers, it truncates the 1360range described by $line_number and $n to the existing lines. If 1361$n is 0 or negative, no lines are returned (an empty string in scalar 1362context). 1363 1364=item stringify() 1365 1366Returns a string representation of the table. This method is called for 1367stringification by overload. 1368 1369 my @table_strings = map { $_->stringify() } @tables; 1370 1371=item title() 1372 1373Returns lines from the title area of a table, where the column titles 1374are rendered. Parameters and response to context are as with C<table()>, 1375but no lines are returned from outside the title area. 1376 1377=item body() 1378 1379Returns lines from the body area of a table, that is the part where 1380the data content is rendered, so that $tb->body( 0) is the first data 1381line. Parameters and response to context are as with C<table()>. 1382 1383=item rule() 1384 1385 $tb->rule; 1386 $tb->rule( $char); 1387 $tb->rule( $char, $char1); 1388 $tb->rule( sub { my ($index, $len) = @_; }, 1389 sub { my ($index, $len) = @_; }, 1390 ); 1391 1392Returns a rule for the table. 1393 1394A rule is a line of table width that can be used between table lines 1395to provide visual horizontal divisions, much like column separators 1396provide vertical visual divisions. In its basic form (returned by the 1397first call) it looks like a table line with no data, hence a blank 1398line except for the non-blank parts of any column-separators. If 1399one character is specified (the second call), it replaces the blanks 1400in the first form, but non-blank column separators are retained. If 1401a second character is specified, it replaces the non-blank parts of 1402the separators. So specifying the same character twice gives a solid 1403line of table width. Another useful combo is C<$tb-E<gt>rule( '-', '+')>, 1404together with separators that contain a single nonblank "|", for a 1405popular representation of line crossings. 1406 1407C<rule()> uses the column separators for the title section if there 1408is a difference. 1409 1410If callbacks are specified instead of the characters, then they receive the 1411index of the section of the rule they need to render and its desired length in 1412characters, and should return the string to put there. The indexes given 1413are 0 based (where 0 is either the left column separator or the leftmost 1414cell) and the strings will be trimmed or extended in the replacement. 1415 1416=item body_rule() 1417 1418C<body_rule()> works like <rule()>, except the rule is generated using 1419the column separators for the table body. 1420 1421=back 1422 1423=head2 Warning Control 1424 1425=over 4 1426 1427=item warnings() 1428 1429 Text::Table->warnings(); 1430 Text::Table->warnings( 'on'); 1431 Text::Table->warnings( 'off'): 1432 Text::Table->warnings( 'fatal'): 1433 1434The C<warnings()> method is used to control the appearance of warning 1435messages while tables are manipulated. When Text::Table starts, warnings 1436are disabled. The default action of C<warnings()> is to turn warnings 1437on. The other possible arguments are self-explanatory. C<warnings()> 1438can also be called as an object method (C<$tb-E<gt>warnings( ...)>). 1439 1440=back 1441 1442=head1 VERSION 1443 1444This document pertains to Text::Table version 1.127 1445 1446=head1 BUGS 1447 1448=over 4 1449 1450=item o 1451 1452I<auto> alignment doesn't support alternative characters for the decimal 1453point. This is actually a bug in the underlying Text::Aligner by the 1454same author. 1455 1456=back 1457 1458=head1 AUTHOR 1459 1460=head2 MAINTAINER 1461 1462Shlomi Fish, L<http://www.shlomifish.org/> - CPAN ID: "SHLOMIF". 1463 1464=head2 ORIGINAL AUTHOR 1465 1466 Anno Siegel 1467 CPAN ID: ANNO 1468 siegel@zrz.tu-berlin.de 1469 http://www.tu-berlin.de/~siegel 1470 1471=head1 COPYRIGHT 1472 1473Copyright (c) 2002 Anno Siegel. All rights reserved. 1474This program is free software; you can redistribute 1475it and/or modify it under the terms of the ISC license. 1476 1477(This program had been licensed under the same terms as Perl itself up to 1478version 1.118 released on 2011, and was relicensed by permission of its 1479originator). 1480 1481The full text of the license can be found in the 1482LICENSE file included with this module. 1483 1484=head1 SEE ALSO 1485 1486L<Text::Aligner>, L<perl(1)> . 1487 1488=head1 EXAMPLES 1489 1490=head2 center align and Unicode output 1491 1492 #!/usr/bin/perl 1493 1494 use strict; 1495 use warnings; 1496 use utf8; 1497 1498 use Text::Table (); 1499 1500 binmode STDOUT, ':encoding(utf8)'; 1501 1502 my @cols = qw/First Last/; 1503 push @cols, 1504 +{ 1505 title => "Country", 1506 align => "center", 1507 }; 1508 my $sep = \'│'; 1509 1510 my $major_sep = \'║'; 1511 my $tb = Text::Table->new( $sep, " Number ", $major_sep, 1512 ( map { +( ( ref($_) ? $_ : " $_ " ), $sep ) } @cols ) ); 1513 1514 my $num_cols = @cols; 1515 1516 $tb->load( [ 1, "Mark", "Twain", "USA", ] ); 1517 $tb->load( [ 2, "Charles", "Dickens", "Great Britain", ] ); 1518 $tb->load( [ 3, "Jules", "Verne", "France", ] ); 1519 1520 my $make_rule = sub { 1521 my ($args) = @_; 1522 1523 my $left = $args->{left}; 1524 my $right = $args->{right}; 1525 my $main_left = $args->{main_left}; 1526 my $middle = $args->{middle}; 1527 1528 return $tb->rule( 1529 sub { 1530 my ( $index, $len ) = @_; 1531 1532 return ( '─' x $len ); 1533 }, 1534 sub { 1535 my ( $index, $len ) = @_; 1536 1537 my $char = ( 1538 ( $index == 0 ) ? $left 1539 : ( $index == 1 ) ? $main_left 1540 : ( $index == $num_cols + 1 ) ? $right 1541 : $middle 1542 ); 1543 1544 return $char x $len; 1545 }, 1546 ); 1547 }; 1548 1549 my $start_rule = $make_rule->( 1550 { 1551 left => '┌', 1552 main_left => '╥', 1553 right => '┐', 1554 middle => '┬', 1555 } 1556 ); 1557 1558 my $mid_rule = $make_rule->( 1559 { 1560 left => '├', 1561 main_left => '╫', 1562 right => '┤', 1563 middle => '┼', 1564 } 1565 ); 1566 1567 my $end_rule = $make_rule->( 1568 { 1569 left => '└', 1570 main_left => '╨', 1571 right => '┘', 1572 middle => '┴', 1573 } 1574 ); 1575 1576 print $start_rule, $tb->title, 1577 ( map { $mid_rule, $_, } $tb->body() ), $end_rule; 1578 1579This emits the following output: 1580 1581 ┌────────╥───────┬───────┬─────────────┐ 1582 │ Number ║ First │ Last │Country │ 1583 ├────────╫───────┼───────┼─────────────┤ 1584 │1 ║Mark │Twain │ USA │ 1585 ├────────╫───────┼───────┼─────────────┤ 1586 │2 ║Charles│Dickens│Great Britain│ 1587 ├────────╫───────┼───────┼─────────────┤ 1588 │3 ║Jules │Verne │ France │ 1589 └────────╨───────┴───────┴─────────────┘ 1590 1591=for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan 1592 1593=head1 SUPPORT 1594 1595=head2 Websites 1596 1597The following websites have more information about this module, and may be of help to you. As always, 1598in addition to those websites please use your favorite search engine to discover more resources. 1599 1600=over 4 1601 1602=item * 1603 1604MetaCPAN 1605 1606A modern, open-source CPAN search engine, useful to view POD in HTML format. 1607 1608L<https://metacpan.org/release/Text-Table> 1609 1610=item * 1611 1612RT: CPAN's Bug Tracker 1613 1614The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN. 1615 1616L<https://rt.cpan.org/Public/Dist/Display.html?Name=Text-Table> 1617 1618=item * 1619 1620CPANTS 1621 1622The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution. 1623 1624L<http://cpants.cpanauthors.org/dist/Text-Table> 1625 1626=item * 1627 1628CPAN Testers 1629 1630The CPAN Testers is a network of smoke testers who run automated tests on uploaded CPAN distributions. 1631 1632L<http://www.cpantesters.org/distro/T/Text-Table> 1633 1634=item * 1635 1636CPAN Testers Matrix 1637 1638The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms. 1639 1640L<http://matrix.cpantesters.org/?dist=Text-Table> 1641 1642=item * 1643 1644CPAN Testers Dependencies 1645 1646The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution. 1647 1648L<http://deps.cpantesters.org/?module=Text::Table> 1649 1650=back 1651 1652=head2 Bugs / Feature Requests 1653 1654Please report any bugs or feature requests by email to C<bug-text-table at rt.cpan.org>, or through 1655the web interface at L<https://rt.cpan.org/Public/Bug/Report.html?Queue=Text-Table>. You will be automatically notified of any 1656progress on the request by the system. 1657 1658=head2 Source Code 1659 1660The code is open to the world, and available for you to hack on. Please feel free to browse it and play 1661with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull 1662from your repository :) 1663 1664L<https://github.com/shlomif/Text-Table> 1665 1666 git clone git://github.com/shlomif/Text-Table.git 1667 1668=head1 AUTHOR 1669 1670Shlomi Fish <shlomif@cpan.org> 1671 1672=head1 BUGS 1673 1674Please report any bugs or feature requests on the bugtracker website 1675L<https://github.com/shlomif/Text-Table/issues> 1676 1677When submitting a bug or request, please include a test-file or a 1678patch to an existing test-file that illustrates the bug or desired 1679feature. 1680 1681=head1 COPYRIGHT AND LICENSE 1682 1683This software is Copyright (c) 2002 by Anno Siegel and others. 1684 1685This is free software, licensed under: 1686 1687 The ISC License 1688 1689=for :stopwords cpan testmatrix url bugtracker rt cpants kwalitee diff irc mailto metadata placeholders metacpan 1690 1691=head1 SUPPORT 1692 1693=head2 Websites 1694 1695The following websites have more information about this module, and may be of help to you. As always, 1696in addition to those websites please use your favorite search engine to discover more resources. 1697 1698=over 4 1699 1700=item * 1701 1702MetaCPAN 1703 1704A modern, open-source CPAN search engine, useful to view POD in HTML format. 1705 1706L<https://metacpan.org/release/Text-Table> 1707 1708=item * 1709 1710RT: CPAN's Bug Tracker 1711 1712The RT ( Request Tracker ) website is the default bug/issue tracking system for CPAN. 1713 1714L<https://rt.cpan.org/Public/Dist/Display.html?Name=Text-Table> 1715 1716=item * 1717 1718CPANTS 1719 1720The CPANTS is a website that analyzes the Kwalitee ( code metrics ) of a distribution. 1721 1722L<http://cpants.cpanauthors.org/dist/Text-Table> 1723 1724=item * 1725 1726CPAN Testers 1727 1728The CPAN Testers is a network of smoke testers who run automated tests on uploaded CPAN distributions. 1729 1730L<http://www.cpantesters.org/distro/T/Text-Table> 1731 1732=item * 1733 1734CPAN Testers Matrix 1735 1736The CPAN Testers Matrix is a website that provides a visual overview of the test results for a distribution on various Perls/platforms. 1737 1738L<http://matrix.cpantesters.org/?dist=Text-Table> 1739 1740=item * 1741 1742CPAN Testers Dependencies 1743 1744The CPAN Testers Dependencies is a website that shows a chart of the test results of all dependencies for a distribution. 1745 1746L<http://deps.cpantesters.org/?module=Text::Table> 1747 1748=back 1749 1750=head2 Bugs / Feature Requests 1751 1752Please report any bugs or feature requests by email to C<bug-text-table at rt.cpan.org>, or through 1753the web interface at L<https://rt.cpan.org/Public/Bug/Report.html?Queue=Text-Table>. You will be automatically notified of any 1754progress on the request by the system. 1755 1756=head2 Source Code 1757 1758The code is open to the world, and available for you to hack on. Please feel free to browse it and play 1759with it, or whatever. If you want to contribute patches, please send me a diff or prod me to pull 1760from your repository :) 1761 1762L<https://github.com/shlomif/Text-Table> 1763 1764 git clone git://github.com/shlomif/Text-Table.git 1765 1766=head1 AUTHOR 1767 1768Shlomi Fish <shlomif@cpan.org> 1769 1770=head1 BUGS 1771 1772Please report any bugs or feature requests on the bugtracker website 1773L<https://github.com/shlomif/Text-Table/issues> 1774 1775When submitting a bug or request, please include a test-file or a 1776patch to an existing test-file that illustrates the bug or desired 1777feature. 1778 1779=head1 COPYRIGHT AND LICENSE 1780 1781This software is Copyright (c) 2002 by Anno Siegel and others. 1782 1783This is free software, licensed under: 1784 1785 The ISC License 1786 1787=cut 1788 1789__END__ 1790 1791 17921; 1793