Projekt

Allgemein

Profil

« Zurück | Weiter » 

Revision 1de28336

Von Moritz Bunkus vor mehr als 16 Jahren hinzugefügt

  • ID 1de2833629df53e3687ba1e8f2c8d0aa731f5b17
  • Vorgänger d33ad436
  • Nachfolger 84ba8214

Kosmetik: Tabs in Spaces geändert; trailing whitespaces entfernt; Einrückungen und Ausrichtungen angepasst; Blockklammern angepasst. Keine funktionalen Änderungen.

Unterschiede anzeigen:

modules/override/PDF/Table.pm
17 17
#
18 18
############################################################
19 19

  
20
sub new
21
{
22
	my ($type) = @_;
23

  
24
	my $class = ref($type) || $type;
25
	my $self  = {};
26
	bless ($self, $class);
27
	return $self;
20
sub new {
21
  my ($type) = @_;
22

  
23
  my $class = ref($type) || $type;
24
  my $self  = {};
25
  bless ($self, $class);
26
  return $self;
28 27
}
29 28

  
30 29
############################################################
......
33 32
#
34 33
############################################################
35 34

  
36
sub text_block
37
{
38
    my $self 		= shift;
39
    my $text_object = shift;
40
    my $text 		= shift;	# The text to be displayed
41
    my %arg 		= @_;		# Additional Arguments
42

  
43
    my	( $align, $xpos, $ypos, $xbase, $ybase, $line_width, $wordspace, $endw , $width, $height) = 
44
		( undef , undef, undef, undef , undef , undef      , undef     , undef , undef , undef  );
45
    my @line 		= ();		# Temp data array with words on one line 
46
    my %width 		= ();		# The width of every unique word in the givven text
47

  
48
	# Try to provide backward compatibility
49
	foreach my $key (keys %arg)
50
	{
51
		my $newkey = $key;
52
		if($newkey =~ s#^-##)
53
		{
54
			$arg{$newkey} = $arg{$key};
55
			delete $arg{$key};
56
		}
57
	}
58
	#####
59

  
60
	#---
61
	# Lets check mandatory parameters with no default values
62
	#---
63
	$xbase 	= $arg{'x'} || -1;
64
	$ybase 	= $arg{'y'} || -1;
65
	$width 	= $arg{'w'} || -1;
66
	$height	= $arg{'h'} || -1;
67
	unless( $xbase  > 0 ){ print "Error: Left Edge of Block is NOT defined!\n";	return; }
68
	unless( $ybase  > 0 ){ print "Error: Base Line of Block is NOT defined!\n"; return; }
69
	unless( $width  > 0 ){ print "Error: Width of Block is NOT defined!\n"; 	return; }
70
	unless( $height > 0 ){ print "Error: Height of Block is NOT defined!\n";	return;	}
71
	# Check if any text to display
72
	unless( defined( $text) and length($text) > 0 )
73
	{
74
		print "Warning: No input text found. Trying to add dummy '-' and not to break everything.\n";
75
		$text = '-';
76
	}
77

  
78
    # Strip any <CR> and Split the text into paragraphs
79
	$text =~ s/\r//g;
80
    my @paragraphs 	= split(/\n/, $text);
81

  
82
	# Width between lines in pixels
83
	my $line_space = defined $arg{'lead'} && $arg{'lead'} > 0 ? $arg{'lead'} : 12;
84

  
85
    # Calculate width of all words
86
    my $space_width = $text_object->advancewidth("\x20");
87
    my @words = split(/\s+/, $text);
88
    foreach (@words) 
89
	{
90
		next if exists $width{$_};
91
		$width{$_} = $text_object->advancewidth($_);
35
sub text_block {
36
  my $self        = shift;
37
  my $text_object = shift;
38
  my $text        = shift;          # The text to be displayed
39
  my %arg         = @_;             # Additional Arguments
40

  
41
  my  ($align, $xpos, $ypos, $xbase, $ybase, $line_width, $wordspace, $endw , $width, $height)
42
    = (undef , undef, undef, undef , undef , undef      , undef     , undef , undef , undef  );
43
  my @line  = ();          # Temp data array with words on one line
44
  my %width = ();          # The width of every unique word in the givven text
45

  
46
  # Try to provide backward compatibility
47
  foreach my $key (keys %arg) {
48
    my $newkey = $key;
49
    if ($newkey =~ s#^-##) {
50
      $arg{$newkey} = $arg{$key};
51
      delete $arg{$key};
52
    }
53
  }
54
  #####
55

  
56
  #---
57
  # Lets check mandatory parameters with no default values
58
  #---
59
  $xbase  = $arg{'x'} || -1;
60
  $ybase  = $arg{'y'} || -1;
61
  $width  = $arg{'w'} || -1;
62
  $height = $arg{'h'} || -1;
63
  unless ( $xbase  > 0 ) { print "Error: Left Edge of Block is NOT defined!\n"; return; }
64
  unless ( $ybase  > 0 ) { print "Error: Base Line of Block is NOT defined!\n"; return; }
65
  unless ( $width  > 0 ) { print "Error: Width of Block is NOT defined!\n";     return; }
66
  unless ( $height > 0 ) { print "Error: Height of Block is NOT defined!\n";    return; }
67
  # Check if any text to display
68
  unless ( defined( $text) and length($text) > 0 ) {
69
    print "Warning: No input text found. Trying to add dummy '-' and not to break everything.\n";
70
    $text = '-';
71
  }
72

  
73
  # Strip any <CR> and Split the text into paragraphs
74
  $text          =~ s/\r//g;
75
  my @paragraphs =  split(/\n/, $text);
76

  
77
  # Width between lines in pixels
78
  my $line_space = defined $arg{'lead'} && $arg{'lead'} > 0 ? $arg{'lead'} : 12;
79

  
80
  # Calculate width of all words
81
  my $space_width = $text_object->advancewidth("\x20");
82
  my @words       = split(/\s+/, $text);
83
  foreach (@words) {
84
    next if exists $width{$_};
85
    $width{$_} = $text_object->advancewidth($_);
86
  }
87

  
88
  my @paragraph       = split(' ', shift(@paragraphs));
89
  my $first_line      = 1;
90
  my $first_paragraph = 1;
91

  
92
  # Little Init
93
  $xpos             = $xbase;
94
  $ypos             = $ybase;
95
  $ypos             = $ybase + $line_space;
96
  my $bottom_border = $ybase - $height;
97
  # While we can add another line
98
  while ( $ypos >= $bottom_border + $line_space ) {
99
    # Is there any text to render ?
100
    unless (@paragraph) {
101
      # Finish if nothing left
102
      last unless scalar @paragraphs;
103
      # Else take one line from the text
104
      @paragraph  = split(' ', shift( @paragraphs ) );
105

  
106
      $ypos      -= $arg{'parspace'} if $arg{'parspace'};
107
      last unless $ypos >= $bottom_border;
108
    }
109
    $ypos -= $line_space;
110
    $xpos  = $xbase;
111

  
112
    # While there's room on the line, add another word
113
    @line       = ();
114
    $line_width = 0;
115
    if ( $first_line && exists $arg{'hang'} ) {
116
      my $hang_width = $text_object->advancewidth($arg{'hang'});
117

  
118
      $text_object->translate( $xpos, $ypos );
119
      $text_object->text( $arg{'hang'} );
120

  
121
      $xpos          += $hang_width;
122
      $line_width    += $hang_width;
123
      $arg{'indent'} += $hang_width if $first_paragraph;
124

  
125
    } elsif ( $first_line && exists $arg{'flindent'} && $arg{'flindent'} > 0 ) {
126
      $xpos       += $arg{'flindent'};
127
      $line_width += $arg{'flindent'};
128

  
129
    } elsif ( $first_paragraph && exists $arg{'fpindent'} && $arg{'fpindent'} > 0 ) {
130
      $xpos       += $arg{'fpindent'};
131
      $line_width += $arg{'fpindent'};
132

  
133
    } elsif (exists $arg{'indent'} && $arg{'indent'} > 0 ) {
134
      $xpos       += $arg{'indent'};
135
      $line_width += $arg{'indent'};
136
    }
137

  
138
    # Lets take from paragraph as many words as we can put into $width - $indent;
139
    while ( @paragraph and $text_object->advancewidth( join("\x20", @line)."\x20" . $paragraph[0]) + $line_width < $width ) {
140
      push(@line, shift(@paragraph));
92 141
    }
142
    $line_width += $text_object->advancewidth(join('', @line));
93 143

  
94
    my @paragraph = split(' ', shift(@paragraphs));
95
    my $first_line = 1;
96
    my $first_paragraph = 1;
97

  
98
	# Little Init
99
	$xpos = $xbase;
100
	$ypos = $ybase;
101
	$ypos = $ybase + $line_space;
102
	my $bottom_border = $ybase - $height; 
103
    # While we can add another line
104
    while ( $ypos >= $bottom_border + $line_space ) 
105
	{
106
		# Is there any text to render ?
107
		unless (@paragraph) 
108
		{
109
			# Finish if nothing left
110
	    	last unless scalar @paragraphs;
111
			# Else take one line from the text
112
	    	@paragraph = split(' ', shift( @paragraphs ) );
113

  
114
	    	$ypos -= $arg{'parspace'} if $arg{'parspace'};
115
	    	last unless $ypos >= $bottom_border;
116
		}
117
		$ypos -= $line_space;
118
		$xpos = $xbase;
119

  
120
		# While there's room on the line, add another word
121
		@line = ();
122
		$line_width = 0;
123
		if( $first_line && exists $arg{'hang'} ) 
124
		{
125
		    my $hang_width = $text_object->advancewidth($arg{'hang'});
126
	
127
		    $text_object->translate( $xpos, $ypos );
128
		    $text_object->text( $arg{'hang'} );
129
	
130
		    $xpos         += $hang_width;
131
		    $line_width   += $hang_width;
132
		    $arg{'indent'} += $hang_width if $first_paragraph;
133
		}
134
		elsif( $first_line && exists $arg{'flindent'} && $arg{'flindent'} > 0 ) 
135
		{
136
		    $xpos += $arg{'flindent'};
137
		    $line_width += $arg{'flindent'};
138
		}
139
		elsif( $first_paragraph && exists $arg{'fpindent'} && $arg{'fpindent'} > 0 ) 
140
		{
141
		    $xpos += $arg{'fpindent'};
142
		    $line_width += $arg{'fpindent'};
143
		}
144
		elsif (exists $arg{'indent'} && $arg{'indent'} > 0 ) 
145
		{
146
		    $xpos += $arg{'indent'};
147
		    $line_width += $arg{'indent'};
148
		}
149
	
150
		# Lets take from paragraph as many words as we can put into $width - $indent; 
151
		while ( @paragraph and $text_object->advancewidth( join("\x20", @line)."\x20" . $paragraph[0]) + 
152
								$line_width < $width ) 
153
		{
154
		    push(@line, shift(@paragraph));
155
		}
156
		$line_width += $text_object->advancewidth(join('', @line));
157
			
158
		# calculate the space width
159
		if( $arg{'align'} eq 'fulljustify' or ($arg{'align'} eq 'justify' and @paragraph)) 
160
		{
161
		    @line = split(//,$line[0]) if (scalar(@line) == 1) ;
162
		    $wordspace = ($width - $line_width) / (scalar(@line) - 1);
163
		    $align='justify';
164
		} 
165
		else 
166
		{
167
		    $align=($arg{'align'} eq 'justify') ? 'left' : $arg{'align'};
168
		    $wordspace = $space_width;
169
		}
170
		$line_width += $wordspace * (scalar(@line) - 1);
171
	
172
		if( $align eq 'justify') 
173
		{
174
		    foreach my $word (@line) 
175
			{
176
				$text_object->translate( $xpos, $ypos );
177
				$text_object->text( $word );
178
				$xpos += ($width{$word} + $wordspace) if (@line);
179
		    }
180
		    $endw = $width;
181
		} 
182
		else 
183
		{
184
		    # calculate the left hand position of the line
185
		    if( $align eq 'right' ) 
186
			{
187
				$xpos += $width - $line_width;
188
		    } 
189
			elsif( $align eq 'center' ) 
190
			{
191
				$xpos += ( $width / 2 ) - ( $line_width / 2 );
192
		    }
193
	
194
		    # render the line
195
		    $text_object->translate( $xpos, $ypos );
196
		    $endw = $text_object->text( join("\x20", @line));
197
		}
198
		$first_line = 0;
199
    }#End of while(
200
    unshift(@paragraphs, join(' ',@paragraph)) if scalar(@paragraph);
201
    return ($endw, $ypos, join("\n", @paragraphs))
144
    # calculate the space width
145
    if ( $arg{'align'} eq 'fulljustify' or ($arg{'align'} eq 'justify' and @paragraph)) {
146
      @line      = split(//,$line[0]) if (scalar(@line) == 1) ;
147
      $wordspace = ($width - $line_width) / (scalar(@line) - 1);
148
      $align     ='justify';
149

  
150
    } else {
151
      $align     = ($arg{'align'} eq 'justify') ? 'left' : $arg{'align'};
152
      $wordspace = $space_width;
153
    }
154
    $line_width += $wordspace * (scalar(@line) - 1);
155

  
156
    if ( $align eq 'justify') {
157
      foreach my $word (@line) {
158
        $text_object->translate( $xpos, $ypos );
159
        $text_object->text( $word );
160
        $xpos += ($width{$word} + $wordspace) if (@line);
161
      }
162
      $endw = $width;
163

  
164
    } else {
165
      # calculate the left hand position of the line
166
      if ( $align eq 'right' ) {
167
        $xpos += $width - $line_width;
168

  
169
      } elsif ( $align eq 'center' ) {
170
        $xpos += ( $width / 2 ) - ( $line_width / 2 );
171
      }
172

  
173
      # render the line
174
      $text_object->translate( $xpos, $ypos );
175
      $endw = $text_object->text( join("\x20", @line));
176
    }
177
    $first_line = 0;
178
  }#End of while(
179

  
180
  unshift(@paragraphs, join(' ',@paragraph)) if scalar(@paragraph);
181

  
182
  return ($endw, $ypos, join("\n", @paragraphs))
202 183
}
203 184

  
204 185

  
205 186
############################################################
206 187
# table - utility method to build multi-row, multicolumn tables
207 188
############################################################
208
sub table
209
{
210
	my $self	= shift;
211
	my $pdf 	= shift;
212
	my $page 	= shift;
213
	my $data 	= shift;
214
	my %arg 	= @_;
215

  
216
	#=====================================
217
	# Mandatory Arguments Section
218
	#=====================================
219
	unless($pdf and $page and $data)
220
	{
221
		print "Error: Mandatory parameter is missing pdf/page/data object!\n";
222
		return;
223
	}
224
	# Try to provide backward compatibility
225
	foreach my $key (keys %arg)
226
	{
227
		my $newkey = $key;
228
		if($newkey =~ s#^-##)
229
		{
230
			$arg{$newkey} = $arg{$key};
231
			delete $arg{$key};
232
		}
233
	}
234
	#TODO: Add code for header props compatibility and col_props comp....
235
	#####
236
	my ( $xbase, $ybase, $width, $height ) = ( undef, undef, undef, undef );
237
	# Could be 'int' or 'real' values
238
	$xbase 	= $arg{'x'		} || -1;	
239
	$ybase 	= $arg{'start_y'} || -1;
240
	$width 	= $arg{'w'		} || -1;
241
	$height	= $arg{'start_h'} || -1;
242

  
243
	# Global geometry parameters are also mandatory. 
244
	unless( $xbase	> 0 ){ print "Error: Left Edge of Table is NOT defined!\n";	return; }
245
	unless( $ybase	> 0 ){ print "Error: Base Line of Table is NOT defined!\n"; return; }
246
	unless( $width	> 0 ){ print "Error: Width of Table is NOT defined!\n"; 	return; }
247
	unless( $height	> 0 ){ print "Error: Height of Table is NOT defined!\n";	return;	}
248

  
249
	# Ensure default values for -next_y and -next_h
250
	my $next_y	= $arg{'next_y'} || $arg{'start_y'} || 0;
251
	my $next_h	= $arg{'next_h'} || $arg{'start_h'} || 0;
252

  
253
	# Create Text Object
254
	my $txt 	= $page->text;
255
	# Set Default Properties
256
	my $fnt_name 	= $arg{'font'		 	 } || $pdf->corefont('Times',-encode => 'utf8');
257
	my $fnt_size 	= $arg{'font_size'	 	 } || 12;
258
	my $max_word_len= $arg{'max_word_length' } || 20;
259

  
260
	#=====================================
261
	# Table Header Section
262
	#=====================================
263
	# Disable header row into the table
264
	my $header_props = 0;
265
	# Check if the user enabled it ?
266
	if(defined $arg{'header_props'} and ref( $arg{'header_props'}) eq 'HASH')
267
	{
268
		# Transfer the reference to local variable
269
		$header_props = $arg{'header_props'};
270
		# Check other params and put defaults if needed
271
		$header_props->{'repeat'		} = $header_props->{'repeat' 		} || 0;
272
		$header_props->{'font'			} = $header_props->{'font' 			} || $fnt_name;
273
		$header_props->{'font_color'	} = $header_props->{'font_color' 	} || '#000066';
274
		$header_props->{'font_size'		} = $header_props->{'font_size' 	} || $fnt_size + 2;
275
		$header_props->{'bg_color'		} = $header_props->{'bg_color'		} || '#FFFFAA';
276
	}
277
	my $header_row	= undef;
278
	#=====================================
279
	# Other Parameters check
280
	#=====================================
281
	
282
	my $lead 		= $arg{'lead'			} || $fnt_size;
283
	my $pad_left 	= $arg{'padding_left'	} || $arg{'padding'} || 0;
284
	my $pad_right	= $arg{'padding_right'	} || $arg{'padding'} || 0;
285
	my $pad_top 	= $arg{'padding_top'	} || $arg{'padding'} || 0;
286
	my $pad_bot 	= $arg{'padding_bottom'	} || $arg{'padding'} || 0;
287
	my $pad_w 		= $pad_left + $pad_right;
288
	my $pad_h 		= $pad_top  + $pad_bot	;
289
	my $line_w 		= defined $arg{'border'} ? $arg{'border'} : 1 ;
290
	
291
	my $background_color_even 	= $arg{'background_color_even'	} || $arg{'background_color'} || undef;
292
	my $background_color_odd 	= $arg{'background_color_odd'	} || $arg{'background_color'} || undef;
293
	my $font_color_even 		= $arg{'font_color_even'		} || $arg{'font_color'		} || 'black';
294
	my $font_color_odd 			= $arg{'font_color_odd'			} || $arg{'font_color'		} || 'black';
295
	my $border_color 			= $arg{'border_color'			} || 'black';
296

  
297
	my $min_row_h 	= $fnt_size + $pad_top + $pad_bot;
298
	my $row_h 		= defined ($arg{'row_height'}) 
299
								&& 
300
					($arg{'row_height'} > $min_row_h) 
301
								? 
302
					 $arg{'row_height'} : $min_row_h;
303

  
304
	my $pg_cnt 		= 1;
305
	my $cur_y 		= $ybase;
306
	my $cell_props	= $arg{cell_props} || [];   # per cell properties
307
	my $row_cnt		= ( ref $header_props and $header_props->{'repeat'} ) ?  1 : 0; # current row in user data
308

  
309
	#If there is valid data array reference use it!
310
	if(ref $data eq 'ARRAY')
311
	{
312
		# Copy the header row if header is enabled
313
		@$header_row = $$data[0] if defined $header_props;
314
		# Determine column widths based on content
315

  
316
		#  an arrayref whose values are a hashref holding 
317
		#  the minimum and maximum width of that column
318
		my $col_props =  $arg{'column_props'} || [];
319

  
320
		# An array ref of arrayrefs whose values are 
321
		#  the actual widths of the column/row intersection
322
		my $row_props = [];
323
		# An array ref with the widths of the header row 
324
		my $header_row_props = [];
325
 
326
		# Scalars that hold sum of the maximum and minimum widths of all columns 
327
		my ( $max_col_w  , $min_col_w   ) = ( 0,0 );
328
		my ( $row, $col_name, $col_fnt_size, $space_w );
329

  
330
		# Hash that will hold the width of every word from input text
331
		my $word_w		 = {};
332
		my $rows_counter = 0;
333
		my $first_row	 = 1;
334

  
335
		foreach $row ( @{$data} )
336
		{
337
			my $column_widths = []; #holds the width of each column
338
			for( my $j = 0; $j < scalar(@$row) ; $j++ )
339
			{
340
				# look for font information for this column
341
				$col_fnt_size 	=  $col_props->[$j]->{'font_size'} || $fnt_size;
342
				if( !$rows_counter and ref $header_props)
343
				{	
344
					$txt->font(  $header_props->{'font'}, $header_props->{'font_size'} ); 
345
				}
346
				elsif( $col_props->[$j]->{'font'} ) 
347
				{	
348
					$txt->font( $col_props->[$j]->{'font'}, $col_fnt_size ); 
349
				}
350
				else
351
				{	
352
					$txt->font( $fnt_name, $col_fnt_size ); 
353
				}
354

  
355
				# This should fix a bug with very long word like serial numbers etc.
356
				# $myone is used because $1 gets out of scope in while condition
357
				my $myone;
358
				do{
359
			    	$myone = 0;
360
					# This RegEx will split any word that is longer than {25} symbols
361
					$row->[$j] =~ s#(\b\S{$max_word_len}?)(\S.*?\b)# $1 $2#;
362
					$myone = 1 if( defined $2 );
363
				}while( $myone );
364

  
365
				$space_w 				= $txt->advancewidth( "\x20" );
366
				$column_widths->[$j] 	= 0;
367
				$max_col_w 				= 0;
368
				$min_col_w 				= 0;
369

  
370
				my @words = split( /\s+/, $row->[$j] );
371

  
372
				foreach( @words ) 
373
				{
374
					unless( exists $word_w->{$_} )
375
					{	# Calculate the width of every word and add the space width to it
376
						$word_w->{$_} = $txt->advancewidth( $_ ) + $space_w;
377
					}
378
					$column_widths->[$j] 	+= $word_w->{$_};
379
					$min_col_w				 = $word_w->{$_} if $word_w->{$_} > $min_col_w;
380
					$max_col_w 				+= $word_w->{$_};
381
				}
382
				$min_col_w 					+= $pad_w;
383
				$max_col_w 					+= $pad_w;
384
				$column_widths->[$j] 		+= $pad_w;
385

  
386
				# Keep a running total of the overall min and max widths
387
				$col_props->[$j]->{min_w} = $col_props->[$j]->{min_w} || 0;
388
				$col_props->[$j]->{max_w} = $col_props->[$j]->{max_w} || 0;
389

  
390
				if( $min_col_w > $col_props->[$j]->{min_w} )
391
				{	# Calculated Minimum Column Width is more than user-defined
392
					$col_props->[$j]->{min_w} 	 = $min_col_w ;
393
				}
394
				if( $max_col_w > $col_props->[$j]->{max_w} )
395
				{	# Calculated Maximum Column Width is more than user-defined
396
					$col_props->[$j]->{max_w} 	 = $max_col_w ;
397
				}
398
			}#End of for(my $j....
399
			$row_props->[$rows_counter] = $column_widths;
400
			# Copy the calculated row properties of header row. 
401
			@$header_row_props = @$column_widths if(!$rows_counter and ref $header_props);
402
			$rows_counter++;
403
		}
404
		# Calc real column widths and expand table width if needed.
405
		my $calc_column_widths; 
406
		($calc_column_widths, $width) = $self->CalcColumnWidths( $col_props, $width );
407
        my $num_cols     = scalar @{ $calc_column_widths };
408
		my $comp_cnt 	 = 1;
409
		$rows_counter	 = 0;
410

  
411
		my ( $gfx	  , $gfx_bg		, $background_color	, $font_color,				);
412
		my ( $bot_marg, $table_top_y, $text_start		, $record,	$record_widths	);
413

  
414
		# Each iteration adds a new page as neccessary
415
		while(scalar(@{$data}))
416
		{
417
			my $page_header;
418
			if($pg_cnt == 1)
419
			{
420
				$table_top_y = $ybase;
421
				$bot_marg = $table_top_y - $height;
422
			}
423
			else
424
			{
425
				if(ref $arg{'new_page_func'})
426
				{	
427
					$page = &{$arg{'new_page_func'}};	
428
				}
429
				else
430
				{	
431
					$page = $pdf->page;	
432
				}
433

  
434
				$table_top_y = $next_y;
435
				$bot_marg = $table_top_y - $next_h;
436

  
437
				if( ref $header_props and $header_props->{'repeat'})
438
				{
439
					# Copy Header Data
440
					@$page_header = @$header_row;
441
					my $hrp ;
442
					@$hrp = @$header_row_props ;
443
					# Then prepend it to master data array
444
					unshift @$data		,@$page_header	;
445
					unshift @$row_props	,$hrp			;
446
					$first_row = 1; # Means YES
447
				}
448
			}
449

  
450
			# Check for safety reasons
451
			if( $bot_marg < 0 )
452
			{	# This warning should remain i think
453
# 				print "!!! Warning: !!! Incorrect Table Geometry! Setting bottom margin to end of sheet!\n";
454
				$bot_marg = 0;
455
			}
456

  
457
			$gfx_bg = $page->gfx;
458
			$txt = $page->text;
459
			$txt->font($fnt_name, $fnt_size); 
460
			$gfx = $page->gfx;
461
			$gfx->strokecolor($border_color);
462
			$gfx->linewidth($line_w);
463

  
464
			# Draw the top line
465
			$cur_y = $table_top_y;
466
			$gfx->move( $xbase , $cur_y );
467
			$gfx->hline($xbase + $width );
468

  
469
			# Each iteration adds a row to the current page until the page is full 
470
			#  or there are no more rows to add
471
			while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg)
472
			{
473
				# Remove the next item from $data
474
				$record = shift @{$data};
475
				# Added to resolve infite loop bug with returned undef values
476
				for(my $d = 0; $d < scalar(@{$record}) ; $d++)
477
				{ 
478
					$record->[$d] = '-' unless( defined $record->[$d]);	
479
				}
480

  
481
				$record_widths = shift @$row_props;
482
				next unless $record;
483

  
484
				# Choose colors for this row
485
				$background_color = $rows_counter % 2 ? $background_color_even	: $background_color_odd;
486
				$font_color 	  = $rows_counter % 2 ? $font_color_even		: $font_color_odd;
487

  
488
				if($first_row and ref $header_props)
489
				{
490
					$background_color = $header_props->{'bg_color'}
491
				}
492
				$text_start		 = $cur_y - $fnt_size - $pad_top;
493
				my $cur_x		 = $xbase;
494
				my $leftovers 	 = undef;	# Reference to text that is returned from textblock()
495
				my $do_leftovers = 0;
496

  
497
                my ($colspan, @vertical_lines);
498

  
499
				# Process every column from current row
500
				for( my $j = 0; $j < scalar( @$record); $j++ ) 
501
				{
502
					next unless $col_props->[$j]->{max_w};
503
					next unless $col_props->[$j]->{min_w};	
504
					$leftovers->[$j] = undef;
505

  
506
					# Choose font color
507
					if( $first_row and ref $header_props )
508
                    {   
509
						$txt->fillcolor( $header_props->{'font_color'} ); 
510
					}	
511
					elsif( $cell_props->[$row_cnt][$j]{font_color} )
512
					{
513
						$txt->fillcolor( $cell_props->[$row_cnt][$j]{font_color} );
514
					}
515
					elsif( $col_props->[$j]->{'font_color'} )
516
					{ 
517
						$txt->fillcolor( $col_props->[$j]->{'font_color'} ); 
518
					}
519
					else
520
					{ 
521
						$txt->fillcolor($font_color);	
522
					}
523

  
524
					# Choose font size
525
					if( $first_row and ref $header_props )
526
					{	
527
						$col_fnt_size = $header_props->{'font_size'}; 
528
					}
529
					elsif( $col_props->[$j]->{'font_size'} )
530
					{	
531
						$col_fnt_size = $col_props->[$j]->{'font_size'}; 
532
					}
533
					else
534
					{	
535
						$col_fnt_size = $fnt_size; 
536
					}
537

  
538
					# Choose font family
539
					if( $first_row and ref $header_props )
540
					{	
541
						$txt->font( $header_props->{'font'}, $header_props->{'font_size'}); 
542
					}
543
					elsif( $col_props->[$j]->{'font'} )
544
					{	
545
						$txt->font( $col_props->[$j]->{'font'}, $col_fnt_size);	
546
					}
547
					else
548
					{
549
						$txt->font( $fnt_name, $col_fnt_size); 
550
					}
551
					#TODO: Implement Center text align
552
					$col_props->[$j]->{justify} = $col_props->[$j]->{justify} || 'left';
553

  
554
                    my $this_width;
555
                    if (!$first_row && $cell_props->[$row_cnt]->[$j]->{colspan}) {
556
                        $colspan     = -1 == $cell_props->[$row_cnt]->[$j]->{colspan} ? $num_cols - $j : $cell_props->[$row_cnt]->[$j]->{colspan};
557
                        my $last_idx = $j + $colspan - 1;
558
                        $this_width  = sum @{ $calc_column_widths }[$j..$last_idx];
559

  
560
                    } else {
561
                        $this_width = $calc_column_widths->[$j];
562
                    }
563

  
564
					# If the content is wider than the specified width, we need to add the text as a text block
565
					if($record->[$j] !~ m#(.\n.)# and  $record_widths->[$j] and ($record_widths->[$j] < $this_width))
566
					{
567
						my $space = $pad_left;
568
						if($col_props->[$j]->{justify} eq 'right')
569
						{
570
							$space = $this_width -($txt->advancewidth($record->[$j]) + $pad_right);
571
						}
572
						$txt->translate( $cur_x + $space, $text_start );
573
						$txt->text( $record->[$j] );
574
					}
575
					# Otherwise just use the $page->text() method
576
					else
577
					{
578
						my($width_of_last_line, $ypos_of_last_line, $left_over_text) = $self->text_block(
579
						   	$txt,
580
						    $record->[$j],
581
						    x        => $cur_x + $pad_left,
582
						    y        => $text_start,
583
						    w        => $this_width - $pad_w,
584
							h		 => $cur_y - $bot_marg - $pad_top - $pad_bot,
585
							align    => $col_props->[$j]->{justify},
586
							lead	 => $lead
587
						);
588
						# Desi - Removed $lead because of fixed incorrect ypos bug in text_block
589
						my $this_row_h = $cur_y - ( $ypos_of_last_line - $pad_bot );
590
						$row_h = $this_row_h if $this_row_h > $row_h;
591
						if( $left_over_text )
592
						{
593
							$leftovers->[$j] = $left_over_text;
594
							$do_leftovers 	 = 1;
595
						}
596
					}
597
					$cur_x += $calc_column_widths->[$j];
598

  
599
                    push @vertical_lines, (!$colspan || (1 >= $colspan)) ? 1 : 0;
600
                    $colspan-- if ($colspan);
601
				}
602
				if( $do_leftovers )
603
				{
604
					unshift @$data, $leftovers;
605
					unshift @$row_props, $record_widths;
606
					$rows_counter--;
607
				}
608
				# Draw cell bgcolor
609
				# This has to be separately from the text loop 
610
				#  because we do not know the final height of the cell until all text has been drawn
611
				$cur_x = $xbase;
612
				for(my $j =0;$j < scalar(@$record);$j++)
613
				{
614
					if ( 	$cell_props->[$row_cnt][$j]->{'background_color'} || 
615
							$col_props->[$j]->{'background_color'} || 
616
							$background_color ) 
617
					{
618
						$gfx_bg->rect( $cur_x, $cur_y-$row_h, $calc_column_widths->[$j], $row_h);
619
						if ( $cell_props->[$row_cnt][$j]->{'background_color'} && !$first_row )
620
						{
621
						    $gfx_bg->fillcolor($cell_props->[$row_cnt][$j]->{'background_color'});
622
						}
623
						elsif( $col_props->[$j]->{'background_color'} && !$first_row  )
624
						{
625
						    $gfx_bg->fillcolor($col_props->[$j]->{'background_color'});
626
						}
627
						else
628
						{
629
						    $gfx_bg->fillcolor($background_color);
630
						}
631
						$gfx_bg->fill();
632
					}
633

  
634
					$cur_x += $calc_column_widths->[$j];
635

  
636
                    if ($line_w && $vertical_lines[$j] && ($j != (scalar(@{ $record }) - 1))) {
637
                        $gfx->move($cur_x, $cur_y);
638
                        $gfx->vline($cur_y - $row_h);
639
                        $gfx->fillcolor($border_color);
640
                    }
641
				}#End of for(my $j....
642

  
643
				$cur_y -= $row_h;
644
				$row_h  = $min_row_h;
645
				$gfx->move(  $xbase , $cur_y );
646
				$gfx->hline( $xbase + $width );
647
				$rows_counter++;
648
				$row_cnt++ unless ( $first_row );
649
				$first_row = 0;
650
			}# End of while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg)
651

  
652
			# Draw vertical lines
653
            if ($line_w) {
654
                $gfx->move($xbase, $table_top_y);
655
                $gfx->vline($cur_y);
656
                $gfx->move($xbase + sum(@{ $calc_column_widths }[0..$num_cols - 1]), $table_top_y);
657
                $gfx->vline($cur_y);
658
                $gfx->fillcolor($border_color);
659
                $gfx->stroke();
189
sub table {
190
  my $self  = shift;
191
  my $pdf   = shift;
192
  my $page  = shift;
193
  my $data  = shift;
194
  my %arg   = @_;
195

  
196
  #=====================================
197
  # Mandatory Arguments Section
198
  #=====================================
199
  unless ($pdf and $page and $data) {
200
    print "Error: Mandatory parameter is missing pdf/page/data object!\n";
201
    return;
202
  }
203
  # Try to provide backward compatibility
204
  foreach my $key (keys %arg) {
205
    my $newkey = $key;
206
    if ($newkey =~ s#^-##) {
207
      $arg{$newkey} = $arg{$key};
208
      delete $arg{$key};
209
    }
210
  }
211
  #TODO: Add code for header props compatibility and col_props comp....
212
  #####
213
  my ( $xbase, $ybase, $width, $height ) = ( undef, undef, undef, undef );
214
  # Could be 'int' or 'real' values
215
  $xbase  = $arg{'x'    } || -1;
216
  $ybase  = $arg{'start_y'} || -1;
217
  $width  = $arg{'w'    } || -1;
218
  $height = $arg{'start_h'} || -1;
219

  
220
  # Global geometry parameters are also mandatory.
221
  unless ( $xbase  > 0 ) { print "Error: Left Edge of Table is NOT defined!\n"; return; }
222
  unless ( $ybase  > 0 ) { print "Error: Base Line of Table is NOT defined!\n"; return; }
223
  unless ( $width  > 0 ) { print "Error: Width of Table is NOT defined!\n";     return; }
224
  unless ( $height > 0 ) { print "Error: Height of Table is NOT defined!\n";    return; }
225

  
226
  # Ensure default values for -next_y and -next_h
227
  my $next_y       = $arg{'next_y'} || $arg{'start_y'} || 0;
228
  my $next_h       = $arg{'next_h'} || $arg{'start_h'} || 0;
229

  
230
  # Create Text Object
231
  my $txt          = $page->text;
232
  # Set Default Properties
233
  my $fnt_name     = $arg{'font'}            || $pdf->corefont('Times', -encode => 'utf8');
234
  my $fnt_size     = $arg{'font_size'}       || 12;
235
  my $max_word_len = $arg{'max_word_length'} || 20;
236

  
237
  #=====================================
238
  # Table Header Section
239
  #=====================================
240
  # Disable header row into the table
241
  my $header_props = 0;
242
  # Check if the user enabled it ?
243
  if (defined $arg{'header_props'} and ref( $arg{'header_props'}) eq 'HASH') {
244
    # Transfer the reference to local variable
245
    $header_props = $arg{'header_props'};
246
    # Check other params and put defaults if needed
247
    $header_props->{'repeat'}     = $header_props->{'repeat'}     || 0;
248
    $header_props->{'font'}       = $header_props->{'font'}       || $fnt_name;
249
    $header_props->{'font_color'} = $header_props->{'font_color'} || '#000066';
250
    $header_props->{'font_size'}  = $header_props->{'font_size'}  || $fnt_size + 2;
251
    $header_props->{'bg_color'}   = $header_props->{'bg_color'}   || '#FFFFAA';
252
  }
253
  my $header_row  = undef;
254
  #=====================================
255
  # Other Parameters check
256
  #=====================================
257

  
258
  my $lead      = $arg{'lead'}           || $fnt_size;
259
  my $pad_left  = $arg{'padding_left'}   || $arg{'padding'} || 0;
260
  my $pad_right = $arg{'padding_right'}  || $arg{'padding'} || 0;
261
  my $pad_top   = $arg{'padding_top'}    || $arg{'padding'} || 0;
262
  my $pad_bot   = $arg{'padding_bottom'} || $arg{'padding'} || 0;
263
  my $pad_w     = $pad_left + $pad_right;
264
  my $pad_h     = $pad_top  + $pad_bot  ;
265
  my $line_w    = defined $arg{'border'} ? $arg{'border'} : 1 ;
266

  
267
  my $background_color_even = $arg{'background_color_even'} || $arg{'background_color'} || undef;
268
  my $background_color_odd  = $arg{'background_color_odd'}  || $arg{'background_color'} || undef;
269
  my $font_color_even       = $arg{'font_color_even'}       || $arg{'font_color'}       || 'black';
270
  my $font_color_odd        = $arg{'font_color_odd'}        || $arg{'font_color'}       || 'black';
271
  my $border_color          = $arg{'border_color'}          || 'black';
272

  
273
  my $min_row_h  = $fnt_size + $pad_top + $pad_bot;
274
  my $row_h      = defined ($arg{'row_height'}) && ($arg{'row_height'} > $min_row_h) ? $arg{'row_height'} : $min_row_h;
275

  
276
  my $pg_cnt     = 1;
277
  my $cur_y      = $ybase;
278
  my $cell_props = $arg{cell_props} || [];   # per cell properties
279
  my $row_cnt    = ( ref $header_props and $header_props->{'repeat'} ) ?  1 : 0; # current row in user data
280

  
281
  #If there is valid data array reference use it!
282
  if (ref $data eq 'ARRAY') {
283
    # Copy the header row if header is enabled
284
    @$header_row = $$data[0] if defined $header_props;
285
    # Determine column widths based on content
286

  
287
    #  an arrayref whose values are a hashref holding
288
    #  the minimum and maximum width of that column
289
    my $col_props =  $arg{'column_props'} || [];
290

  
291
    # An array ref of arrayrefs whose values are
292
    #  the actual widths of the column/row intersection
293
    my $row_props = [];
294
    # An array ref with the widths of the header row
295
    my $header_row_props = [];
296

  
297
    # Scalars that hold sum of the maximum and minimum widths of all columns
298
    my ( $max_col_w, $min_col_w ) = ( 0,0 );
299
    my ( $row, $col_name, $col_fnt_size, $space_w );
300

  
301
    # Hash that will hold the width of every word from input text
302
    my $word_w       = {};
303
    my $rows_counter = 0;
304
    my $first_row    = 1;
305

  
306
    foreach $row ( @{$data} ) {
307
      my $column_widths = []; #holds the width of each column
308
      for( my $j = 0; $j < scalar(@$row) ; $j++ ) {
309
        # look for font information for this column
310
        $col_fnt_size   =  $col_props->[$j]->{'font_size'} || $fnt_size;
311
        if ( !$rows_counter and ref $header_props) {
312
          $txt->font(  $header_props->{'font'}, $header_props->{'font_size'} );
313

  
314
        } elsif ( $col_props->[$j]->{'font'} ) {
315
          $txt->font( $col_props->[$j]->{'font'}, $col_fnt_size );
316

  
317
        } else {
318
          $txt->font( $fnt_name, $col_fnt_size );
319
        }
320

  
321
        # This should fix a bug with very long word like serial numbers etc.
322
        # $myone is used because $1 gets out of scope in while condition
323
        my $myone;
324
        do {
325
          $myone = 0;
326
          # This RegEx will split any word that is longer than {25} symbols
327
          $row->[$j] =~ s#(\b\S{$max_word_len}?)(\S.*?\b)# $1 $2#;
328
          $myone = 1 if ( defined $2 );
329
        } while( $myone );
330

  
331
        $space_w             = $txt->advancewidth( "\x20" );
332
        $column_widths->[$j] = 0;
333
        $max_col_w           = 0;
334
        $min_col_w           = 0;
335

  
336
        my @words = split( /\s+/, $row->[$j] );
337

  
338
        foreach( @words ) {
339
          unless ( exists $word_w->{$_} ) { # Calculate the width of every word and add the space width to it
340
            $word_w->{$_} = $txt->advancewidth( $_ ) + $space_w;
341
          }
342
          $column_widths->[$j] += $word_w->{$_};
343
          $min_col_w            = $word_w->{$_} if $word_w->{$_} > $min_col_w;
344
          $max_col_w           += $word_w->{$_};
345
        }
346
        $min_col_w             += $pad_w;
347
        $max_col_w             += $pad_w;
348
        $column_widths->[$j]   += $pad_w;
349

  
350
        # Keep a running total of the overall min and max widths
351
        $col_props->[$j]->{min_w} = $col_props->[$j]->{min_w} || 0;
352
        $col_props->[$j]->{max_w} = $col_props->[$j]->{max_w} || 0;
353

  
354
        if ( $min_col_w > $col_props->[$j]->{min_w} ) { # Calculated Minimum Column Width is more than user-defined
355
          $col_props->[$j]->{min_w}    = $min_col_w ;
356
        }
357
        if ( $max_col_w > $col_props->[$j]->{max_w} ) { # Calculated Maximum Column Width is more than user-defined
358
          $col_props->[$j]->{max_w}    = $max_col_w ;
359
        }
360
      }#End of for(my $j....
361
      $row_props->[$rows_counter] = $column_widths;
362
      # Copy the calculated row properties of header row.
363
      @$header_row_props = @$column_widths if (!$rows_counter and ref $header_props);
364
      $rows_counter++;
365
    }
366
    # Calc real column widths and expand table width if needed.
367
    my $calc_column_widths;
368
    ($calc_column_widths, $width) = $self->CalcColumnWidths( $col_props, $width );
369
    my $num_cols  = scalar @{ $calc_column_widths };
370
    my $comp_cnt  = 1;
371
    $rows_counter = 0;
372

  
373
    my ( $gfx   , $gfx_bg   , $background_color , $font_color,        );
374
    my ( $bot_marg, $table_top_y, $text_start   , $record,  $record_widths  );
375

  
376
    # Each iteration adds a new page as neccessary
377
    while(scalar(@{$data})) {
378
      my $page_header;
379
      if ($pg_cnt == 1) {
380
        $table_top_y = $ybase;
381
        $bot_marg = $table_top_y - $height;
382

  
383
      } else {
384
        if (ref $arg{'new_page_func'}) {
385
          $page = &{$arg{'new_page_func'}};
386

  
387
        } else {
388
          $page = $pdf->page;
389
        }
390

  
391
        $table_top_y = $next_y;
392
        $bot_marg = $table_top_y - $next_h;
393

  
394
        if ( ref $header_props and $header_props->{'repeat'}) {
395
          # Copy Header Data
396
          @$page_header = @$header_row;
397
          my $hrp ;
398
          @$hrp = @$header_row_props ;
399
          # Then prepend it to master data array
400
          unshift @$data    ,@$page_header  ;
401
          unshift @$row_props ,$hrp     ;
402
          $first_row = 1; # Means YES
403
        }
404
      }
405

  
406
      # Check for safety reasons
407
      if ( $bot_marg < 0 ) { # This warning should remain i think
408
#         print "!!! Warning: !!! Incorrect Table Geometry! Setting bottom margin to end of sheet!\n";
409
        $bot_marg = 0;
410
      }
411

  
412
      $gfx_bg = $page->gfx;
413
      $txt = $page->text;
414
      $txt->font($fnt_name, $fnt_size);
415
      $gfx = $page->gfx;
416
      $gfx->strokecolor($border_color);
417
      $gfx->linewidth($line_w);
418

  
419
      # Draw the top line
420
      $cur_y = $table_top_y;
421
      $gfx->move( $xbase , $cur_y );
422
      $gfx->hline($xbase + $width );
423

  
424
      # Each iteration adds a row to the current page until the page is full
425
      #  or there are no more rows to add
426
      while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg) {
427
        # Remove the next item from $data
428
        $record = shift @{$data};
429
        # Added to resolve infite loop bug with returned undef values
430
        for(my $d = 0; $d < scalar(@{$record}) ; $d++) {
431
          $record->[$d] = '-' unless ( defined $record->[$d]);
432
        }
433

  
434
        $record_widths = shift @$row_props;
435
        next unless $record;
436

  
437
        # Choose colors for this row
438
        $background_color = $rows_counter % 2 ? $background_color_even  : $background_color_odd;
439
        $font_color     = $rows_counter % 2 ? $font_color_even    : $font_color_odd;
440

  
441
        if ($first_row and ref $header_props) {
442
          $background_color = $header_props->{'bg_color'}
443
        }
444
        $text_start    = $cur_y - $fnt_size - $pad_top;
445
        my $cur_x    = $xbase;
446
        my $leftovers    = undef; # Reference to text that is returned from textblock()
447
        my $do_leftovers = 0;
448

  
449
        my ($colspan, @vertical_lines);
450

  
451
        # Process every column from current row
452
        for( my $j = 0; $j < scalar( @$record); $j++ ) {
453
          next unless $col_props->[$j]->{max_w};
454
          next unless $col_props->[$j]->{min_w};
455
          $leftovers->[$j] = undef;
456

  
457
          # Choose font color
458
          if ( $first_row and ref $header_props ) {
459
            $txt->fillcolor( $header_props->{'font_color'} );
460

  
461
          } elsif ( $cell_props->[$row_cnt][$j]{font_color} ) {
462
            $txt->fillcolor( $cell_props->[$row_cnt][$j]{font_color} );
463

  
464
          } elsif ( $col_props->[$j]->{'font_color'} ) {
465
            $txt->fillcolor( $col_props->[$j]->{'font_color'} );
466

  
467
          } else {
468
            $txt->fillcolor($font_color);
469
          }
470

  
471
          # Choose font size
472
          if ( $first_row and ref $header_props ) {
473
            $col_fnt_size = $header_props->{'font_size'};
474

  
475
          } elsif ( $col_props->[$j]->{'font_size'} ) {
476
            $col_fnt_size = $col_props->[$j]->{'font_size'};
477

  
478
          } else {
479
            $col_fnt_size = $fnt_size;
480
          }
481

  
482
          # Choose font family
483
          if ( $first_row and ref $header_props ) {
484
            $txt->font( $header_props->{'font'}, $header_props->{'font_size'});
485

  
486
          } elsif ( $col_props->[$j]->{'font'} ) {
487
            $txt->font( $col_props->[$j]->{'font'}, $col_fnt_size);
488

  
489
          } else {
490
            $txt->font( $fnt_name, $col_fnt_size);
491
          }
492
          #TODO: Implement Center text align
493
          $col_props->[$j]->{justify} = $col_props->[$j]->{justify} || 'left';
494

  
495
          my $this_width;
496
          if (!$first_row && $cell_props->[$row_cnt]->[$j]->{colspan}) {
497
            $colspan     = -1 == $cell_props->[$row_cnt]->[$j]->{colspan} ? $num_cols - $j : $cell_props->[$row_cnt]->[$j]->{colspan};
498
            my $last_idx = $j + $colspan - 1;
499
            $this_width  = sum @{ $calc_column_widths }[$j..$last_idx];
500

  
501
          } else {
502
            $this_width = $calc_column_widths->[$j];
503
          }
504

  
505
          # If the content is wider than the specified width, we need to add the text as a text block
506
          if ($record->[$j] !~ m#(.\n.)# and  $record_widths->[$j] and ($record_widths->[$j] < $this_width)) {
507
            my $space = $pad_left;
508
            if ($col_props->[$j]->{justify} eq 'right') {
509
              $space = $this_width -($txt->advancewidth($record->[$j]) + $pad_right);
660 510
            }
661
			$pg_cnt++;
662
		}# End of while(scalar(@{$data}))
663
	}# End of if(ref $data eq 'ARRAY')
664

  
665
	return ($page,--$pg_cnt,$cur_y);
511
            $txt->translate( $cur_x + $space, $text_start );
512
            $txt->text( $record->[$j] );
513
          } else { # Otherwise just use the $page->text() method
514
            my($width_of_last_line, $ypos_of_last_line, $left_over_text) =
515
              $self->text_block($txt,
516
                                $record->[$j],
517
                                'x'     => $cur_x + $pad_left,
518
                                'y'     => $text_start,
519
                                'w'     => $this_width - $pad_w,
520
                                'h'     => $cur_y - $bot_marg - $pad_top - $pad_bot,
521
                                'align' => $col_props->[$j]->{justify},
522
                                'lead'  => $lead
523
              );
524
            # Desi - Removed $lead because of fixed incorrect ypos bug in text_block
525
            my $this_row_h = $cur_y - ( $ypos_of_last_line - $pad_bot );
526
            $row_h = $this_row_h if $this_row_h > $row_h;
527
            if ( $left_over_text ) {
528
              $leftovers->[$j] = $left_over_text;
529
              $do_leftovers    = 1;
530
            }
531
          }
532
          $cur_x += $calc_column_widths->[$j];
533

  
534
          push @vertical_lines, (!$colspan || (1 >= $colspan)) ? 1 : 0;
535
          $colspan-- if ($colspan);
536
        }
537

  
538
        if ( $do_leftovers ) {
539
          unshift @$data, $leftovers;
540
          unshift @$row_props, $record_widths;
541
          $rows_counter--;
542
        }
543

  
544
        # Draw cell bgcolor
545
        # This has to be separately from the text loop
546
        #  because we do not know the final height of the cell until all text has been drawn
547
        $cur_x = $xbase;
548
        for(my $j =0;$j < scalar(@$record);$j++) {
549
          if (  $cell_props->[$row_cnt][$j]->{'background_color'} ||
550
                $col_props->[$j]->{'background_color'} ||
551
                $background_color ) {
552
            $gfx_bg->rect( $cur_x, $cur_y-$row_h, $calc_column_widths->[$j], $row_h);
553
            if ( $cell_props->[$row_cnt][$j]->{'background_color'} && !$first_row ) {
554
              $gfx_bg->fillcolor($cell_props->[$row_cnt][$j]->{'background_color'});
555

  
556
            } elsif ( $col_props->[$j]->{'background_color'} && !$first_row  ) {
557
              $gfx_bg->fillcolor($col_props->[$j]->{'background_color'});
558

  
559
            } else {
560
              $gfx_bg->fillcolor($background_color);
561
            }
562
            $gfx_bg->fill();
563
          }
564

  
565
          $cur_x += $calc_column_widths->[$j];
566

  
567
          if ($line_w && $vertical_lines[$j] && ($j != (scalar(@{ $record }) - 1))) {
568
            $gfx->move($cur_x, $cur_y);
569
            $gfx->vline($cur_y - $row_h);
570
            $gfx->fillcolor($border_color);
571
          }
572
        }#End of for(my $j....
573

  
574
        $cur_y -= $row_h;
575
        $row_h  = $min_row_h;
576
        $gfx->move(  $xbase , $cur_y );
577
        $gfx->hline( $xbase + $width );
578
        $rows_counter++;
579
        $row_cnt++ unless ( $first_row );
580
        $first_row = 0;
581
      }# End of while(scalar(@{$data}) and $cur_y-$row_h > $bot_marg)
582

  
583
      # Draw vertical lines
584
      if ($line_w) {
585
        $gfx->move($xbase, $table_top_y);
586
        $gfx->vline($cur_y);
587
        $gfx->move($xbase + sum(@{ $calc_column_widths }[0..$num_cols - 1]), $table_top_y);
588
        $gfx->vline($cur_y);
589
        $gfx->fillcolor($border_color);
590
        $gfx->stroke();
591
      }
592
      $pg_cnt++;
593
    }# End of while(scalar(@{$data}))
594
  }# End of if (ref $data eq 'ARRAY')
595

  
596
  return ($page,--$pg_cnt,$cur_y);
666 597
}
667 598

  
668 599

  
669 600
# calculate the column widths
670
sub CalcColumnWidths
671
{
672
	my $self 		= shift;
673
	my $col_props 	= shift;
674
	my $avail_width = shift;
675
	my $min_width 	= 0;
676

  
677
	my $calc_widths	;
678
	for(my $j = 0; $j < scalar( @$col_props); $j++)
679
	{
680
		$min_width += $col_props->[$j]->{min_w};
681
	}
682

  
683
	# I think this is the optimal variant when good view can be guaranateed
684
	if($avail_width < $min_width)
685
	{
686
# 		print "!!! Warning !!!\n Calculated Mininal width($min_width) > Table width($avail_width).\n",
687
# 			' Expanding table width to:',int($min_width)+1,' but this could lead to unexpected results.',"\n",
688
# 			' Possible solutions:',"\n",
689
# 			'  0)Increase table width.',"\n",
690
# 			'  1)Decrease font size.',"\n",
691
# 			'  2)Choose a more narrow font.',"\n",
692
# 			'  3)Decrease "max_word_length" parameter.',"\n",
693
# 			'  4)Rotate page to landscape(if it is portrait).',"\n",
694
# 			'  5)Use larger paper size.',"\n",
695
# 			'!!! --------- !!!',"\n";
696
		$avail_width = int( $min_width) + 1;
697

  
698
	}
699

  
700
	my $span = 0;
701
	# Calculate how much can be added to every column to fit the available width
702
	$span = ($avail_width - $min_width) / scalar( @$col_props);
703
	for(my $j = 0; $j < scalar(@$col_props); $j++ )
704
	{
705
		$calc_widths->[$j] = $col_props->[$j]->{min_w} + $span;
706
	}
707

  
708
	return ($calc_widths,$avail_width);
601
sub CalcColumnWidths {
602
  my $self    = shift;
603
  my $col_props   = shift;
604
  my $avail_width = shift;
605
  my $min_width   = 0;
606

  
607
  my $calc_widths ;
608
  for(my $j = 0; $j < scalar( @$col_props); $j++) {
609
    $min_width += $col_props->[$j]->{min_w};
610
  }
611

  
612
  # I think this is the optimal variant when good view can be guaranateed
613
  if ($avail_width < $min_width) {
614
#     print "!!! Warning !!!\n Calculated Mininal width($min_width) > Table width($avail_width).\n",
615
#       ' Expanding table width to:',int($min_width)+1,' but this could lead to unexpected results.',"\n",
616
#       ' Possible solutions:',"\n",
617
#       '  0)Increase table width.',"\n",
618
#       '  1)Decrease font size.',"\n",
619
#       '  2)Choose a more narrow font.',"\n",
620
#       '  3)Decrease "max_word_length" parameter.',"\n",
621
#       '  4)Rotate page to landscape(if it is portrait).',"\n",
622
#       '  5)Use larger paper size.',"\n",
623
#       '!!! --------- !!!',"\n";
624
    $avail_width = int( $min_width) + 1;
625

  
626
  }
627

  
628
  my $span = 0;
629
  # Calculate how much can be added to every column to fit the available width
630
  $span = ($avail_width - $min_width) / scalar( @$col_props);
631
  for (my $j = 0; $j < scalar(@$col_props); $j++ ) {
632
    $calc_widths->[$j] = $col_props->[$j]->{min_w} + $span;
633
  }
634

  
635
  return ($calc_widths,$avail_width);
709 636
}
710 637
1;
711 638

  
......
768 695

  
769 696
=head1 DESCRIPTION
770 697

  
771
This class is a utility for use with the PDF::API2 module from CPAN. 
772
It can be used to display text data in a table layout within the PDF. 
773
The text data must be in a 2d array (such as returned by a DBI statement handle fetchall_arrayref() call). 
774
The PDF::Table will automatically add as many new pages as necessary to display all of the data. 
775
Various layout properties, such as font, font size, and cell padding and background color can be specified for each column and/or for even/odd rows. 
776
Also a (non)repeated header row with different layout properties can be specified. 
698
This class is a utility for use with the PDF::API2 module from CPAN.
699
It can be used to display text data in a table layout within the PDF.
700
The text data must be in a 2d array (such as returned by a DBI statement handle fetchall_arrayref() call).
701
The PDF::Table will automatically add as many new pages as necessary to display all of the data.
702
Various layout properties, such as font, font size, and cell padding and background color can be specified for each column and/or for even/odd rows.
703
Also a (non)repeated header row with different layout properties can be specified.
777 704

  
778 705
See the METHODS section for complete documentation of every parameter.
779 706

  
......
791 718

  
792 719
=over
793 720

  
794
The main method of this class. 
795
Takes a PDF::API2 instance, a page instance, some data to build the table and formatting options. 
796
The formatting options should be passed as named parameters. 
721
The main method of this class.
722
Takes a PDF::API2 instance, a page instance, some data to build the table and formatting options.
723
The formatting options should be passed as named parameters.
797 724
This method will add more pages to the pdf instance as required based on the formatting options and the amount of data.
798 725

  
799 726
=back
800 727

  
801 728
=over
802 729

  
803
The return value is a 3 item list where 
730
The return value is a 3 item list where
804 731
The first item is the PDF::API2::Page instance that the table ends on,
805
The second item is the count of pages that the table spans, and 
732
The second item is the count of pages that the table spans, and
806 733
The third item is the y position of the table bottom.
807 734

  
808 735
=back
......
813 740

  
814 741
 ($end_page, $pages_spanned, $table_bot_y) = $pdftable->table(
815 742
     $pdf,               # A PDF::API2 instance
816
     $page_to_start_on,  # A PDF::API2::Page instance created with $page_to_start_on = $pdf->page(); 
743
     $page_to_start_on,  # A PDF::API2::Page instance created with $page_to_start_on = $pdf->page();
817 744
     $data,              # 2D arrayref of text strings
818 745
     x  => $left_edge_of_table,    #X - coordinate of upper left corner
819 746
     w  => 570, # width of table.
......
848 775

  
849 776
=item HEADER ROW PROPERTIES
850 777

  
851
If the 'header_props' parameter is used, it should be a hashref. 
778
If the 'header_props' parameter is used, it should be a hashref.
852 779
It is your choice if it will be anonymous inline hash or predefined one.
853 780
Also as you can see there is no data variable for the content because the module asumes that the first table row will become the header row. It will copy this row and put it on every new page if 'repeat' param is set.
854 781

  
855 782
=back
856 783

  
857
    $hdr_props = 
784
    $hdr_props =
858 785
    {
859 786
        # This param could be a pdf core font or user specified TTF.
860 787
        #  See PDF::API2 FONT METHODS for more information
......
869 796

  
870 797
=item COLUMN PROPERTIES
871 798

  
872
If the 'column_props' parameter is used, it should be an arrayref of hashrefs, 
873
with one hashref for each column of the table. The columns are counted from left to right so the hash reference at $col_props[0] will hold properties for the first column from left to right. 
799
If the 'column_props' parameter is used, it should be an arrayref of hashrefs,
800
with one hashref for each column of the table. The columns are counted from left to right so the hash reference at $col_props[0] will hold properties for the first column from left to right.
874 801
If you DO NOT want to give properties for a column but to give for another just insert and empty hash reference into the array for the column that you want to skip. This will cause the counting to proceed as expected and the properties to be applyed at the right columns.
875 802

  
876 803
Each hashref can contain any of the keys shown below:
......
892 819

  
893 820
=over
894 821

  
895
If the 'min_w' parameter is used for 'col_props', have in mind that it can be overwritten 
822
If the 'min_w' parameter is used for 'col_props', have in mind that it can be overwritten
896 823
by the calculated minimum cell witdh if the userdefined value is less that calculated.
897
This is done for safety reasons. 
898
In cases of a conflict between column formatting and odd/even row formatting, 
824
This is done for safety reasons.
825
In cases of a conflict between column formatting and odd/even row formatting,
899 826
the former will override the latter.
900 827

  
901 828
=back
......
929 856
      },
930 857
      # etc.
931 858
    ],
932
	# etc.
859
  # etc.
933 860
  ];
934 861

  
935 862
=over
......
943 870

  
944 871

  
945 872

  
946
=item TABLE SPANNING    
873
=item TABLE SPANNING
947 874

  
948 875
If used the parameter 'new_page_func' must be a function reference which when executed will create a new page and will return the object back to the module.
949 876
For example you can use it to put Page Title, Page Frame, Page Numbers and other staff that you need.
......
989 916
    lead     => $font_size | $distance_between_lines,
990 917
    align    => "left|right|center|justify|fulljustify",
991 918
    hang     => $optional_hanging_indent,
992
    Only one of the subsequent 3params can be given. 
919
    Only one of the subsequent 3params can be given.
993 920
    They override each other.-parspace is the weightest
994 921
    parspace => $optional_vertical_space_before_first_paragraph,
995 922
    flindent => $optional_indent_of_first_line,
......
1046 973
L<PDF::API2>
1047 974

  
1048 975
=cut
1049

  

Auch abrufbar als: Unified diff