File Coverage

blib/lib/Text/Table/Span.pm
Criterion Covered Total %
statement 282 295 95.5
branch 124 172 72.0
condition 40 88 45.4
subroutine 17 20 85.0
pod 1 1 100.0
total 464 576 80.5


line stmt bran cond sub pod time code
1             package Text::Table::Span;
2              
3             our $AUTHORITY = 'cpan:PERLANCAR'; # AUTHORITY
4             our $DATE = '2021-02-19'; # DATE
5             our $DIST = 'Text-Table-Span'; # DIST
6             our $VERSION = '0.007'; # VERSION
7              
8 1     1   77311 use 5.010001;
  1         15  
9 1     1   6 use strict;
  1         8  
  1         25  
10 1     1   5 use warnings;
  1         2  
  1         47  
11              
12 1     1   693 use List::AllUtils qw(first firstidx max);
  1         20138  
  1         101  
13 1     1   715 use String::Pad qw(pad);
  1         405  
  1         59  
14              
15 1     1   8 use Exporter qw(import);
  1         2  
  1         3665  
16             our @EXPORT_OK = qw/ generate_table /;
17              
18             # consts
19             sub IDX_EXPTABLE_CELL_ROWSPAN() {0} # number of rowspan, only defined for the rowspan head
20             sub IDX_EXPTABLE_CELL_COLSPAN() {1} # number of colspan, only defined for the colspan head
21             sub IDX_EXPTABLE_CELL_WIDTH() {2} # visual width. this does not include the cell padding.
22             sub IDX_EXPTABLE_CELL_HEIGHT() {3} # visual height. this does not include row separator.
23             sub IDX_EXPTABLE_CELL_ORIG() {4} # str/hash
24             sub IDX_EXPTABLE_CELL_IS_ROWSPAN_TAIL() {5} # whether this cell is tail of a rowspan
25             sub IDX_EXPTABLE_CELL_IS_COLSPAN_TAIL() {6} # whether this cell is tail of a colspan
26              
27             # whether an exptable cell is the head (1st cell) or tail (the rest) of a
28             # rowspan/colspan. these should be macros if possible, for speed.
29 15 50   15   49 sub _exptable_cell_is_rowspan_tail { defined($_[0]) && $_[0][IDX_EXPTABLE_CELL_IS_ROWSPAN_TAIL] }
30 14 50   14   43 sub _exptable_cell_is_colspan_tail { defined($_[0]) && $_[0][IDX_EXPTABLE_CELL_IS_COLSPAN_TAIL] }
31 12 50 66 12   45 sub _exptable_cell_is_tail { defined($_[0]) && ($_[0][IDX_EXPTABLE_CELL_IS_ROWSPAN_TAIL] || $_[0][IDX_EXPTABLE_CELL_IS_COLSPAN_TAIL]) }
32 9 50   9   33 sub _exptable_cell_is_rowspan_head { defined($_[0]) && !$_[0][IDX_EXPTABLE_CELL_IS_ROWSPAN_TAIL] }
33 0 0   0   0 sub _exptable_cell_is_colspan_head { defined($_[0]) && !$_[0][IDX_EXPTABLE_CELL_IS_COLSPAN_TAIL] }
34 14 50   14   63 sub _exptable_cell_is_head { defined($_[0]) && defined $_[0][IDX_EXPTABLE_CELL_ORIG] }
35              
36             sub _divide_int_to_n_ints {
37 18     18   29 my ($int, $n) = @_;
38 18         23 my $subtot = 0;
39 18         30 my $int_subtot = 0;
40 18         24 my $prev_int_subtot = 0;
41 18         22 my @ints;
42 18         29 for (1..$n) {
43 20         47 $subtot += $int/$n;
44 20         39 $int_subtot = sprintf "%.0f", $subtot;
45 20         30 push @ints, $int_subtot - $prev_int_subtot;
46 20         34 $prev_int_subtot = $int_subtot;
47             }
48 18         33 @ints;
49             }
50              
51             sub _get_attr {
52 29     29   55 my ($attr_name, $y, $x, $cell_value, $table_args) = @_;
53              
54             CELL_ATTRS_FROM_CELL_VALUE: {
55 29 100       35 last unless ref $cell_value eq 'HASH';
  29         63  
56 2         5 my $attr_val = $cell_value->{$attr_name};
57 2 100       7 return $attr_val if defined $attr_val;
58             }
59              
60             CELL_ATTRS_FROM_CELL_ATTRS_ARG:
61             {
62 28 100 66     39 last unless defined $x && defined $y;
  28         60  
63 8         13 my $cell_attrs = $table_args->{cell_attrs};
64 8 50       17 last unless $cell_attrs;
65 0         0 for my $entry (@$cell_attrs) {
66 0 0 0     0 next unless $entry->[0] == $y && $entry->[1] == $x;
67 0         0 my $attr_val = $entry->[2]{$attr_name};
68 0 0       0 return $attr_val if defined $attr_val;
69             }
70             }
71              
72             COL_ATTRS:
73             {
74 28 100       34 last unless defined $x;
  28         45  
75 8         11 my $col_attrs = $table_args->{col_attrs};
76 8 50       16 last unless $col_attrs;
77 8         14 for my $entry (@$col_attrs) {
78 8 100       29 next unless $entry->[0] == $x;
79 3         7 my $attr_val = $entry->[1]{$attr_name};
80 3 50       10 return $attr_val if defined $attr_val;
81             }
82             }
83              
84             ROW_ATTRS:
85             {
86 25 50       34 last unless defined $y;
  25         37  
87 25         35 my $row_attrs = $table_args->{row_attrs};
88 25 50       45 last unless $row_attrs;
89 25         34 for my $entry (@$row_attrs) {
90 25 100       49 next unless $entry->[0] == $y;
91 8         13 my $attr_val = $entry->[1]{$attr_name};
92 8 100       21 return $attr_val if defined $attr_val;
93             }
94             }
95              
96             TABLE_ARGS:
97             {
98 23         26 my $attr_val = $table_args->{$attr_name};
  23         38  
99 23 100       41 return $attr_val if defined $attr_val;
100             }
101              
102 20         32 undef;
103             }
104              
105             sub _get_exptable_cell_lines {
106 9     9   17 my ($table_args, $exptable, $row_heights, $column_widths,
107             $bottom_borders, $intercol_width, $y, $x) = @_;
108              
109 9         16 my $exptable_cell = $exptable->[$y][$x];
110 9         13 my $cell = $exptable_cell->[IDX_EXPTABLE_CELL_ORIG];
111 9 100       28 my $text = ref $cell eq 'HASH' ? $cell->{text} : $cell;
112 9   50     21 my $align = _get_attr('align', $y, $x, $cell, $table_args) // 'left';
113 9 100       24 my $pad = $align eq 'left' ? 'r' : $align eq 'right' ? 'l' : 'c';
    100          
114 9         11 my $height = 0;
115 9         15 my $width = 0;
116 9         15 for my $ic (1..$exptable_cell->[IDX_EXPTABLE_CELL_COLSPAN]) {
117 10         18 $width += $column_widths->[$x+$ic-1];
118 10 100       21 $width += $intercol_width if $ic > 1;
119             }
120 9         15 for my $ir (1..$exptable_cell->[IDX_EXPTABLE_CELL_ROWSPAN]) {
121 10         18 $height += $row_heights->[$y+$ir-1];
122 10 100 66     33 $height++ if $bottom_borders->[$y+$ir-2] && $ir > 1;
123             }
124              
125 9         13 my @lines;
126 9         32 my @datalines = split /\R/, $text;
127 9         19 for (1..@datalines) {
128 12         70 push @lines, pad($datalines[$_-1], $width, $pad, ' ', 'truncate');
129             }
130 9         160 for (@datalines+1 .. $height) {
131 2         8 push @lines, " " x $width;
132             }
133 9         25 \@lines;
134             }
135              
136             sub generate_table {
137 1     1 1 2390 require Module::Load::Util;
138 1         2312 require Text::NonWideChar::Util;
139              
140 1         275 my %args = @_;
141 1 50       8 my $rows = $args{rows} or die "Please specify rows";
142 1   50     9 my $bs_name = $args{border_style} // 'ASCII::SingleLineDoubleAfterHeader';
143 1   50     7 my $cell_attrs = $args{cell_attrs} // [];
144              
145 1         7 my $bs_obj = Module::Load::Util::instantiate_class_with_optional_args({ns_prefix=>"BorderStyle"}, $bs_name);
146              
147             # XXX when we allow cell attrs right_border and left_border, this will
148             # become array too like $exptable_bottom_borders.
149 1         3751 my $intercol_width = length(" " . $bs_obj->get_border_char(3, 1) . " ");
150              
151 1         63 my $exptable = []; # [ [[$orig_rowidx,$orig_colidx,$rowspan,$colspan,...], ...], [[...], ...], ... ]
152 1         4 my $exptable_bottom_borders = []; # idx=exptable rownum, val=bool
153 1         2 my $M = 0; # number of rows in the exptable
154 1         2 my $N = 0; # number of columns in the exptable
155             CONSTRUCT_EXPTABLE: {
156             # 1. the first step is to construct a 2D array we call "exptable" (short
157             # for expanded table), which is like the original table but with all the
158             # spanning rows/columns split into the smaller boxes so it's easier to
159             # draw later. for example, a table cell with colspan=2 will become 2
160             # exptable cells. an m-row x n-column table will become M-row x N-column
161             # exptable, where M>=m, N>=n.
162              
163 1         2 my $rownum;
  1         3  
164              
165             # 1a. first substep: construct exptable and calculate everything except
166             # each exptable cell's width and height, because this will require
167             # information from the previous substeps.
168              
169 1         2 $rownum = -1;
170 1         4 for my $row (@$rows) {
171 4         7 $rownum++;
172 4         7 my $colnum = -1;
173 4   100     20 $exptable->[$rownum] //= [];
174 4         9 push @{ $exptable->[$rownum] }, undef
175 4 50 66     7 if (@{ $exptable->[$rownum] } == 0 ||
  4         18  
176             defined($exptable->[$rownum][-1]));
177             #use DDC; say "D:exptable->[$rownum] = ", DDC::dump($exptable->[$rownum]);
178 4     4   17 my $exptable_colnum = firstidx {!defined} @{ $exptable->[$rownum] };
  4         10  
  4         16  
179             #say "D:rownum=$rownum, exptable_colnum=$exptable_colnum";
180 4 50       16 if ($exptable_colnum == -1) { $exptable_colnum = 0 }
  0         0  
181 4 50 33     22 $exptable_bottom_borders->[$rownum] //= $args{separate_rows} ? 1:0;
182              
183 4         7 for my $cell (@$row) {
184 9         13 $colnum++;
185 9         10 my $text;
186              
187 9         12 my $rowspan = 1;
188 9         11 my $colspan = 1;
189 9 100       20 if (ref $cell eq 'HASH') {
190 2         5 $text = $cell->{text};
191 2 100       9 $rowspan = $cell->{rowspan} if $cell->{rowspan};
192 2 100       6 $colspan = $cell->{colspan} if $cell->{colspan};
193             } else {
194 7         11 $text = $cell;
195 7         9 my $el;
196 7 0 0 0   30 $el = first {$_->[0] == $rownum && $_->[1] == $colnum && $_->[2]{rowspan}} @$cell_attrs;
  0         0  
197 7 50       23 $rowspan = $el->[2]{rowspan} if $el;
198 7 0 0 0   21 $el = first {$_->[0] == $rownum && $_->[1] == $colnum && $_->[2]{colspan}} @$cell_attrs;
  0         0  
199 7 50       23 $colspan = $el->[2]{colspan} if $el;
200             }
201              
202 9         12 my @widths;
203             my @heights;
204 9         21 for my $ir (1..$rowspan) {
205 10         15 for my $ic (1..$colspan) {
206 12         17 my $exptable_cell;
207 12         27 $exptable->[$rownum+$ir-1][$exptable_colnum+$ic-1] = $exptable_cell = [];
208              
209 12 100 100     39 if ($ir == 1 && $ic == 1) {
210 9         37 $exptable_cell->[IDX_EXPTABLE_CELL_ROWSPAN] = $rowspan;
211 9         14 $exptable_cell->[IDX_EXPTABLE_CELL_COLSPAN] = $colspan;
212 9         19 $exptable_cell->[IDX_EXPTABLE_CELL_ORIG] = $cell;
213             } else {
214 3 100       17 $exptable_cell->[IDX_EXPTABLE_CELL_IS_ROWSPAN_TAIL] = 1 if $ir > 1;
215 3 100       9 $exptable_cell->[IDX_EXPTABLE_CELL_IS_COLSPAN_TAIL] = 1 if $ic > 1;
216             }
217             #use DDC; dd $exptable; say ''; # debug
218             }
219              
220 10         14 my $val;
221 10 50       22 $val = _get_attr('bottom_border', $rownum+$ir-1, undef, undef, \%args); $exptable_bottom_borders->[$rownum+$ir-1] = $val if $val;
  10         21  
222 10 50       19 $val = _get_attr('top_border' , $rownum+$ir-1, undef, undef, \%args); $exptable_bottom_borders->[$rownum+$ir-2] = $val if $val;
  10         19  
223 10 50 66     29 $exptable_bottom_borders->[0] = 1 if $rownum+$ir-1 == 0 && $args{header_row};
224              
225 10 100       26 $M = $rownum+$ir if $M < $rownum+$ir;
226             }
227              
228 9         13 $exptable_colnum += $colspan;
229 9         24 $exptable_colnum++ while defined $exptable->[$rownum][$exptable_colnum];
230              
231             } # for a row
232 4 100       10 $N = $exptable_colnum if $N < $exptable_colnum;
233             } # for rows
234              
235             # 1b. calculate the heigth and width of each exptable cell (as required
236             # by the text, or specified width/height when we allow cell attrs width,
237             # height)
238              
239 1         4 for my $exptable_rownum (0..$M-1) {
240 4         18 for my $exptable_colnum (0..$N-1) {
241 12         19 my $exptable_cell = $exptable->[$exptable_rownum][$exptable_colnum];
242 12 100       24 next if _exptable_cell_is_tail($exptable_cell);
243 9         15 my $rowspan = $exptable_cell->[IDX_EXPTABLE_CELL_ROWSPAN];
244 9         14 my $colspan = $exptable_cell->[IDX_EXPTABLE_CELL_COLSPAN];
245 9         11 my $cell = $exptable_cell->[IDX_EXPTABLE_CELL_ORIG];
246 9 100       24 my $text = ref $cell eq 'HASH' ? $cell->{text} : $cell;
247 9         18 my $lh = Text::NonWideChar::Util::length_height($text);
248             #use DDC; say "D:length_height[$exptable_rownum,$exptable_colnum] = (".DDC::dump($text)."): ".DDC::dump($lh);
249 9         255 my $tot_intercol_widths = ($colspan-1) * $intercol_width;
250 9 50       11 my $tot_interrow_heights = 0; for (1..$rowspan-1) { $tot_interrow_heights++ if $exptable_bottom_borders->[$exptable_rownum+$_-1] }
  9         19  
  1         5  
251             #say "D:interrow_heights=$tot_interrow_heights";
252 9         27 my @heights = _divide_int_to_n_ints(max(0, $lh->[1] - $tot_interrow_heights), $rowspan);
253 9         20 my @widths = _divide_int_to_n_ints(max(0, $lh->[0] - $tot_intercol_widths ), $colspan);
254 9         17 for my $ir (1..$rowspan) {
255 10         16 for my $ic (1..$colspan) {
256 12         22 $exptable->[$exptable_rownum+$ir-1][$exptable_colnum+$ic-1][IDX_EXPTABLE_CELL_HEIGHT] = $heights[$ir-1];
257 12         36 $exptable->[$exptable_rownum+$ir-1][$exptable_colnum+$ic-1][IDX_EXPTABLE_CELL_WIDTH] = $widths [$ic-1];
258             }
259             }
260             }
261             } # for rows
262              
263             } # CONSTRUCT_EXPTABLE
264             #use DDC; dd $exptable; # debug
265             #print "D: exptable size: $M x $N (HxW)\n"; # debug
266             #use DDC; print "bottom borders: "; dd $exptable_bottom_borders; # debug
267              
268             OPTIMIZE_EXPTABLE: {
269             # TODO
270              
271             # 2. we reduce extraneous columns and rows if there are colspan that are
272             # too many. for example, if all exptable cells in column 1 has colspan=2
273             # (or one row has colspan=2 and another row has colspan=3), we might as
274             # remove 1 column because the extra column span doesn't have any
275             # content. same case for extraneous row spans.
276              
277             # 2a. remove extra undefs. skip this. doesn't make a difference.
278             #for my $exptable_row (@{ $exptable }) {
279             # splice @$exptable_row, $N if @$exptable_row > $N;
280             #}
281              
282 1         3 1;
  1         2  
283             } # OPTIMIZE_EXPTABLE
284             #use DDC; dd $exptable; # debug
285              
286 1         3 my $exptable_column_widths = []; # idx=exptable colnum
287 1         2 my $exptable_row_heights = []; # idx=exptable rownum
288             DETERMINE_SIZE_OF_EACH_EXPTABLE_COLUMN_AND_ROW: {
289             # 3. before we draw the exptable, we need to determine the width and
290             # height of each exptable column and row.
291             #use DDC;
292 1         2 for my $ir (0..$M-1) {
  1         3  
293 4         8 my $exptable_row = $exptable->[$ir];
294             $exptable_row_heights->[$ir] = max(
295 4   100     7 1, map {$_->[IDX_EXPTABLE_CELL_HEIGHT] // 0} @$exptable_row);
  13         47  
296             }
297              
298 1         12 for my $ic (0..$N-1) {
299             $exptable_column_widths->[$ic] = max(
300 3 50       11 1, map {$exptable->[$_][$ic] ? $exptable->[$_][$ic][IDX_EXPTABLE_CELL_WIDTH] : 0} 0..$M-1);
  12         28  
301             }
302             } # DETERMINE_SIZE_OF_EACH_EXPTABLE_COLUMN_AND_ROW
303             #use DDC; print "column widths: "; dd $exptable_column_widths; # debug
304             #use DDC; print "row heights: "; dd $exptable_row_heights; # debug
305              
306             # each elem is an arrayref containing characters to render a line of the
307             # table, e.g. for element [0] the row is all borders. for element [1]:
308             # [$left_border_str, $exptable_cell_content1, $border_between_col,
309             # $exptable_cell_content2, ...]. all will be joined together with "\n" to
310             # form the final rendered table.
311 1         2 my @buf;
312              
313             DRAW_EXPTABLE: {
314             # 4. finally we draw the (exp)table.
315              
316 1         2 my $y = 0;
  1         1  
317              
318 1         3 for my $ir (0..$M-1) {
319              
320             DRAW_TOP_BORDER:
321             {
322 4 100       10 last unless $ir == 0;
323 1 50       5 my $b_y = $args{header_row} ? 0 : 6;
324 1         6 my $b_topleft = $bs_obj->get_border_char($b_y, 0);
325 1         32 my $b_topline = $bs_obj->get_border_char($b_y, 1);
326 1         21 my $b_topbetwcol = $bs_obj->get_border_char($b_y, 2);
327 1         21 my $b_topright = $bs_obj->get_border_char($b_y, 3);
328 1 0 33     75 last unless length $b_topleft || length $b_topline || length $b_topbetwcol || length $b_topright;
      33        
      0        
329 1         5 $buf[$y][0] = $b_topleft;
330 1         5 for my $ic (0..$N-1) {
331 3 100       11 my $cell_right = $ic < $N-1 ? $exptable->[$ir][$ic+1] : undef;
332 3   66     13 my $cell_right_has_content = defined $cell_right && _exptable_cell_is_head($cell_right);
333 3         12 $buf[$y][$ic*4+2] = $bs_obj->get_border_char($b_y, 1, $exptable_column_widths->[$ic]+2); # +1, +2, +3
334 3 50       79 $buf[$y][$ic*4+4] = $ic == $N-1 ? $b_topright : ($cell_right_has_content ? $b_topbetwcol : $b_topline);
    100          
335             }
336 1         4 $y++;
337             } # DRAW_TOP_BORDER
338              
339             # DRAW_DATA_OR_HEADER_ROW
340             {
341             # draw leftmost border, which we always do.
342 4 100 66     7 my $b_y = $ir == 0 && $args{header_row} ? 1 : 3;
  4         5  
  4         15  
343 4         11 for my $i (1 .. $exptable_row_heights->[$ir]) {
344 5         33 $buf[$y+$i-1][0] = $bs_obj->get_border_char($b_y, 0);
345             }
346              
347 4         95 my $lines;
348 4         18 for my $ic (0..$N-1) {
349 12         19 my $cell = $exptable->[$ir][$ic];
350              
351             # draw cell content. also possibly draw border between
352             # cells. we don't draw border inside a row/colspan.
353 12 100       87 if (_exptable_cell_is_head($cell)) {
354 9         23 $lines = _get_exptable_cell_lines(
355             \%args, $exptable, $exptable_row_heights, $exptable_column_widths,
356             $exptable_bottom_borders, $intercol_width, $ir, $ic);
357 9         13 for my $i (0..$#{$lines}) {
  9         28  
358 14         31 $buf[$y+$i][$ic*4+0] = $bs_obj->get_border_char($b_y, 1);
359 14         307 $buf[$y+$i][$ic*4+1] = " ";
360 14         28 $buf[$y+$i][$ic*4+2] = $lines->[$i];
361 14         26 $buf[$y+$i][$ic*4+3] = " ";
362             }
363             #use DDC; say "D: Drawing exptable_cell($ir,$ic): ", DDC::dump($lines);
364             }
365              
366             # draw rightmost border, which we always do.
367 12 100       34 if ($ic == $N-1) {
368 4 100 66     16 my $b_y = $ir == 0 && $args{header_row} ? 1 : 3;
369 4         12 for my $i (1 .. $exptable_row_heights->[$ir]) {
370 5         32 $buf[$y+$i-1][$ic*4+4] = $bs_obj->get_border_char($b_y, 2);
371             }
372             }
373              
374             }
375             } # DRAW_DATA_OR_HEADER_ROW
376 4         101 $y += $exptable_row_heights->[$ir];
377              
378             DRAW_ROW_SEPARATOR:
379             {
380 4 100       17 last unless $ir < $M-1;
  4         18  
381 3 50       8 last unless $exptable_bottom_borders->[$ir];
382 3 100 66     13 my $b_y = $ir == 0 && $args{header_row} ? 2 : 4;
383 3         15 my $b_betwrowleft = $bs_obj->get_border_char($b_y, 0);
384 3         65 my $b_betwrowline = $bs_obj->get_border_char($b_y, 1);
385 3         60 my $b_betwrowbetwcol = $bs_obj->get_border_char($b_y, 2);
386 3         59 my $b_betwrowright = $bs_obj->get_border_char($b_y, 3);
387 3 0 33     62 last unless length $b_betwrowleft || length $b_betwrowline || length $b_betwrowbetwcol || length $b_betwrowright;
      33        
      0        
388 3         7 my $b_betwrowbetwcol_notop = $bs_obj->get_border_char($b_y, 4);
389 3         69 my $b_betwrowbetwcol_nobot = $bs_obj->get_border_char($b_y, 5);
390 3         62 my $b_betwrowbetwcol_noleft = $bs_obj->get_border_char($b_y, 6);
391 3         58 my $b_betwrowbetwcol_noright = $bs_obj->get_border_char($b_y, 7);
392 3 100 66     60 my $b_yd = $ir == 0 && $args{header_row} ? 2 : 3;
393 3         8 my $b_datarowleft = $bs_obj->get_border_char($b_yd, 0);
394 3         62 my $b_datarowbetwcol = $bs_obj->get_border_char($b_yd, 1);
395 3         59 my $b_datarowright = $bs_obj->get_border_char($b_yd, 2);
396 3         61 for my $ic (0..$N-1) {
397 9         13 my $cell = $exptable->[$ir][$ic];
398 9 100       22 my $cell_right = $ic < $N-1 ? $exptable->[$ir][$ic+1] : undef;
399 9 50       19 my $cell_bottom = $ir < $M-1 ? $exptable->[$ir+1][$ic] : undef;
400 9 100 66     39 my $cell_rightbottom = $ir < $M-1 && $ic < $N-1 ? $exptable->[$ir+1][$ic+1] : undef;
401              
402             # leftmost border
403 9 100       18 if ($ic == 0) {
404 3 50       7 $buf[$y][0] = _exptable_cell_is_rowspan_tail($cell_bottom) ? $b_datarowleft : $b_betwrowleft;
405             }
406              
407             # along the width of cell content
408 9 100       22 if (_exptable_cell_is_rowspan_head($cell_bottom)) {
409 7         20 $buf[$y][$ic*4+2] = $bs_obj->get_border_char($b_y, 1, $exptable_column_widths->[$ic]+2);
410             }
411              
412 9         151 my $char;
413 9 100       19 if ($ic == $N-1) {
414             # rightmost
415 3 100       5 if (_exptable_cell_is_rowspan_tail($cell_bottom)) {
416 1         2 $char = $b_datarowright;
417             } else {
418 2         4 $char = $b_betwrowright;
419             }
420             } else {
421             # between cells
422 6 100       12 if (_exptable_cell_is_colspan_tail($cell_right)) {
423 1 50       3 if (_exptable_cell_is_colspan_tail($cell_rightbottom)) {
424 1 50       3 if (_exptable_cell_is_rowspan_tail($cell_bottom)) {
425 1         2 $char = "";
426             } else {
427 0         0 $char = $b_betwrowline;
428             }
429             } else {
430 0         0 $char = $b_betwrowbetwcol_notop;
431             }
432             } else {
433 5 100       9 if (_exptable_cell_is_colspan_tail($cell_rightbottom)) {
434 1         2 $char = $b_betwrowbetwcol_nobot;
435             } else {
436 4 50       9 if (_exptable_cell_is_rowspan_tail($cell_bottom)) {
    100          
437 0 0       0 if (_exptable_cell_is_rowspan_tail($cell_rightbottom)) {
438 0         0 $char = $b_datarowbetwcol;
439             } else {
440 0         0 $char = $b_betwrowbetwcol_noleft;
441             }
442             } elsif (_exptable_cell_is_rowspan_tail($cell_rightbottom)) {
443 1         2 $char = $b_betwrowbetwcol_noright;
444             } else {
445 3         5 $char = $b_betwrowbetwcol;
446             }
447             }
448             }
449             }
450 9         38 $buf[$y][$ic*4+4] = $char;
451              
452             }
453 3         5 $y++;
454             } # DRAW_ROW_SEPARATOR
455              
456             DRAW_BOTTOM_BORDER:
457             {
458 4 100       8 last unless $ir == $M-1;
  4         11  
459 1 50 33     29 my $b_y = $ir == 0 && $args{header_row} ? 7 : 5;
460 1         6 my $b_botleft = $bs_obj->get_border_char($b_y, 0);
461 1         23 my $b_botline = $bs_obj->get_border_char($b_y, 1);
462 1         22 my $b_botbetwcol = $bs_obj->get_border_char($b_y, 2);
463 1         21 my $b_botright = $bs_obj->get_border_char($b_y, 3);
464 1 0 33     23 last unless length $b_botleft || length $b_botline || length $b_botbetwcol || length $b_botright;
      33        
      0        
465 1         3 $buf[$y][0] = $b_botleft;
466 1         5 for my $ic (0..$N-1) {
467 3 100       9 my $cell_right = $ic < $N-1 ? $exptable->[$ir][$ic+1] : undef;
468 3         8 $buf[$y][$ic*4+2] = $bs_obj->get_border_char($b_y, 1, $exptable_column_widths->[$ic]+2);
469 3 100       68 $buf[$y][$ic*4+4] = $ic == $N-1 ? $b_botright : (_exptable_cell_is_colspan_tail($cell_right) ? $b_botline : $b_botbetwcol);
    100          
470             }
471 1         12 $y++;
472             } # DRAW_BOTTOM_BORDER
473              
474             }
475             } # DRAW_EXPTABLE
476              
477 1 100       4 for my $row (@buf) { for (@$row) { $_ = "" if !defined($_) } } # debug. remove undef to "" to save dump width
  10         17  
  130         253  
478             #use DDC; dd \@buf;
479 1         3 join "", (map { my $linebuf = $_; join("", grep {defined} @$linebuf)."\n" } @buf);
  10         14  
  10         12  
  130         248  
480             }
481              
482             # Back-compat: 'table' is an alias for 'generate_table', but isn't exported
483             {
484 1     1   18 no warnings 'once';
  1         3  
  1         127  
485             *table = \&generate_table;
486             }
487              
488             1;
489             # ABSTRACT: Text::Table::Tiny + support for column/row spans
490              
491             __END__