File Coverage

blib/lib/Text/ASCIITable.pm
Criterion Covered Total %
statement 339 445 76.1
branch 141 244 57.7
condition 62 124 50.0
subroutine 35 41 85.3
pod 11 28 39.2
total 588 882 66.6


line stmt bran cond sub pod time code
1             package Text::ASCIITable;
2             # by Håkon Nessjøen
3              
4             @ISA=qw(Exporter);
5             @EXPORT = qw();
6             @EXPORT_OK = qw();
7             $VERSION = '0.20';
8 13     13   24452 use Exporter;
  13         25  
  13         603  
9 13     13   207 use strict;
  13         26  
  13         851  
10 13     13   66 use Carp;
  13         166  
  13         1251  
11 13     13   15866 use Text::ASCIITable::Wrap qw{ wrap };
  13         38  
  13         934  
12 13     13   33447 use overload '@{}' => 'addrow_overload', '""' => 'drawit';
  13         16531  
  13         98  
13 13     13   13684 use Encode;
  13         207977  
  13         1582  
14 13     13   114 use List::Util qw(reduce max sum);
  13         29  
  13         102855  
15              
16             =head1 NAME
17              
18             Text::ASCIITable - Create a nice formatted table using ASCII characters.
19              
20             =head1 SHORT DESCRIPTION
21              
22             Pretty nifty if you want to output dynamic text to your console or other
23             fixed-size-font displays, and at the same time it will display it in a
24             nice human-readable, or "cool" way.
25              
26             =head1 SYNOPSIS
27              
28             use Text::ASCIITable;
29             $t = Text::ASCIITable->new({ headingText => 'Basket' });
30            
31             $t->setCols('Id','Name','Price');
32             $t->addRow(1,'Dummy product 1',24.4);
33             $t->addRow(2,'Dummy product 2',21.2);
34             $t->addRow(3,'Dummy product 3',12.3);
35             $t->addRowLine();
36             $t->addRow('','Total',57.9);
37             print $t;
38            
39             # Result:
40             .------------------------------.
41             | Basket |
42             +----+-----------------+-------+
43             | Id | Name | Price |
44             +----+-----------------+-------+
45             | 1 | Dummy product 1 | 24.4 |
46             | 2 | Dummy product 2 | 21.2 |
47             | 3 | Dummy product 3 | 12.3 |
48             +----+-----------------+-------+
49             | | Total | 57.9 |
50             '----+-----------------+-------'
51              
52             =head1 FUNCTIONS
53              
54             =head2 new(options)
55              
56             Initialize a new table. You can specify output-options. For more options, check out the usage for setOptions()
57              
58             Usage:
59             $t = Text::ASCIITable->new();
60              
61             Or with options:
62             $t = Text::ASCIITable->new({ hide_Lastline => 1, reportErrors => 0});
63              
64             =cut
65              
66             sub new {
67 14   100 14 1 5391 my $self = {
68             tbl_cols => [],
69             tbl_rows => [],
70             tbl_cuts => [],
71             tbl_align => {},
72             tbl_lines => {},
73              
74             des_top => ['.','.','-','-'],
75             des_middle => ['+','+','-','+'],
76             des_bottom => ["'","'",'-','+'],
77             des_rowline => ['+','+','-','+'],
78              
79             des_toprow => ['|','|','|'],
80             des_middlerow => ['|','|','|'],
81              
82             cache_width => {},
83              
84             options => $_[1] || { }
85             };
86              
87 14 50       98 $self->{options}{reportErrors} = defined($self->{options}{reportErrors}) ? $self->{options}{reportErrors} : 1; # default setting
88 14   100     113 $self->{options}{alignHeadRow} = $self->{options}{alignHeadRow} || 'auto'; # default setting
89 14   50     127 $self->{options}{undef_as} = $self->{options}{undef_as} || ''; # default setting
90 14   100     110 $self->{options}{chaining} = $self->{options}{chaining} || 0; # default setting
91 14 100       76 $self->{options}{utf8} = defined($self->{options}{utf8}) ? $self->{options}{utf8} : 1; # default setting
92              
93 14         29 bless $self;
94              
95 14         49 return $self;
96             }
97              
98             =head2 setCols(@cols)
99              
100             Define the columns for the table(compare with in HTML). For example C.
101             B that you cannot add Cols after you have added a row. Multiline columnnames are allowed.
102              
103             =cut
104              
105             sub setCols {
106 13     13 1 98 my $self = shift;
107 13 0       49 do { $self->reperror("setCols needs an array"); return $self->{options}{chaining} ? $self : 1; } unless defined($_[0]);
  0 50       0  
  0         0  
108 13 100       70 @_ = @{$_[0]} if (ref($_[0]) eq 'ARRAY');
  10         45  
109 13 0       67 do { $self->reperror("setCols needs an array"); return $self->{options}{chaining} ? $self : 1; } unless scalar(@_) != 0;
  0 50       0  
  0         0  
110 13 0       43 do { $self->reperror("Cannot edit cols at this state"); return $self->{options}{chaining} ? $self : 1; } unless scalar(@{$self->{tbl_rows}}) == 0;
  13 50       771  
  0         0  
  0         0  
111              
112 13         41 my @lines = map { [ split(/\n/,$_) ] } @_;
  39         152  
113              
114             # Multiline support
115 13         28 my $max=0;
116 13         25 my @out;
117 13 100       265 grep {$max = scalar(@{$_}) if scalar(@{$_}) > $max} @lines;
  39         82  
  14         59  
  39         140  
118 13         55 foreach my $num (0..($max-1)) {
119 14   66     184 my @tmp = map defined $$_[$num] && $$_[$num], @lines;
120 14         58 push @out, \@tmp;
121             }
122              
123 13         32 @{$self->{tbl_cols}} = @_;
  13         42  
124 13 50       52 @{$self->{tbl_multilinecols}} = @out if ($max);
  13         48  
125 13         187 $self->{tbl_colsismultiline} = $max;
126              
127 13 100       131 return $self->{options}{chaining} ? $self : undef;
128             }
129              
130             =head2 addRow(@collist)
131              
132             Adds one row to the table. This must be an array of strings. If you defined 3 columns. This array must
133             have 3 items in it. And so on. Should be self explanatory. The strings can contain newlines.
134              
135             Note: It does not require argument to be an array, thus;
136             $t->addRow(['id','name']) and $t->addRow('id','name') does the same thing.
137              
138             This module is also overloaded to accept push. To construct a table with the use of overloading you might do the following:
139              
140             $t = Text::ASCIITable->new();
141             $t->setCols('one','two','three','four');
142             push @$t, ( "one\ntwo" ) x 4; # Replaces $t->addrow();
143             print $t; # Replaces print $t->draw();
144            
145             Which would construct:
146             .-----+-----+-------+------.
147             | one | two | three | four |
148             |=----+-----+-------+-----=|
149             | one | one | one | one | # Note that theese two lines
150             | two | two | two | two | # with text are one singe row.
151             '-----+-----+-------+------'
152              
153             There is also possible to give this function an array of arrayrefs and hence support the output from
154             DBI::selectall_arrayref($sql) without changes.
155              
156             Example of multiple-rows pushing:
157             $t->addRow([
158             [ 1, 2, 3 ],
159             [ 4, 5, 6 ],
160             [ 7, 8, 9 ],
161             ]);
162              
163             =cut
164              
165             sub addRow {
166 42     42 1 102 my $self = shift;
167 42 100       127 @_ = @{$_[0]} if (ref($_[0]) eq 'ARRAY');
  4         10  
168 42 0 33     64 do { $self->reperror("Received too many columns"); return $self->{options}{chaining} ? $self : 1; } if scalar(@_) > scalar(@{$self->{tbl_cols}}) && ref($_[0]) ne 'ARRAY';
  42 50       163  
  0         0  
  0         0  
169 42         63 my (@in,@out,@lines,$max);
170              
171 42 100 66     225 if (scalar(@_) > 0 && ref($_[0]) eq 'ARRAY') {
172 1         2 foreach my $row (@_) {
173 3         12 $self->addRow($row);
174             }
175 1 50       7 return $self->{options}{chaining} ? $self : undef;
176             }
177              
178             # Fill out row, if columns are missing (requested) Mar 21 2004 by a anonymous person
179 41         62 while (scalar(@_) < scalar(@{$self->{tbl_cols}})) {
  41         130  
180 0         0 push @_, ' ';
181             }
182              
183             # Word wrapping & undef-replacing
184 41         166 foreach my $c (0..$#_) {
185 121 50       250 $_[$c] = $self->{options}{undef_as} unless defined $_[$c]; # requested by david@landgren.net/dland@cpan.org - https://rt.cpan.org/NoAuth/Bugs.html?Dist=Text-ASCIITable
186 121         224 my $colname = $self->{tbl_cols}[$c];
187 121   100     520 my $width = $self->{tbl_width}{$colname} || 0;
188 121 100       476 if ($width > 0) {
189 4         16 $in[$c] = wrap($_[$c],$width);
190             } else {
191 117         375 $in[$c] = $_[$c];
192             }
193             }
194              
195             # Multiline support:
196 41         84 @lines = map { [ split /\n/ ] } @in;
  121         355  
197 41         73 $max = max map {scalar @$_} @lines;
  121         309  
198 41         101 foreach my $num (0..($max-1)) {
199 57 100 66     79 my @tmp = map { defined(@{$_}[$num]) && $self->count(@{$_}[$num]) ? @{$_}[$num] : '' } @lines;
  157         169  
  140         403  
200 57         246 push @out, [ @tmp ];
201             }
202              
203             # Add row(s)
204 41         56 push @{$self->{tbl_rows}}, @out;
  41         103  
205              
206             # Rowlinesupport:
207 41         81 $self->{tbl_rowline}{scalar(@{$self->{tbl_rows}})} = 1;
  41         367  
208              
209 41 100       237 return $self->{options}{chaining} ? $self : undef;
210             }
211              
212             sub addrow_overload {
213 3     3 0 24 my $self = shift;
214 3         4 my @arr;
215 3         11 tie @arr, $self;
216 3         13 return \@arr;
217             }
218              
219             =head2 addRowLine([$row])
220              
221             Will add a line after the current row. As an argument, you may specify after which row you want a line (first row is 1)
222             or an array of row numbers. (HINT: If you want a line after every row, read about the drawRowLine option in setOptions())
223              
224             Example without arguments:
225             $t->addRow('one','two'ž'three');
226             $t->addRowLine();
227             $t->addRow('one','two'ž'three');
228              
229             Example with argument:
230             $t->addRow('one','two'ž'three');
231             $t->addRow('one','two'ž'three');
232             $t->addRow('one','two'ž'three');
233             $t->addRow('one','two'ž'three');
234             $t->addRowLine(1); # or multiple: $t->addRowLine([2,3]);
235              
236             =cut
237              
238             sub addRowLine {
239 1     1 1 2 my ($self,$row) = @_;
240 1 0       2 do { $self->reperror("rows not added yet"); return $self->{options}{chaining} ? $self : 1; } unless scalar(@{$self->{tbl_rows}}) > 0;
  1 50       5  
  0         0  
  0         0  
241              
242 1 50 33     6 if (defined($row) && ref($row) eq 'ARRAY') {
    50          
243 0         0 foreach (@$row) {
244 0         0 $_=int($_);
245 0         0 $self->{tbl_lines}{$_} = 1;
246             }
247             }
248             elsif (defined($row)) {
249 0         0 $row = int($row);
250 0 0 0     0 do { $self->reperror("$row is higher than number of rows added"); return $self->{options}{chaining} ? $self : 1; } if ($row < 0 || $row > scalar(@{$self->{tbl_rows}}));
  0 0       0  
  0         0  
  0         0  
251 0         0 $self->{tbl_lines}{$row} = 1;
252             } else {
253 1         3 $self->{tbl_lines}{scalar(@{$self->{tbl_rows}})} = 1;
  1         3  
254             }
255              
256 1 50       5 return $self->{options}{chaining} ? $self : undef;
257             }
258              
259             # backwardscompatibility, deprecated
260             sub alignColRight {
261 4     4 0 9 my ($self,$col) = @_;
262 4 0       12 do { $self->reperror("alignColRight is missing parameter(s)"); return $self->{options}{chaining} ? $self : 1; } unless defined($col);
  0 50       0  
  0         0  
263 4         15 return $self->alignCol($col,'right');
264             }
265              
266             =head2 alignCol($col,$direction) or alignCol({col1 => direction1, col2 => direction2, ... })
267              
268             Given a columnname, it aligns all data to the given direction in the table. This looks nice on numerical displays
269             in a column. The column names in the table will be unaffected by the alignment. Possible directions is: left,
270             center, right, justify, auto or your own subroutine. (Hint: Using auto(default), aligns numbers right and text left)
271              
272             =cut
273              
274             sub alignCol {
275 5     5 1 9 my ($self,$col,$direction) = @_;
276 5 0 33     31 do { $self->reperror("alignCol is missing parameter(s)"); return $self->{options}{chaining} ? $self : 1; } unless defined($col) && defined($direction) || (defined($col) && ref($col) eq 'HASH');
  0 0 0     0  
  0   33     0  
277 5 0 0     23 do { $self->reperror("Could not find '$col' in columnlist"); return $self->{options}{chaining} ? $self : 1; } unless defined(&find($col,$self->{tbl_cols})) || (defined($col) && ref($col) eq 'HASH');
  0 0 33     0  
  0         0  
278              
279 5 50       15 if (ref($col) eq 'HASH') {
280 0         0 for (keys %{$col}) {
  0         0  
281 0 0       0 do { $self->reperror("Could not find '$_' in columnlist"); return $self->{options}{chaining} ? $self : 1; } unless defined(&find($_,$self->{tbl_cols}));
  0 0       0  
  0         0  
282 0         0 $self->{tbl_align}{$_} = $col->{$_};
283             }
284             } else {
285 5         20 $self->{tbl_align}{$col} = $direction;
286             }
287 5 50       30 return $self->{options}{chaining} ? $self : undef;
288             }
289              
290             =head2 alignColName($col,$direction)
291              
292             Given a columnname, it aligns the columnname in the row explaining columnnames, to the given direction. (auto,left,right,center,justify
293             or a subroutine) (Hint: Overrides the 'alignHeadRow' option for the specified column.)
294              
295             =cut
296              
297             sub alignColName {
298 0     0 1 0 my ($self,$col,$direction) = @_;
299 0 0 0     0 do { $self->reperror("alignColName is missing parameter(s)"); return $self->{options}{chaining} ? $self : 1; } unless defined($col) && defined($direction);
  0 0       0  
  0         0  
300 0 0       0 do { $self->reperror("Could not find '$col' in columnlist"); return $self->{options}{chaining} ? $self : 1; } unless defined(&find($col,$self->{tbl_cols}));
  0 0       0  
  0         0  
301              
302 0         0 $self->{tbl_colalign}{$col} = $direction;
303 0 0       0 return $self->{options}{chaining} ? $self : undef;
304             }
305              
306             =head2 setColWidth($col,$width,$strict)
307              
308             Wordwrapping/strict size. Set a max-width(in chars) for a column.
309             If last parameter is 1, the column will be set to the specified width, even if no text is that long.
310              
311             Usage:
312             $t->setColWidth('Description',30);
313              
314             =cut
315              
316             sub setColWidth {
317 1     1 1 4 my ($self,$col,$width,$strict) = @_;
318 1 0 33     17 do { $self->reperror("setColWidth is missing parameter(s)"); return $self->{options}{chaining} ? $self : 1; } unless defined($col) && defined($width);
  0 50       0  
  0         0  
319 1 0       7 do { $self->reperror("Could not find '$col' in columnlist"); return $self->{options}{chaining} ? $self : 1; } unless defined(&find($col,$self->{tbl_cols}));
  0 50       0  
  0         0  
320 1 0       2 do { $self->reperror("Cannot change width at this state"); return $self->{options}{chaining} ? $self : 1; } unless scalar(@{$self->{tbl_rows}}) == 0;
  1 50       5  
  0         0  
  0         0  
321              
322 1         7 $self->{tbl_width}{$col} = int($width);
323 1 50       5 $self->{tbl_width_strict}{$col} = $strict ? 1 : 0;
324              
325 1 50       8 return $self->{options}{chaining} ? $self : undef;
326             }
327              
328             sub headingWidth {
329 6     6 0 10 my $self = shift;
330 6         15 my $title = $self->{options}{headingText};
331 6         51 return max map {$self->count($_)} split /\r?\n/, $self->{options}{headingText};
  10         26  
332             }
333              
334             # drawing etc, below
335             sub getColWidth {
336 458     458 0 603 my ($self,$colname) = @_;
337 458 50       932 $self->reperror("Could not find '$colname' in columnlist") unless defined find($colname, $self->{tbl_cols});
338              
339 458         1394 return $self->{cache_width}{$colname};
340             }
341              
342             # Width-calculating functions rewritten for more speed by Alexey Sheynuk
343             # Thanks :)
344             sub calculateColWidths {
345 30     30 0 51 my ($self) = @_;
346 30         61 $self->{cache_width} = undef;
347 30         89 my $cols = $self->{tbl_cols};
348 30         47 foreach my $c (0..$#{$cols}) {
  30         84  
349 92         151 my $colname = $cols->[$c];
350 92 50 66     380 if (defined($self->{tbl_width_strict}{$colname}) && ($self->{tbl_width_strict}{$colname} == 1) && int($self->{tbl_width}{$colname}) > 0) {
      33        
351             # maxsize plus the spaces on each side
352 0         0 $self->{cache_width}{$colname} = $self->{tbl_width}{$colname} + 2;
353             } else {
354 92         223 my $colwidth = max((map {$self->count($_)} split(/\n/,$colname)), (map {$self->count($_->[$c])} @{$self->{tbl_rows}}));
  94         197  
  346         881  
  92         240  
355 92         388 $self->{cache_width}{$colname} = $colwidth + 2;
356             }
357             }
358 30         154 $self->addExtraHeadingWidth;
359             }
360              
361             sub addExtraHeadingWidth {
362 30     30 0 55 my ($self) = @_;
363 30 100       272 return unless defined $self->{options}{headingText};
364 6         12 my $tablewidth = -3 + sum map {$_ + 1} values %{$self->{cache_width}};
  16         57  
  6         22  
365 6         19 my $headingwidth = $self->headingWidth();
366 6 100       23 if ($headingwidth > $tablewidth) {
367 4         6 my $extra = $headingwidth - $tablewidth;
368 4         6 my $cols = scalar(@{$self->{tbl_cols}});
  4         9  
369 4         9 my $extra_for_all = int($extra/$cols);
370 4         7 my $extrasome = $extra % $cols;
371 4         6 my $antall = 0;
372 4         5 foreach my $col (@{$self->{tbl_cols}}) {
  4         11  
373 12         33 my $extrawidth = $extra_for_all;
374 12 100       24 if ($antall < $extrasome) {
375 2         3 $antall++;
376 2         2 $extrawidth++;
377             }
378 12         33 $self->{cache_width}{$col} += $extrawidth;
379             }
380             }
381             }
382              
383             =head2 getTableWidth()
384              
385             If you need to know how wide your table will be before you draw it. Use this function.
386              
387             =cut
388              
389             sub getTableWidth {
390 38     38 1 2458 my $self = shift;
391 38         57 my $totalsize = 1;
392 38 100       122 if (!defined($self->{cache_TableWidth})) {
393 12         44 $self->calculateColWidths;
394 12         23 grep {$totalsize += $self->getColWidth($_,undef) + 1} @{$self->{tbl_cols}};
  35         87  
  12         38  
395 12         36 $self->{cache_TableWidth} = $totalsize;
396             }
397 38         121 return $self->{cache_TableWidth};
398             }
399              
400             sub drawLine {
401 55     55 0 151 my ($self,$start,$stop,$line,$delim) = @_;
402 55 50       131 do { $self->reperror("Missing reqired parameters"); return 1; } unless defined($stop);
  0         0  
  0         0  
403 55 50       110 $line = defined($line) ? $line : '-';
404 55 50       98 $delim = defined($delim) ? $delim : '+';
405              
406 55         85 my $contents;
407              
408 55         70 $contents = $start;
409              
410 55         81 for (my $i=0;$i < scalar(@{$self->{tbl_cols}});$i++) {
  228         566  
411 173         185 my $offset = 0;
412 173 100       365 $offset = $self->count($start) - 1 if ($i == 0);
413 173 100       191 $offset = $self->count($stop) - 1 if ($i == scalar(@{$self->{tbl_cols}}) -1);
  173         512  
414              
415 173         211 $contents .= $line x ($self->getColWidth(@{$self->{tbl_cols}}[$i]) - $offset);
  173         444  
416              
417 173 100       199 $contents .= $delim if ($i != scalar(@{$self->{tbl_cols}}) - 1);
  173         566  
418             }
419 55         317 return $contents.$stop."\n";
420             }
421              
422             =head2 setOptions(name,value) or setOptions({ option1 => value1, option2 => value2, ... })
423              
424             Use this to set options like: hide_FirstLine,reportErrors, etc.
425              
426             Usage:
427             $t->setOptions('hide_HeadLine',1);
428            
429             Or set more than one option on the fly:
430             $t->setOptions({ hide_HeadLine => 1, hide_HeadRow => 1 });
431              
432             B
433              
434             =over 4
435              
436             =item hide_HeadRow
437              
438             Hides output of the columnlisting. Together with hide_HeadLine, this makes a table only show the rows. (However, even though
439             the column-names will not be shown, they will affect the output if they have for example ridiculoustly long
440             names, and the rows contains small amount of info. You would end up with a lot of whitespace)
441              
442             =item reportErrors
443              
444             Set to 0 to disable error reporting. Though if a function encounters an error, it will still return the value 1, to
445             tell you that things didn't go exactly as they should.
446              
447             =item allowHTML
448              
449             If you are going to use Text::ASCIITable to be shown on HTML pages, you should set this option to 1 when you are going
450             to use HTML tags to for example color the text inside the rows, and you want the browser to handle the table correct.
451              
452             =item allowANSI
453              
454             If you use ANSI codes like [1mHi this is bold[m or similar. This option will make the table to be
455             displayed correct when showed in a ANSI compliant terminal. Set this to 1 to enable. There is an example of ANSI support
456             in this package, named ansi-example.pl.
457              
458             =item alignHeadRow
459              
460             Set wich direction the Column-names(in the headrow) are supposed to point. Must be left, right, center, justify, auto or a user-defined subroutine.
461              
462             =item hide_FirstLine, hide_HeadLine, hide_LastLine
463              
464             Speaks for it self?
465              
466             =item drawRowLine
467              
468             Set this to 1 to print a line between each row. You can also define the outputstyle
469             of this line in the draw() function.
470              
471             =item headingText
472              
473             Add a heading above the columnnames/rows wich uses the whole width of the table to output
474             a heading/title to the table. The heading-part of the table is automatically shown when
475             the headingText option contains text. B If this text is so long that it makes the
476             table wider, it will not hesitate to change width of columns that have "strict width".
477              
478             It supports multiline, and with Text::ASCIITable::Wrap you may wrap your text before entering
479             it, to prevent the title from expanding the table. Internal wrapping-support for headingText
480             might come in the future.
481              
482             =item headingAlign
483              
484             Align the heading(as mentioned above) to left, right, center, auto or using a subroutine.
485              
486             =item headingStartChar, headingStopChar
487              
488             Choose the startingchar and endingchar of the row where the title is. The default is
489             '|' on both. If you didn't understand this, try reading about the draw() function.
490              
491             =item cb_count
492              
493             Set the callback subroutine to use when counting characters inside the table. This is useful
494             to make support for having characters or codes inside the table that are not shown on the
495             screen to the user, so the table should not count these characters. This could be for example
496             HTML tags, or ANSI codes. Though those two examples are alredy supported internally with the
497             allowHTML and allowANSI, options. This option expects a CODE reference. (\&callback_function)
498              
499             =item undef_as
500              
501             Sets the replacing string that replaces an undef value sent to addRow() (or even the overloaded
502             push version of addRow()). The default value is an empty string ''. An example of use would be
503             to set it to '(undef)', to show that the input really was undefined.
504              
505              
506             =item chaining
507              
508             Set this to 1 to support chainging of methods. The default is 0, where the methods return 1 if
509             they come upon an error as mentioned in the reportErrors option description.
510              
511             Usage example:
512             print Text::ASCIITable->new({ chaining => 1 })
513             ->setCols('One','Two','Three')
514             ->addRow([
515             [ 1, 2, 3 ],
516             [ 4, 5, 6 ],
517             [ 7, 8, 9 ],
518             ])
519             ->draw();
520              
521             Note that ->draw() can be omitted, since Text::ASCIITable is overloaded to print the table by default.
522              
523             =back
524              
525             =cut
526              
527             sub setOptions {
528 5     5 1 190 my ($self,$name,$value) = @_;
529 5         8 my $old;
530 5 50       15 if (ref($name) eq 'HASH') {
531 0         0 for (keys %{$name}) {
  0         0  
532 0         0 $self->{options}{$_} = $name->{$_};
533             }
534             } else {
535 5   100     100 $old = $self->{options}{$name} || undef;
536 5         16 $self->{options}{$name} = $value;
537             }
538 5         14 return $old;
539             }
540              
541             # Thanks to Khemir Nadim ibn Hamouda
542             # Original code from Spreadsheet::Perl::ASCIITable
543             sub prepareParts {
544 1     1 0 2 my ($self)=@_;
545 1         2 my $running_width = 1 ;
546              
547 1         4 $self->{tbl_cuts} = [];
548 1         2 foreach my $column (@{$self->{tbl_cols}}) {
  1         3  
549 3         6 my $column_width = $self->getColWidth($column,undef);
550 3 100       10 if ($running_width + $column_width >= $self->{options}{outputWidth}) {
551 1         2 push @{$self->{tbl_cuts}}, $running_width;
  1         2  
552 1         3 $running_width = $column_width + 2;
553             } else {
554 2         5 $running_width += $column_width + 1 ;
555             }
556             }
557 1         2 push @{$self->{tbl_cuts}}, $self->getTableWidth() ;
  1         3  
558             }
559              
560             sub pageCount {
561 2     2 0 217 my $self = shift;
562 2 50       9 do { $self->reperror("Table has no max output-width set"); return 1; } unless defined($self->{options}{outputWidth});
  0         0  
  0         0  
563              
564 2 50       8 return 1 if ($self->getTableWidth() < $self->{options}{outputWidth});
565 2 100       3 $self->prepareParts() if (scalar(@{$self->{tbl_cuts}}) < 1);
  2         9  
566              
567 2         3 return scalar(@{$self->{tbl_cuts}});
  2         8  
568             }
569              
570             sub drawSingleColumnRow {
571 5     5 0 13 my ($self,$text,$start,$stop,$align,$opt) = @_;
572 5 50       13 do { $self->reperror("Missing reqired parameters"); return 1; } unless defined($text);
  0         0  
  0         0  
573              
574 5         10 my $contents = $start;
575 5         6 my $width = 0;
576 5         15 my $tablewidth = $self->getTableWidth();
577             # ok this is a bad shortcut, but 'till i get up with a better one, I use this.
578 5 50 33     17 if (($tablewidth - 4) < $self->count($text) && $opt eq 'title') {
579 0         0 $width = $self->count($text);
580             }
581             else {
582 5         11 $width = $tablewidth - 4;
583             }
584 5 50 50     65 $contents .= ' '.$self->align(
      33        
585             $text,
586             $align || 'left',
587             $width,
588             ($self->{options}{allowHTML} || $self->{options}{allowANSI} || $self->{options}{cb_count} ?0:1)
589             ).' ';
590 5         22 return $contents.$stop."\n";
591             }
592              
593             sub drawRow {
594 85     85 0 173 my ($self,$row,$isheader,$start,$stop,$delim) = @_;
595 85 50       188 do { $self->reperror("Missing reqired parameters"); return 1; } unless defined($row);
  0         0  
  0         0  
596 85   100     270 $isheader = $isheader || 0;
597 85   50     163 $delim = $delim || '|';
598              
599 85         109 my $contents = $start;
600 85         118 for (my $i=0;$i
  332         791  
601 247         289 my $colwidth = $self->getColWidth(@{$self->{tbl_cols}}[$i]);
  247         678  
602 247         320 my $text = @{$row}[$i];
  247         709  
603              
604 247 100 100     820 if ($isheader != 1 && defined($self->{tbl_align}{@{$self->{tbl_cols}}[$i]})) {
  190 100       800  
605             $contents .= ' '.$self->align(
606             $text,
607 29 50 50     46 $self->{tbl_align}{@{$self->{tbl_cols}}[$i]} || 'auto',
      33        
608             $colwidth-2,
609             ($self->{options}{allowHTML} || $self->{options}{allowANSI} || $self->{options}{cb_count}?0:1)
610             ).' ';
611             } elsif ($isheader == 1) {
612              
613             $contents .= ' '.$self->align(
614             $text,
615 57 100 50     237 $self->{tbl_colalign}{@{$self->{tbl_cols}}[$i]} || $self->{options}{alignHeadRow} || 'left',
      66        
616             $colwidth-2,
617             ($self->{options}{allowHTML} || $self->{options}{allowANSI} || $self->{options}{cb_count}?0:1)
618             ).' ';
619             } else {
620 161 100 66     1330 $contents .= ' '.$self->align(
621             $text,
622             'auto',
623             $colwidth-2,
624             ($self->{options}{allowHTML} || $self->{options}{allowANSI} || $self->{options}{cb_count}?0:1)
625             ).' ';
626             }
627 247 100       346 $contents .= $delim if ($i != scalar(@{$row}) - 1);
  247         753  
628             }
629 85         367 return $contents.$stop."\n";
630             }
631              
632             =head2 draw([@topdesign,@toprow,@middle,@middlerow,@bottom,@rowline])
633              
634             All the arrays containing the layout is optional. If you want to make your own "design" to the table, you
635             can do that by giving this method these arrays containing information about which characters to use
636             where.
637              
638             B
639              
640             The draw method takes C<6> arrays of strings to define the layout. The first, third, fifth and sixth is B
641             layout and the second and fourth is B layout. The C parameter is repeated for each row in the table.
642             The sixth parameter is only used if drawRowLine is enabled.
643              
644             $t->draw(,,,,,[])
645              
646             =over 4
647              
648             =item LINE
649              
650             Takes an array of C<4> strings. For example C<['|','|','-','+']>
651              
652             =over 4
653              
654             =item *
655              
656             LEFT - Defines the left chars. May be more than one char.
657              
658             =item *
659              
660             RIGHT - Defines the right chars. May be more then one char.
661              
662             =item *
663              
664             LINE - Defines the char used for the line. B.
665              
666             =item *
667              
668             DELIMETER - Defines the char used for the delimeters. B.
669              
670             =back
671              
672             =item ROW
673              
674             Takes an array of C<3> strings. You should not give more than one char to any of these parameters,
675             if you do.. it will probably destroy the output.. Unless you do it with the knowledge
676             of how it will end up. An example: C<['|','|','+']>
677              
678             =over 4
679              
680             =item *
681              
682             LEFT - Define the char used for the left side of the table.
683              
684             =item *
685              
686             RIGHT - Define the char used for the right side of the table.
687              
688             =item *
689              
690             DELIMETER - Defines the char used for the delimeters.
691              
692             =back
693              
694             =back
695              
696             Examples:
697              
698             The easiest way:
699              
700             print $t;
701              
702             Explanatory example:
703              
704             print $t->draw( ['L','R','l','D'], # LllllllDllllllR
705             ['L','R','D'], # L info D info R
706             ['L','R','l','D'], # LllllllDllllllR
707             ['L','R','D'], # L info D info R
708             ['L','R','l','D'] # LllllllDllllllR
709             );
710              
711             Nice example:
712              
713             print $t->draw( ['.','.','-','-'], # .-------------.
714             ['|','|','|'], # | info | info |
715             ['|','|','-','-'], # |-------------|
716             ['|','|','|'], # | info | info |
717             [' \\','/ ','_','|'] # \_____|_____/
718             );
719              
720             Nice example2:
721              
722             print $t->draw( ['.=','=.','-','-'], # .=-----------=.
723             ['|','|','|'], # | info | info |
724             ['|=','=|','-','+'], # |=-----+-----=|
725             ['|','|','|'], # | info | info |
726             ["'=","='",'-','-'] # '=-----------='
727             );
728              
729             With Options:
730              
731             $t->setOptions('drawRowLine',1);
732             print $t->draw( ['.=','=.','-','-'], # .=-----------=.
733             ['|','|','|'], # | info | info |
734             ['|-','-|','=','='], # |-===========-|
735             ['|','|','|'], # | info | info |
736             ["'=","='",'-','-'], # '=-----------='
737             ['|=','=|','-','+'] # rowseperator
738             );
739             Which makes this output:
740             .=-----------=.
741             | col1 | col2 |
742             |-===========-|
743             | info | info |
744             |=-----+-----=| <-- rowseperator between each row
745             | info | info |
746             '=-----------='
747              
748             A tips is to enable allowANSI, and use the extra charset in your terminal to create
749             a beautiful table. But don't expect to get good results if you use ANSI-formatted table
750             with $t->drawPage.
751              
752             B
753              
754             If you want to format your text more throughoutly than "auto", or think you
755             have a better way of aligning text; you can make your own subroutine.
756              
757             Here's a exampleroutine that aligns the text to the right.
758            
759             sub myownalign_cb {
760             my ($text,$length,$count,$strict) = @_;
761             $text = (" " x ($length - $count)) . $text;
762             return substr($text,0,$length) if ($strict);
763             return $text;
764             }
765              
766             $t->alignCol('Info',\&myownalign_cb);
767              
768             B
769              
770             This is a feature to use if you are not happy with the internal allowHTML or allowANSI
771             support. Given is an example of how you make a count-callback that makes ASCIITable support
772             ANSI codes inside the table. (would make the same result as setting allowANSI to 1)
773              
774             $t->setOptions('cb_count',\&myallowansi_cb);
775             sub myallowansi_cb {
776             $_=shift;
777             s/\33\[(\d+(;\d+)?)?[musfwhojBCDHRJK]//g;
778             return length($_);
779             }
780              
781             =cut
782              
783 6     6 0 30 sub drawit {scalar shift()->draw()}
784              
785             =head2 drawPage($page,@topdesign,@toprow,@middle,@middlerow,@bottom,@rowline)
786              
787             If you don't want your table to be wider than your screen you can use this
788             with $t->setOptions('outputWidth',40) to set the max size of the output.
789              
790             Example:
791              
792             $t->setOptions('outputWidth',80);
793             for my $page (1..$t->pageCount()) {
794             print $t->drawPage($page)."\n";
795             print "continued..\n\n";
796             }
797              
798             =cut
799              
800             sub drawPage {
801 2     2 1 10 my $self = shift;
802 2         5 my ($pagenum,$top,$toprow,$middle,$middlerow,$bottom,$rowline) = @_;
803 2         9 return $self->draw($top,$toprow,$middle,$middlerow,$bottom,$rowline,$pagenum);
804             }
805              
806             # Thanks to Khemir Nadim ibn Hamouda for code and idea.
807             sub getPart {
808 145     145 0 232 my ($self,$page,$text) = @_;
809 145         164 my $offset=0;
810              
811 145 100       518 return $text unless $page > 0;
812 14         41 $text =~ s/\n$//;
813              
814 14 50       15 $self->prepareParts() if (scalar(@{$self->{tbl_cuts}}) < 1);
  14         46  
815 14         33 $offset += (@{$self->{tbl_cuts}}[$_] - 1) for(0..$page-2);
  7         21  
816              
817 14         19 return substr($text, $offset, @{$self->{tbl_cuts}}[$page-1]) . "\n" ;
  14         59  
818             }
819              
820             sub draw {
821 18     18 1 41 my $self = shift;
822 18         40 my ($top,$toprow,$middle,$middlerow,$bottom,$rowline,$page) = @_;
823 18 100       55 my ($tstart,$tstop,$tline,$tdelim) = defined($top) ? @{$top} : @{$self->{des_top}};
  1         8  
  17         60  
824 18 100       53 my ($trstart,$trstop,$trdelim) = defined($toprow) ? @{$toprow} : @{$self->{des_toprow}};
  1         3  
  17         49  
825 18 100       53 my ($mstart,$mstop,$mline,$mdelim) = defined($middle) ? @{$middle} : @{$self->{des_middle}};
  1         2  
  17         49  
826 18 100       50 my ($mrstart,$mrstop,$mrdelim) = defined($middlerow) ? @{$middlerow} : @{$self->{des_middlerow}};
  1         4  
  17         48  
827 18 100       46 my ($bstart,$bstop,$bline,$bdelim) = defined($bottom) ? @{$bottom} : @{$self->{des_bottom}};
  1         2  
  17         53  
828 18 50       49 my ($rstart,$rstop,$rline,$rdelim) = defined($rowline) ? @{$rowline} : @{$self->{des_rowline}};
  0         0  
  18         53  
829 18 100       35 my $contents=""; $page = defined($page) ? $page : 0;
  18         53  
830              
831 18         40 delete $self->{cache_TableWidth}; # Clear cache
832 18         71 $self->calculateColWidths;
833              
834 18 100       166 $contents .= $self->getPart($page,$self->drawLine($tstart,$tstop,$tline,$tdelim)) unless $self->{options}{hide_FirstLine};
835 18 100       80 if (defined($self->{options}{headingText})) {
836 3         8 my $title = $self->{options}{headingText};
837 3 100       17 if ($title =~ m/\n/) { # Multiline title-support
838 2         16 my @lines = split(/\r?\n/,$title);
839 2         5 foreach my $line (@lines) {
840 4   50     63 $contents .= $self->getPart($page,$self->drawSingleColumnRow($line,$self->{options}{headingStartChar} || '|',$self->{options}{headingStopChar} || '|',$self->{options}{headingAlign} || 'center','title'));
      50        
      100        
841             }
842             } else {
843 1   50     21 $contents .= $self->getPart($page,$self->drawSingleColumnRow($self->{options}{headingText},$self->{options}{headingStartChar} || '|',$self->{options}{headingStopChar} || '|',$self->{options}{headingAlign} || 'center','title'));
      50        
      50        
844             }
845 3 50       28 $contents .= $self->getPart($page,$self->drawLine($mstart,$mstop,$mline,$mdelim)) unless $self->{options}{hide_HeadLine};
846             }
847              
848 18 100       69 unless ($self->{options}{hide_HeadRow}) {
849             # multiline-column-support
850 17         27 foreach my $row (@{$self->{tbl_multilinecols}}) {
  17         70  
851 18         91 $contents .= $self->getPart($page,$self->drawRow($row,1,$trstart,$trstop,$trdelim));
852             }
853             }
854 18 100       112 $contents .= $self->getPart($page,$self->drawLine($mstart,$mstop,$mline,$mdelim)) unless $self->{options}{hide_HeadLine};
855 18         37 my $i=0;
856 18         36 for (@{$self->{tbl_rows}}) {
  18         58  
857 67         84 $i++;
858 67         167 $contents .= $self->getPart($page,$self->drawRow($_,0,$mrstart,$mrstop,$mrdelim));
859 67 50 33     568 if (($self->{options}{drawRowLine} && $self->{tbl_rowline}{$i} && ($i != scalar(@{$self->{tbl_rows}}))) ||
  0   33     0  
  1   66     9  
      66        
      33        
      33        
860 1         5 (defined($self->{tbl_lines}{$i}) && $self->{tbl_lines}{$i} && ($i != scalar(@{$self->{tbl_rows}})) && ($i != scalar(@{$self->{tbl_rows}})))) {
861 1         4 $contents .= $self->getPart($page,$self->drawLine($rstart,$rstop,$rline,$rdelim))
862             }
863             }
864 18 100       132 $contents .= $self->getPart($page,$self->drawLine($bstart,$bstop,$bline,$bdelim)) unless $self->{options}{hide_LastLine};
865              
866 18         150 return $contents;
867             }
868              
869             # nifty subs
870              
871             # Replaces length() because of optional HTML and ANSI stripping
872             sub count {
873 993     993 0 1513 my ($self,$str) = @_;
874              
875 993 50 33     5436 if (defined($self->{options}{cb_count}) && ref($self->{options}{cb_count}) eq 'CODE') {
    50 33        
876 0         0 my $ret = eval { return &{$self->{options}{cb_count}}($str); };
  0         0  
  0         0  
877 0 0       0 return $ret if (!$@);
878 0 0       0 do { $self->reperror("Error: 'cb_count' callback returned error, ".$@); return 1; } if ($@);
  0         0  
  0         0  
879             }
880             elsif (defined($self->{options}{cb_count}) && ref($self->{options}{cb_count}) ne 'CODE') {
881 0         0 $self->reperror("Error: 'cb_count' set but no valid callback found, found ".ref($self->{options}{cb_count}));
882 0         0 return length($str);
883             }
884 993 100       2372 $str =~ s/<.+?>//g if $self->{options}{allowHTML};
885 993 100       2190 $str =~ s/\33\[(\d+(;\d+)?)?[musfwhojBCDHRJK]//g if $self->{options}{allowANSI}; # maybe i should only have allowed ESC[#;#m and not things not related to
886 993 100       1884 $str =~ s/\33\([0B]//g if $self->{options}{allowANSI}; # color/bold/underline.. But I want to give people as much room as they need.
887 993 50       3699 $str = decode("utf8", $str) if $self->{options}{utf8};
888              
889 993         26254 return length($str);
890             }
891              
892             sub align {
893              
894 288     288 0 568 my ($self,$text,$dir,$length,$strict) = @_;
895              
896 288 100       1036 if ($dir =~ /auto/i) {
897 215 100       657 if ($text =~ /^-?\d+([.,]\d+)*[%\w]?$/) {
898 35         61 $dir = 'right';
899             } else {
900 180         265 $dir = 'left';
901             }
902             }
903 288 50       1281 if (ref($dir) eq 'CODE') {
    100          
    100          
    50          
    50          
904 0         0 my $ret = eval { return &{$dir}($text,$length,$self->count($text),$strict); };
  0         0  
  0         0  
905 0 0       0 return 'CB-ERR' if ($@);
906             # Removed in v0.14 # return 'CB-LEN-ERR' if ($self->count($ret) != $length);
907 0         0 return $ret;
908             } elsif ($dir =~ /right/i) {
909 79         195 my $visuallen = $self->count($text);
910 79         113 my $reallen = length($text);
911 79         287 $text = (" " x ($length - $visuallen)).$text;
912 79 100       353 return substr($text,0,$length - ($visuallen-$reallen)) if ($strict);
913 16         83 return $text;
914             } elsif ($dir =~ /left/i) {
915 192         376 my $visuallen = $self->count($text);
916 192         290 my $reallen = length($text);
917 192         338 $text = $text.(" " x ($length - $visuallen));
918 192 100       772 return substr($text,0,$length - ($visuallen-$reallen)) if ($strict);
919 28         96 return $text;
920             } elsif ($dir =~ /justify/i) {
921 0         0 my $visuallen = $self->count($text);
922 0         0 my $reallen = length($text);
923 0 0       0 $text = substr($text,0,$length - ($visuallen-$reallen)) if ($strict);
924 0 0       0 if ($self->count($text) < $length - ($visuallen-$reallen)) {
925 0         0 $text =~ s/^\s+//; # trailing whitespace
926 0         0 $text =~ s/\s+$//; # tailing whitespace
927              
928 0         0 my @tmp = split(/\s+/,$text); # split them words
929              
930 0 0       0 if (scalar(@tmp)) {
931 0         0 my $extra = $length - $self->count(join('',@tmp)); # Length of text without spaces
932              
933 0         0 my $modulus = $extra % (scalar(@tmp)); # modulus
934 0         0 $extra = int($extra / (scalar(@tmp))); # for each word
935              
936 0         0 $text = '';
937 0         0 foreach my $word (@tmp) {
938 0         0 $text .= $word . (' ' x $extra); # each word
939 0 0       0 if ($modulus) {
940 0         0 $modulus--;
941 0         0 $text .= ' '; # the first $modulus words, to even out
942             }
943             }
944             }
945             }
946 0         0 return $text; # either way, output text
947             } elsif ($dir =~ /center/i) {
948 17         40 my $visuallen = $self->count($text);
949 17         31 my $reallen = length($text);
950 17         35 my $left = ( $length - $visuallen ) / 2;
951             # Someone tell me if this is matematecally totally wrong. :P
952 17 100 100     78 $left = int($left) + 1 if ($left != int($left) && $left > 0.4);
953 17         30 my $right = int(( $length - $visuallen ) / 2);
954 17         51 $text = (" " x $left).$text.(" " x $right);
955 17 100       58 return substr($text,0,$length) if ($strict);
956 10         47 return $text;
957             } else {
958 0         0 return $self->align($text,'auto',$length,$strict);
959             }
960             }
961              
962             sub TIEARRAY {
963 3     3   4 my $self = shift;
964              
965 3         11 return bless { workaround => $self } , ref $self;
966             }
967             sub FETCH {
968 0     0   0 shift->{workaround}->reperror('usage: push @$t,qw{ one more row };');
969 0         0 return undef;
970             }
971             sub STORE {
972 0     0   0 my $self = shift->{workaround};
973 0         0 my ($index, $value) = @_;
974              
975 0         0 $self->reperror('usage: push @$t,qw{ one more row };');
976             }
977 0     0   0 sub FETCHSIZE {return 0;}
978 0     0   0 sub STORESIZE {return;}
979              
980             # PodMaster should be really happy now, since this was in his wishlist. (ref: http://perlmonks.thepen.com/338456.html)
981             sub PUSH {
982 3     3   5 my $self = shift->{workaround};
983 3         6 my @list = @_;
984              
985 3 50       4 if (scalar(@list) > scalar(@{$self->{tbl_cols}})) {
  3         10  
986 0         0 $self->reperror("too many elements added");
987 0         0 return;
988             }
989              
990 3         7 $self->addRow(@list);
991             }
992              
993             sub reperror {
994 0     0 0 0 my $self = shift;
995             }
996              
997             # Best way I could think of, to search the array.. Please tell me if you got a better way.
998             sub find {
999 464 50   464 0 934 return undef unless defined $_[1];
1000 464 100       539 grep {return $_ if @{$_[1]}[$_] eq $_[0];} (0..scalar(@{$_[1]})-1);
  946         907  
  946         3332  
  464         1038  
1001 0           return undef;
1002             }
1003              
1004             1;
1005              
1006             __END__