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