use strict;
use warnings;
use Carp;
+use List::Util qw(sum);
our $VERSION = '0.9.10';
print __PACKAGE__.' is version: '.$VERSION.$/ if($ENV{'PDF_TABLE_DEBUG'});
# Check if any text to display
unless( defined( $text) and length($text) > 0 )
{
- carp "Warning: No input text found. Trying to add dummy '-' and not to break everything.\n";
- $text = '-';
+# carp "Warning: No input text found. Trying to add dummy '-' and not to break everything.\n";
+ $text = ' ';
}
# Strip any <CR> and Split the text into paragraphs
}
# Lets take from paragraph as many words as we can put into $width - $indent;
- while ( @paragraph and $text_object->advancewidth( join("\x20", @line)."\x20" . $paragraph[0]) +
- $line_width < $width )
+ # Always take at least one word; otherwise we'd end up in an infinite loop.
+ while ( !scalar(@line) || (
+ @paragraph && (
+ $text_object->advancewidth( join("\x20", @line)."\x20" . $paragraph[0]) + $line_width < $width
+ )
+ ))
{
push(@line, shift(@paragraph));
}
column_props => 1,
cell_props => 1,
max_word_length => 1,
+ num_header_rows => 1,
);
foreach my $key (keys %arg) {
croak "Error: Invalid setting key '$key' received."
#=====================================
# Disable header row into the table
my $header_props = undef;
-
+ my (@header_rows, @header_row_cell_props);
# Check if the user enabled it ?
if(defined $arg{'header_props'} and ref( $arg{'header_props'}) eq 'HASH')
{
$header_props->{'font_size' } = $header_props->{'font_size' } || $fnt_size + 2;
$header_props->{'bg_color' } = $header_props->{'bg_color' } || '#FFFFAA';
$header_props->{'justify' } = $header_props->{'justify' };
+ $header_props->{num_header_rows } = $arg{num_header_rows } || 1;
}
-
- my $header_row = undef;
#=====================================
# Other Parameters check
#=====================================
}
# Copy the header row if header is enabled
- @$header_row = $$data[0] if defined $header_props;
+ if (defined $header_props) {
+ map { push @header_rows, $$data[$_] } (0..$header_props->{num_header_rows} - 1);
+ map { push @header_row_cell_props, $$cell_props[$_] } (0..$header_props->{num_header_rows} - 1);
+ }
# Determine column widths based on content
# an arrayref whose values are a hashref holding
# the actual widths of the column/row intersection
my $row_col_widths = [];
# An array ref with the widths of the header row
- my $header_row_props = [];
+ my @header_row_widths;
# Scalars that hold sum of the maximum and minimum widths of all columns
my ( $max_col_w , $min_col_w ) = ( 0,0 );
my $word_widths = {};
my $rows_height = [];
- my $first_row = 1;
for( my $row_idx = 0; $row_idx < scalar(@$data) ; $row_idx++ )
{
+ #push @header_row_widths, [] if $row_idx < $header_props->{num_header_rows};
+
my $column_widths = []; #holds the width of each column
# Init the height for this row
$rows_height->[$row_idx] = 0;
$rows_height->[$row_idx] = $cell_font_size;
}
+ if (!defined $data->[$row_idx][$column_idx]) {
+ $data->[$row_idx][$column_idx] = ' ';
+ }
+
# This should fix a bug with very long words like serial numbers etc.
- if( $max_word_len > 0 )
+ if( $max_word_len > 0 && $data->[$row_idx][$column_idx])
{
$data->[$row_idx][$column_idx] =~ s#(\S{$max_word_len})(?=\S)#$1 #g;
}
$row_col_widths->[$row_idx] = $column_widths;
# Copy the calculated row properties of header row.
- @$header_row_props = @$column_widths if(!$row_idx and ref $header_props);
+ if (ref $header_props && $row_idx < $header_props->{num_header_rows}) {
+ push @header_row_widths, [ @{ $column_widths } ];
+ }
}
# Calc real column widths and expand table width if needed.
my $calc_column_widths;
($calc_column_widths, $width) = CalcColumnWidths( $col_props, $width );
+ my $num_cols = scalar @{ $calc_column_widths };
# Lets draw what we have!
my $row_index = 0;
# Store header row height for later use if headers have to be repeated
- my $header_row_height = $rows_height->[0];
+ my @header_row_heights = @$rows_height[0 .. $header_props->{num_header_rows}-1];
my ( $gfx, $gfx_bg, $background_color, $font_color, $bot_marg, $table_top_y, $text_start);
+ my $remaining_header_rows = $header_props ? $header_props->{num_header_rows} : 0;
+
# Each iteration adds a new page as neccessary
while(scalar(@{$data}))
{
if( ref $header_props and $header_props->{'repeat'})
{
- # Copy Header Data
- @$page_header = @$header_row;
- my $hrp ;
- @$hrp = @$header_row_props ;
- # Then prepend it to master data array
- unshift @$data, @$page_header;
- unshift @$row_col_widths, $hrp;
- unshift @$rows_height, $header_row_height;
-
- $first_row = 1; # Means YES
- $row_index--; # Rollback the row_index because a new header row has been added
+ unshift @$data, @header_rows;
+ unshift @$row_col_widths, @header_row_widths;
+ unshift @$rows_height, @header_row_heights;
+ $remaining_header_rows = $header_props->{num_header_rows};
}
}
# Check for safety reasons
if( $bot_marg < 0 )
{ # This warning should remain i think
- carp "!!! Warning: !!! Incorrect Table Geometry! Setting bottom margin to end of sheet!\n";
+# carp "!!! Warning: !!! Incorrect Table Geometry! Setting bottom margin to end of sheet!\n";
$bot_marg = 0;
}
# Added to resolve infite loop bug with returned undef values
for(my $d = 0; $d < scalar(@{$record}) ; $d++)
{
- $record->[$d] = '-' unless( defined $record->[$d]);
+ $record->[$d] = ' ' unless( defined $record->[$d]);
}
# Choose colors for this row
- $background_color = $row_index % 2 ? $background_color_even : $background_color_odd;
- $font_color = $row_index % 2 ? $font_color_even : $font_color_odd;
+ $background_color = ($row_index - $header_props->{num_header_rows}) % 2 ? $background_color_even : $background_color_odd;
+ $font_color = ($row_index - $header_props->{num_header_rows}) % 2 ? $font_color_even : $font_color_odd;
#Determine current row height
my $current_row_height = $pad_top + $pre_calculated_row_height + $pad_bot;
my $cur_x = $xbase;
my $leftovers = undef; # Reference to text that is returned from textblock()
my $do_leftovers = 0;
+ my ($colspan, @vertical_lines);
# Process every cell(column) from current row
for( my $column_idx = 0; $column_idx < scalar( @$record); $column_idx++ )
# look for font information for this cell
my ($cell_font, $cell_font_size, $cell_font_color, $justify);
- if( $first_row and ref $header_props)
+ if( $remaining_header_rows and ref $header_props)
{
$cell_font = $header_props->{'font'};
$cell_font_size = $header_props->{'font_size'};
# Init cell font object
$txt->font( $cell_font, $cell_font_size );
$txt->fillcolor($cell_font_color);
+
+ my $this_width;
+ if (!$remaining_header_rows && $cell_props->[$row_index + $header_props->{num_header_rows}][$column_idx]->{colspan}) {
+ $colspan = $cell_props->[$row_index + $header_props->{num_header_rows}][$column_idx]->{colspan};
+ } elsif ($remaining_header_rows && ($header_row_cell_props[$header_props->{num_header_rows} - $remaining_header_rows][$column_idx]->{colspan})) {
+ $colspan = $header_row_cell_props[$header_props->{num_header_rows} - $remaining_header_rows][$column_idx]->{colspan};
+ }
+
+ if ($colspan) {
+ $colspan = $num_cols - $column_idx if (-1 == $colspan);
+ my $last_idx = $column_idx + $colspan - 1;
+ $this_width = sum @{ $calc_column_widths }[$column_idx..$last_idx];
+ } else {
+ $this_width = $calc_column_widths->[$column_idx];
+ }
# If the content is wider than the specified width, we need to add the text as a text block
if( $record->[$column_idx] !~ m/(.\n.)/ and
$record_widths->[$column_idx] and
- $record_widths->[$column_idx] <= $calc_column_widths->[$column_idx]
+ $record_widths->[$column_idx] <= $this_width
){
my $space = $pad_left;
if ($justify eq 'right')
{
- $space = $calc_column_widths->[$column_idx] -($txt->advancewidth($record->[$column_idx]) + $pad_right);
+ $space = $this_width -($txt->advancewidth($record->[$column_idx]) + $pad_right);
}
elsif ($justify eq 'center')
{
- $space = ($calc_column_widths->[$column_idx] - $txt->advancewidth($record->[$column_idx])) / 2;
+ $space = ($this_width - $txt->advancewidth($record->[$column_idx])) / 2;
}
$txt->translate( $cur_x + $space, $text_start );
$txt->text( $record->[$column_idx] );
$record->[$column_idx],
x => $cur_x + $pad_left,
y => $text_start,
- w => $calc_column_widths->[$column_idx] - $pad_left - $pad_right,
+ w => $this_width - $pad_left - $pad_right,
h => $cur_y - $bot_marg - $pad_top - $pad_bot,
align => $justify,
lead => $lead
}
}
$cur_x += $calc_column_widths->[$column_idx];
+
+ push @vertical_lines, (!$colspan || (1 >= $colspan)) ? 1 : 0;
+ $colspan-- if $colspan;
}
if( $do_leftovers )
{
{
my $cell_bg_color;
- if( $first_row and ref $header_props)
+ if( $remaining_header_rows and ref $header_props)
{ #Compatibility Consistency with other props
$cell_bg_color = $header_props->{'bg_color'} || $header_props->{'background_color'};
}
# Get the most specific value if none was already set from header_props
- $cell_bg_color ||= $cell_props->[$row_index][$column_idx]->{'background_color'}
+ $cell_bg_color ||= $cell_props->[$row_index + $header_props->{num_header_rows}][$column_idx]->{'background_color'}
|| $col_props->[$column_idx]->{'background_color'}
|| $background_color;
$gfx_bg->fill();
}
$cur_x += $calc_column_widths->[$column_idx];
+
+ if ($line_w && $vertical_lines[$column_idx] && ($column_idx != (scalar(@{ $record }) - 1))) {
+ $gfx->move($cur_x, $cur_y);
+ $gfx->vline($cur_y - $current_row_height);
+ $gfx->fillcolor($border_color);
+ }
}#End of for(my $column_idx....
$cur_y -= $current_row_height;
$gfx->hline( $xbase + $width );
}
- $row_index++ unless ( $do_leftovers );
- $first_row = 0;
+ if ($remaining_header_rows) {
+ $remaining_header_rows--;
+ } else {
+ $row_index++ unless $do_leftovers;
+ }
}# End of Row_Loop
if ($gfx)
{
$gfx->move( $xbase, $table_top_y);
$gfx->vline( $cur_y );
- my $cur_x = $xbase;
- for( my $j = 0; $j < $columns_number; $j++ )
- {
- $cur_x += $calc_column_widths->[$j];
- $gfx->move( $cur_x, $table_top_y );
- $gfx->vline( $cur_y );
- }
+ $gfx->move($xbase + sum(@{ $calc_column_widths }[0..$num_cols - 1]), $table_top_y);
+ $gfx->vline( $cur_y );
}
# ACTUALLY draw all the lines
$calc_widths->[$j] = $col_props->[$j]->{min_w} || 0;;
}
- # Allow columns to expand to max_w before applying extra space equally.
- my $is_last_iter;
- for (;;)
- {
- my $span = ($avail_width - $min_width) / scalar( @$col_props);
- last if $span <= 0;
-
- $min_width = 0;
- my $next_will_be_last_iter = 1;
- for(my $j = 0; $j < scalar(@$col_props); $j++ )
- {
- my $new_w = $calc_widths->[$j] + $span;
-
- if (!$is_last_iter && $new_w > $col_props->[$j]->{max_w})
- {
- $new_w = $col_props->[$j]->{max_w}
- }
- if ($calc_widths->[$j] != $new_w )
- {
- $calc_widths->[$j] = $new_w;
- $next_will_be_last_iter = 0;
- }
- $min_width += $new_w;
- }
- last if $is_last_iter;
- $is_last_iter = $next_will_be_last_iter;
+ my $span = 0;
+ # Calculate how much can be added to every column to fit the available width
+ $span = ($avail_width - $min_width) / scalar( @$col_props);
+ for (my $j = 0; $j < scalar(@$col_props); $j++ ) {
+ $calc_widths->[$j] = $col_props->[$j]->{min_w} + $span;
}
return ($calc_widths,$avail_width);