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