File Coverage

blib/lib/Imager/Font/Wrap.pm
Criterion Covered Total %
statement 11 107 10.2
branch 0 54 0.0
condition 0 16 0.0
subroutine 4 6 66.6
pod 1 1 100.0
total 16 184 8.7


line stmt bran cond sub pod time code
1             package Imager::Font::Wrap;
2 2     2   1395 use 5.006;
  2         6  
3 2     2   9 use strict;
  2         3  
  2         33  
4 2     2   10 use Imager;
  2         5  
  2         9  
5 2     2   13 use Imager::Font;
  2         7  
  2         2134  
6              
7             our $VERSION = "1.005";
8              
9             *_first = \&Imager::Font::_first;
10              
11             # we can't accept the utf8 parameter, too hard at this level
12              
13             # the %state contains:
14             # font - the font
15             # im - the image
16             # x - the left position
17             # w - the width
18             # justify - fill, left, right or center
19              
20             sub _format_line {
21 0     0     my ($state, $spaces, $text, $fill) = @_;
22              
23 0           $text =~ s/ +$//;
24             my $box = $state->{font}->bounding_box(string=>$text,
25 0           size=>$state->{size});
26              
27 0           my $y = $state->{linepos} + $box->global_ascent;
28            
29 0 0 0       if ($state->{bottom}
30             && $state->{linepos} + $box->font_height > $state->{bottom}) {
31 0           $state->{full} = 1;
32 0           return 0;
33             }
34              
35 0 0 0       if ($text =~ /\S/ && $state->{im}) {
36             my $justify = $fill ? $state->{justify} :
37 0 0         $state->{justify} eq 'fill' ? 'left' : $state->{justify};
    0          
38 0 0         if ($justify ne 'fill') {
39 0           my $x = $state->{x};
40 0 0         if ($justify eq 'right') {
    0          
41 0           $x += $state->{w} - $box->advance_width;
42             }
43             elsif ($justify eq 'center') {
44 0           $x += ($state->{w} - $box->advance_width) / 2;
45             }
46             $state->{font}->draw(image=>$state->{im}, string=>$text,
47             x=>$x, 'y'=>$y,
48 0           size=>$state->{size}, %{$state->{input}});
  0            
49             }
50             else {
51 0           (my $nospaces = $text) =~ tr/ //d;
52             my $nospace_bbox = $state->{font}->bounding_box(string=>$nospaces,
53 0           size=>$state->{size});
54 0           my $gap = $state->{w} - $nospace_bbox->advance_width;
55 0           my $x = $state->{x};
56 0           $spaces = $text =~ tr/ / /;
57 0           while (length $text) {
58 0 0         if ($text =~ s/^(\S+)//) {
    0          
59 0           my $word = $1;
60             my $bbox = $state->{font}->bounding_box(string=>$word,
61 0           size=>$state->{size});
62             $state->{font}->draw(image=>$state->{im}, string=>$1,
63             x=>$x, 'y'=>$y,
64 0           size=>$state->{size}, %{$state->{input}});
  0            
65 0           $x += $bbox->advance_width;
66             }
67             elsif ($text =~ s/^( +)//) {
68 0           my $sep = $1;
69 0           my $advance = int($gap * length($sep) / $spaces);
70 0           $spaces -= length $sep;
71 0           $gap -= $advance;
72 0           $x += $advance;
73             }
74             else {
75 0           die "This shouldn't happen\n";
76             }
77             }
78             }
79             }
80 0           $state->{linepos} += $box->font_height + $state->{linegap};
81              
82 0           1;
83             }
84              
85             sub wrap_text {
86 0     0 1   my $class = shift;
87 0           my %input = @_;
88              
89             # try to get something useful
90 0           my $x = _first(delete $input{'x'}, 0);
91 0           my $y = _first(delete $input{'y'}, 0);
92 0           my $im = delete $input{image};
93 0   0       my $imerr = $im || 'Imager';
94 0           my $width = delete $input{width};
95 0 0         if (!defined $width) {
96 0 0 0       defined $im && $im->getwidth > $x
97             or return $imerr->_set_error("No width supplied and can't guess");
98 0           $width = $im->getwidth - $x;
99             }
100             my $font = delete $input{font}
101 0 0         or return $imerr->_set_error("No font parameter supplied");
102 0           my $size = _first(delete $input{size}, $font->{size});
103 0 0         defined $size
104             or return $imerr->_set_error("No font size supplied");
105              
106 0 0         2 * $size < $width
107             or return $imerr->_set_error("Width too small for font size");
108            
109 0           my $text = delete $input{string};
110 0 0         defined $text
111             or return $imerr->_set_error("No string parameter supplied");
112              
113 0           my $justify = _first($input{justify}, "left");
114              
115             my %state =
116             (
117             font => $font,
118             im => $im,
119             x => $x,
120             w => $width,
121             justify => $justify,
122             'y' => $y,
123             linepos=>$y,
124             size=>$size,
125             input => \%input,
126 0   0       linegap => delete $input{linegap} || 0,
127             );
128 0           $state{height} = delete $input{height};
129 0 0         if ($state{height}) {
130 0           $state{bottom} = $y + $state{height};
131             }
132 0           my $line = '';
133 0           my $spaces = 0;
134 0           my $charpos = 0;
135 0           my $linepos = 0;
136 0           pos($text) = 0; # avoid a warning
137 0           while (pos($text) < length($text)) {
138             #print pos($text), "\n";
139 0 0         if ($text =~ /\G( +)/gc) {
    0          
    0          
    0          
140             #print "spaces\n";
141 0           $line .= $1;
142 0           $spaces += length($1);
143             }
144             elsif ($text =~ /\G(?:\x0D\x0A?|\x0A\x0D?)/gc) {
145             #print "newline\n";
146 0 0         _format_line(\%state, $spaces, $line, 0)
147             or last;
148 0           $line = '';
149 0           $spaces = 0;
150 0           $linepos = pos($text);
151             }
152             elsif ($text =~ /\G(\S+)/gc) {
153             #print "word\n";
154 0           my $word = $1;
155 0           my $bbox = $font->bounding_box(string=>$line . $word, size=>$size);
156 0 0         if ($bbox->advance_width > $width) {
157 0 0         _format_line(\%state, $spaces, $line, 1)
158             or last;
159 0           $line = '';
160 0           $spaces = 0;
161 0           $linepos = pos($text) - length($word);
162             }
163 0           $line .= $word;
164             # check for long words
165 0           $bbox = $font->bounding_box(string=>$line, size=>$size);
166 0           while ($bbox->advance_width > $width) {
167 0           my $len = length($line) - 1;
168 0           $bbox = $font->bounding_box(string=>substr($line, 0, $len),
169             size=>$size);
170 0           while ($bbox->advance_width > $width) {
171 0           --$len;
172 0           $bbox = $font->bounding_box(string=>substr($line, 0, $len),
173             size=>$size);
174             }
175 0 0         _format_line(\%state, 0, substr($line, 0, $len), 0)
176             or last;
177 0           $line = substr($line, $len);
178 0           $bbox = $font->bounding_box(string=>$line, size=>$size);
179 0           $linepos = pos($text) - length($line);
180             }
181             }
182             elsif ($text =~ /\G\s/gc) {
183             # skip a single unrecognized whitespace char
184             #print "skip\n";
185 0           $linepos = pos($text);
186             }
187             }
188              
189 0 0 0       if (length $line && !$state{full}) {
190 0 0         $linepos += length $line
191             if _format_line(\%state, 0, $line, 0);
192             }
193              
194 0 0         if ($input{savepos}) {
195 0           ${$input{savepos}} = $linepos;
  0            
196             }
197              
198 0           return ($x, $y, $x+$width, $state{linepos});
199             }
200              
201             1;
202              
203             __END__