File Coverage

blib/lib/Graphics/DZI/A4.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Graphics::DZI::A4;
2              
3 1     1   5121 use warnings;
  1         3  
  1         39  
4 1     1   6 use strict;
  1         1  
  1         34  
5              
6 1     1   437 use Moose;
  0            
  0            
7             extends 'Graphics::DZI::Files';
8              
9             our $log;
10             use Log::Log4perl;
11             BEGIN {
12             $log = Log::Log4perl->get_logger ();
13             }
14              
15             =head1 NAME
16              
17             Graphics::DZI::A4 - DeepZoom Image Pyramid Generation, specifically for documents
18              
19             =head1 SYNOPSIS
20              
21             use Graphics::DZI::A4;
22             $Graphics::DZI::log ->level ($loglevel);
23             $Graphics::DZI::A4::log->level ($loglevel);
24             my $dzi = new Graphics::DZI::A4 (A4s => \@images,
25             overlap => $overlap,
26             tilesize => $tilesize,
27             path => './',
28             prefix => 'xxx',
29             'format' => $format,
30             );
31             use File::Slurp;
32             write_file ('xxx.xml', $dzi->descriptor );
33             $dzi->iterate ();
34              
35             =head1 DESCRIPTION
36              
37             This subclass of L<Graphics::DZI::Files> is specifically though for images covering document
38             pages. While it is named C<A4>, this is mostly historical; as long as all your images have the same
39             dimensions, this package should.
40              
41             The idea is that the whole document (the set of images) forms a large image, the individual images
42             organized in a square fashion (1x1, 2x2, 4x4, ...). At the highest zoom level of course all pages
43             will be visible. But if you zoom out, then not only the pages get smaller. Also the pages shown will
44             be reduced, so that at the smallest zoom level only the first page is visible.
45              
46             =head1 INTERFACE
47              
48             =head2 Constructor
49              
50             Other than the superclass L<Graphics::DZI::Files> this class takes an array (reference) to a list of
51             images.
52              
53             =over
54              
55             =item C<A4s> (no default, list reference)
56              
57             Do not be fooled by the A4; any format should do.
58              
59             =back
60              
61             =cut
62              
63             use Moose::Util::TypeConstraints qw(enum);
64             enum 'packing' => qw( exponential linear );
65              
66             has '+image' => (isa => 'Image::Magick', required => 0);
67             has 'A4s' => (isa => 'ArrayRef', is => 'ro' );
68             has 'W' => (isa => 'Int' , is => 'rw');
69             has 'H' => (isa => 'Int' , is => 'rw');
70             has 'sqrt' => (isa => 'Num', is => 'rw');
71             has 'pack' => (isa => 'packing', is => 'rw', default => 'exponential');
72              
73             sub BUILD {
74             my $self = shift;
75             ($self->{W}, $self->{H}) = $self->A4s->[0]->GetAttributes ('width', 'height'); # single A4
76              
77             use feature "switch";
78             given ($self->{pack}) {
79             when ('linear') {
80             use POSIX;
81             $self->{ sqrt } = POSIX::ceil ( sqrt ( scalar @{$self->A4s}) ); # take the root + 1
82             }
83             when ('exponential') {
84             use POSIX;
85             my $log2 = POSIX::ceil (log (scalar @{$self->A4s}) / log (2)); # next fitting 2-potenz
86             $log2++ if $log2 % 2; # we can only use even ones
87             $self->{ sqrt } = ( 2**($log2/2) ); # how many along one edge when we organize them into a square?
88             }
89             default { die "unhandled packing"; }
90             }
91             $self->{ image } = _list2huge ($self->sqrt, $self->W, $self->H, @{ $self->A4s }) ;
92             }
93              
94             =head2 Methods
95              
96             =over
97              
98             =item B<iterate>
99              
100             This iterate honors the fact that we are dealing with a set of documents, not ONE large image.
101              
102             =cut
103              
104             sub _list2huge {
105             my $sqrt = shift;
106             my ($W, $H) = (shift, shift);
107              
108             my $dim = sprintf "%dx%d", map { $_ * $sqrt } ($W, $H);
109             $log->debug ("building composite document: DIM $dim ($sqrt)");
110             use Image::Magick;
111             my $huge = Image::Magick->new ($dim);
112             $huge->Read ('xc:white');
113             $huge->Transparent (color => 'white');
114              
115             foreach my $a (0 .. $sqrt*$sqrt - 1) {
116             my ($j, $i) = ( int( $a / $sqrt) , $a % $sqrt );
117             $log->debug (" index $a (x,y) = $i $j");
118              
119             $huge->Composite (image => $_[$a],
120             x => $i * $W,
121             'y' => $j * $H,
122             compose => 'Over',
123             );
124             }
125             # $huge->Display();
126             return $huge;
127             }
128              
129              
130             sub iterate {
131             my $self = shift;
132              
133             my $overlap_tilesize = $self->tilesize + 2 * $self->overlap;
134             my $border_tilesize = $self->tilesize + $self->overlap;
135              
136             my ($WIDTH, $HEIGHT) = $self->image->GetAttributes ('width', 'height');
137             $log->debug ("total dimension: $WIDTH, $HEIGHT");
138             use POSIX;
139             my $MAXLEVEL = POSIX::ceil (log ($WIDTH > $HEIGHT ? $WIDTH : $HEIGHT) / log (2));
140             $log->debug (" --> $MAXLEVEL");
141              
142             my ($width, $height) = ($WIDTH, $HEIGHT);
143             foreach my $level (reverse (0..$MAXLEVEL)) {
144              
145             my ($x, $col) = (0, 0);
146             while ($x < $width) {
147             my ($y, $row) = (0, 0);
148             my $tile_dx = $x == 0 ? $border_tilesize : $overlap_tilesize;
149             while ($y < $height) {
150              
151             my $tile_dy = $y == 0 ? $border_tilesize : $overlap_tilesize;
152              
153             my $tile = $self->crop (1, $x, $y, $tile_dx, $tile_dy); # scale is here always 1
154             $self->manifest ($tile, $level, $row, $col);
155              
156             $y += ($tile_dy - 2 * $self->overlap);
157             $row++;
158             }
159             $x += ($tile_dx - 2 * $self->overlap);
160             $col++;
161             }
162             ($width, $height) = map { int ($_ / 2) } ($width, $height); # half size, and remember this is A4!
163              
164             if ($self->{ sqrt } > 1) {
165             use feature "switch";
166             given ($self->{pack}) {
167             when ('linear') { $self->{ sqrt }--; } # in linear packing we simply reduce the square root by one
168             when ('exponential') { $self->{ sqrt } /= 2; }
169             default {}
170             }
171             $self->{ image } = _list2huge ($self->sqrt, # pack sqrt x sqrt A4s into one image
172             $self->W, $self->H,
173             @{ $self->A4s });
174             }
175             $self->image->Resize (width => $width, height => $height); # at higher levels we need to resize that properly
176             }
177             }
178              
179             =item B<descriptor>
180              
181             Also the descriptor generation is a bit special.
182              
183             =cut
184              
185             sub descriptor {
186             my $self = shift;
187             my $overlap = $self->overlap;
188             my $tilesize = $self->tilesize;
189             my $format = $self->format;
190             my ($width, $height) = map { $_ * $self->sqrt } ($self->W, $self->H);
191             return qq{<?xml version='1.0' encoding='UTF-8'?>
192             <Image TileSize='$tilesize'
193             Overlap='$overlap'
194             Format='$format'
195             xmlns='http://schemas.microsoft.com/deepzoom/2008'>
196             <Size Width='$width' Height='$height'/>
197             </Image>
198             };
199              
200              
201             }
202              
203             =back
204              
205             =head1 AUTHOR
206              
207             Robert Barta, C<< <drrho at cpan.org> >>
208              
209             =head1 COPYRIGHT & LICENSE
210              
211             Copyright 2010 Robert Barta, all rights reserved.
212              
213             This program is free software; you can redistribute it and/or modify it
214             under the same terms as Perl itself.
215              
216             =cut
217              
218             our $VERSION = '0.02';
219              
220             "against all odds";