1# PDFJ/Matrix.pm - PDFJ::Block::Matrix 2# 2005 <nakajima@netstock.co.jp> 3# automatically required by Block() 4 5#-------------------------------------------------------------------------- 6package PDFJ::Block::Matrix; 7use Carp; 8use strict; 9use vars qw(@ISA); 10@ISA = qw(PDFJ::Block); 11 12sub _checkobjects { 13 my($self) = @_; 14 my $objects = $self->{objects}; 15 if( ref($objects) ne 'ARRAY' ) { 16 $self->{objects} = [[$objects]]; 17 $objects = $self->{objects}; 18 } elsif( !grep {ref($_) eq 'ARRAY'} @$objects ) { 19 $self->{objects} = [$objects]; 20 $objects = $self->{objects}; 21 } 22 for( my $j = 0; $j < @$objects; $j++ ) { 23 my $obj = $objects->[$j]; 24 croak "Matrix objects must be array of array: $obj" 25 unless ref($obj) eq 'ARRAY'; 26 for( my $k = 0; $k < @$obj; $k++ ) { 27 if( PDFJ::Util::objisa($obj->[$k], 'PDFJ::BlockElement') ) { 28 # OK 29 } elsif( $obj->[$k] =~ /^\d+$/ ) { 30 $obj->[$k] = PDFJ::BlockSkip::BlockSkip($obj->[$k]); 31 } else { 32 croak "illegal Matrix element: $obj->[$k]" 33 } 34 } 35 } 36 #print "_checkobjects OK\n"; 37} 38 39sub _calcsize { 40 my($self) = @_; 41 $self->_calccol; 42 $self->_calcrow; 43} 44 45sub _contcell { my $tmp; bless \$tmp, 'PDFJ::Matrix::ContCell'; } 46sub _iscontcell { ref($_[0]) eq 'PDFJ::Matrix::ContCell'; } 47 48sub _trimobjects { 49 my($self) = @_; 50 return if $self->{objectstrimmed}; 51 $self->{postnobreak} = []; 52 my $matrix = $self->{objects}; 53 my $trimed; 54 my $maxrows = 0; 55 for( my $j = 0; $j < @$matrix; $j++ ) { 56 my $row = $matrix->[$j]; 57 my $trow = []; 58 push @$trimed, $trow; 59 for( my $k = 0; $k < @$row; $k++ ) { 60 my $cell = $row->[$k]; 61 push @$trow, $cell; 62 my $colspan = $cell->colspan || 1; 63 while( $colspan-- > 1 ) { 64 push @$trow, _contcell; 65 } 66 if( $k == $#$row && $cell->postnobreak ) { 67 $self->{postnobreak}[$j] = 1; 68 } 69 } 70 $maxrows = @$trow if $maxrows < @$trow; 71 } 72 for( my $k = 0; $k < $maxrows; $k++ ) { 73 for( my $j = 0; $j < @$trimed; $j++ ) { 74 my $cell = $trimed->[$j][$k]; 75 if( $cell && !_iscontcell($cell) ) { 76 my $rowspan = $cell->rowspan || 1; 77 for( my $r = 1; $r < $rowspan; $r++ ) { 78 if( _iscontcell($trimed->[$j + $r][$k]) ) { 79 push @{$self->{collisioncells}}, [$j + $r, $k]; 80 } else { 81 if( $k < @{$trimed->[$j + $r]} ) { 82 splice @{$trimed->[$j + $r]}, $k, 0, _contcell; 83 } else { 84 $trimed->[$j + $r][$k] = _contcell; 85 } 86 } 87 } 88 for( my $r = 0; $r < $rowspan - 1; $r++ ) { 89 $self->{postnobreak}[$j + $r] = 1; 90 } 91 } 92 } 93 } 94 for( my $j = 0; $j < @$trimed; $j++ ) { 95 my $row = $trimed->[$j]; 96 for( my $k = 0; $k < @$row; $k++ ) { 97#print "[$j, $k] ", $row->[$k], "\n"; 98 push @{$self->{emptycells}}, [$j, $k] unless $row->[$k]; 99 } 100 } 101 $self->{objectstrimmed} = 1; 102 $self->{objects} = $trimed; 103} 104 105sub _widths2width { 106 my($self) = @_; 107 my $widths = $self->{widths}; 108 $self->{width} = 0; 109 grep {$self->{width} += $_} @$widths; 110 if( @$widths > 1 ) { 111 my $skip = $self->{direction} =~ /V$/ ? 112 $self->{style}{colskip} : $self->{style}{rowskip}; 113 $self->{width} += ($skip || 0) * (@$widths - 1) 114 } 115} 116 117sub _heights2height { 118 my($self) = @_; 119 my $heights = $self->{heights}; 120 $self->{height} = 0; 121 grep {$self->{height} += $_} @$heights; 122 if( @$heights > 1 ) { 123 my $skip = $self->{direction} =~ /^V/ ? 124 $self->{style}{colskip} : $self->{style}{rowskip}; 125 $self->{height} += ($skip || 0) * (@$heights - 1) 126 } 127} 128 129sub _calccol { 130 my($self) = @_; 131 return unless @{$self->{objects}}; 132 $self->_trimobjects; 133 my $matrix = $self->{objects}; 134 my $adjust = $self->{style}{adjust}; 135 my $direction = $self->{direction}; 136#print "_calccol\n"; 137 my $maxrows = 0; 138 for( my $j = 0; $j < @$matrix; $j++ ) { 139 my $row = $matrix->[$j]; 140 $maxrows = @$row if $maxrows < @$row; 141 } 142 my @maxsize; 143 my $k = 0; 144 my @row; 145 for( my $j = 0; $j < @$matrix; $j++ ) { 146 push @row, $matrix->[$j][$k]; 147 } 148 while(@row) { 149#print "----------\n"; 150 my $maxsize = 0; 151 my @left; 152 my $one = 0; 153 for my $cell( @row ) { 154 next if !$cell || _iscontcell($cell); 155 my($span, $size); 156 if( ref($cell) eq 'ARRAY' ) { 157 ($span, $size) = @$cell; 158 } else { 159 $span = $cell->colspan || 1; 160 $size = $direction =~ /V$/ ? $cell->width : $cell->height; 161 } 162#print " [$span, $size]\n"; 163 if( $span > 1 ) { 164 push @left, [$span, $size]; 165 } else { 166 $maxsize = $size if $maxsize < $size; 167 $one = 1; 168 } 169 } 170 @row = (); 171 for my $cell( @left ) { 172 my($span, $size) = @$cell; 173 push @row, [$span - 1, $size - $maxsize]; 174 } 175 push @maxsize, $maxsize; 176 if( $one ) { 177 $k++; 178 if( $k < $maxrows ) { 179 for( my $j = 0; $j < @$matrix; $j++ ) { 180 push @row, $matrix->[$j][$k]; 181 } 182 } 183 } 184 } 185 if( $direction =~ /V$/ ) { 186#print "widths: @maxsize\n"; 187 $self->{widths} = \@maxsize; 188 $self->_widths2width; 189 } else { 190#print "heights: @maxsize\n"; 191 $self->{heights} = \@maxsize; 192 $self->_heights2height; 193 } 194 if( $adjust ) { 195 my $skip = $self->{style}{colskip} || 0; 196 for( my $j = 0; $j < @$matrix; $j++ ) { 197 my $row = $matrix->[$j]; 198 for( my $k = 0; $k < @$row; $k++ ) { 199 my $cell = $row->[$k]; 200 next if !$cell || _iscontcell($cell); 201 my $span = $cell->colspan || 1; 202 my $size = 0; 203 for( my $m = 0; $m < $span; $m++ ) { 204 $size += $skip if $m > 0; 205 $size += $maxsize[$k + $m]; 206 } 207 if( $direction =~ /V$/ ) { 208 if( UNIVERSAL::can($cell, 'adjustwidth') ) { 209#print "adjust [$j, $k] width to $size\n"; 210 $cell->adjustwidth($size); 211 } 212 } else { 213 if( UNIVERSAL::can($cell, 'adjustheight') ) { 214#print "adjust [$j, $k] height to $size\n"; 215 $cell->adjustheight($size); 216 } 217 } 218 } 219 } 220 } 221} 222 223sub _calcrow { 224 my($self) = @_; 225 return unless @{$self->{objects}}; 226 $self->_trimobjects; 227 my $matrix = $self->{objects}; 228 my $adjust = $self->{style}{adjust}; 229 my $direction = $self->{direction}; 230#print "_calcrow\n"; 231 my @maxsize; 232 my $j = 0; 233 my @row = @{$matrix->[$j]}; 234 while(@row) { 235#print "----------\n"; 236 my $maxsize = 0; 237 my @left; 238 my $one = 0; 239 for my $cell( @row ) { 240 next if !$cell || _iscontcell($cell); 241 my($span, $size); 242 if( ref($cell) eq 'ARRAY' ) { 243 ($span, $size) = @$cell; 244 } else { 245 $span = $cell->rowspan || 1; 246 $size = $direction =~ /^V/ ? $cell->width : $cell->height; 247 } 248#print " [$span, $size]\n"; 249 if( $span > 1 ) { 250 push @left, [$span, $size]; 251 } else { 252 $maxsize = $size if $maxsize < $size; 253 $one = 1; 254 } 255 } 256 @row = (); 257 for my $cell( @left ) { 258 my($span, $size) = @$cell; 259 push @row, [$span - 1, $size - $maxsize]; 260 } 261 push @maxsize, $maxsize; 262 if( $one ) { 263 $j++; 264 if( $j < @$matrix ) { 265 push @row, @{$matrix->[$j]}; 266 } 267 } 268 } 269 if( $direction =~ /^V/ ) { 270#print "widths: @maxsize\n"; 271 $self->{widths} = \@maxsize; 272 $self->_widths2width; 273 } else { 274#print "heights: @maxsize\n"; 275 $self->{heights} = \@maxsize; 276 $self->_heights2height; 277 } 278 if( $adjust ) { 279 my $skip = $self->{style}{rowskip}; 280 for( my $j = 0; $j < @$matrix; $j++ ) { 281 my $row = $matrix->[$j]; 282 for( my $k = 0; $k < @$row; $k++ ) { 283 my $cell = $row->[$k]; 284 next if !$cell || _iscontcell($cell); 285 my $span = $cell->rowspan || 1; 286 my $size = 0; 287 for( my $m = 0; $m < $span; $m++ ) { 288 $size += $skip if $m > 0; 289 $size += $maxsize[$j + $m]; 290 } 291 if( $direction =~ /^V/ ) { 292 if( UNIVERSAL::can($cell, 'adjustwidth') ) { 293#print "adjust [$j, $k] width to $size\n"; 294 $cell->adjustwidth($size); 295 } 296 } else { 297 if( UNIVERSAL::can($cell, 'adjustheight') ) { 298#print "adjust [$j, $k] height to $size\n"; 299 $cell->adjustheight($size); 300 } 301 } 302 } 303 } 304 } 305} 306 307sub widths { $_[0]->{widths} } 308sub heights { $_[0]->{heights} } 309 310sub breakable { 311 my($self, $blockdirection) = @_; 312 $self->nobreak ? 0 : 1; 313} 314 315sub _show { 316 my($self, $page, $x, $y) = @_; 317 my $direction = $self->{direction}; 318 my $colskip = $self->{style}{colskip} || 0; 319 my $rowskip = $self->{style}{rowskip} || 0; 320 $self->_showbox($page, $x, $y); 321 $x += $self->padding + $self->{xpreshift}; 322 $y -= $self->padding + $self->{ypreshift}; 323 my($dx, $showalign) = (1, 'tl'); 324 if( $direction =~ /R/ ) { 325 $x += $self->{width}; 326 ($dx, $showalign) = (-1, 'tr'); 327 } 328 my($ox, $oy) = ($x, $y); 329 my $matrix = $self->{objects}; 330 if( $direction =~ /V$/ ) { 331 for( $y = $oy, my $j = 0; 332 $j < @$matrix; 333 $y -= $self->{heights}[$j] + $rowskip, $j++ ) { 334 my $row = $matrix->[$j]; 335 for( $x = $ox, my $k = 0; 336 $k < @$row; 337 $x += $dx * ($self->{widths}[$k] + $colskip), $k++ ) { 338 my $cell = $row->[$k]; 339 if( PDFJ::Util::objisa($cell, 'PDFJ::Showable') ) { 340 $cell->show($page, $x, $y, $showalign); 341 } 342 } 343 } 344 } else { # $direction =~ /^V/ 345 for( $x = $ox, my $j = 0; 346 $j < @$matrix; 347 $x += $dx * ($self->{widths}[$j] + $rowskip), $j++ ) { 348 my $row = $matrix->[$j]; 349 for( $y = $oy, my $k = 0; 350 $k < @$row; 351 $y -= $self->{heights}[$k] + $colskip, $k++ ) { 352 my $cell = $row->[$k]; 353 if( PDFJ::Util::objisa($cell, 'PDFJ::Showable') ) { 354 $cell->show($page, $x, $y, $showalign); 355 } 356 } 357 } 358 } 359} 360 361sub break { 362 my($self, $sizes) 363 = PDFJ::Util::listargs2ref(1, 0, 364 PDFJ::Util::methodargs([qw(sizes)], @_)); 365 my @sizes = @$sizes; 366 my $unbreakable = $self->nobreak; 367 my $repeatheader = $self->repeatheader; 368 my $lastsize = $sizes[$#sizes]; 369 my $skip = $self->{direction} =~ /^V/ ? 370 $self->{style}{colskip} : $self->{style}{rowskip}; 371 my $direction = $self->{direction} =~ /^V/ ? 'H' : 'V'; 372 my @result; 373 my @objects = $direction eq 'H' ? 374 map {[$self->{objects}[$_], $self->{widths}[$_], 375 $self->{postnobreak}[$_]]} (0..$#{$self->{objects}}) : 376 map {[$self->{objects}[$_], $self->{heights}[$_], 377 $self->{postnobreak}[$_]]} (0..$#{$self->{objects}}); 378 my @repeatheader = $repeatheader ? 379 @objects[0..($repeatheader - 1)] : (); 380 while( @objects ) { 381 my $size = @sizes ? shift(@sizes) : $lastsize; 382 my(@bobjects); 383 if( $unbreakable ) { 384 @bobjects = splice @objects if $size >= $self->size($direction); 385 } else { 386 my $bsize = $self->padding * 2; 387 while( $bsize < $size && @objects ) { 388 my $osize = $objects[0][1]; 389 my $skipsize = @bobjects ? $skip : 0; 390 if( $bsize + $skipsize + $osize <= $size ) { 391 push @bobjects, shift(@objects); 392 $bsize += $skipsize + $osize; 393 } else { 394 last; 395 } 396 } 397 } 398 while( @bobjects && $bobjects[$#bobjects][2] && @objects ) { 399 unshift @objects, pop(@bobjects); 400 } 401 if( !@bobjects && !@sizes ) { 402 carp "break fails"; 403 return; 404 } 405 if( $repeatheader && (@bobjects >= $repeatheader) && @objects ) { 406 unshift @objects, @repeatheader; 407 } 408 my $bobj; 409 %$bobj = %$self; 410 bless $bobj, ref($self); 411 $bobj->{objects} = [map {$bobjects[$_][0]} (0..$#bobjects)]; 412 $bobj->{postnobreak} = [map {$bobjects[$_][2]} (0..$#bobjects)]; 413 if( $direction eq 'H' ) { 414 $bobj->{widths} = [map {$bobjects[$_][1]} (0..$#bobjects)]; 415 $bobj->_widths2width; 416 } else { 417 $bobj->{heights} = [map {$bobjects[$_][1]} (0..$#bobjects)]; 418 $bobj->_heights2height; 419 } 420 push @result, $bobj; 421 } 422 @result; 423} 424 4251; 426