File Coverage

/root/.cpan/build/Imager-1.018-0/blib/lib/Imager/Font.pm
Criterion Covered Total %
statement 58 170 34.1
branch 23 90 25.5
condition 10 38 26.3
subroutine 6 14 42.8
pod 9 9 100.0
total 106 321 33.0


line stmt bran cond sub pod time code
1             package Imager::Font;
2 58     58   914 use 5.006;
  58         161  
3 58     58   263 use Imager::Color;
  58         91  
  58         969  
4 58     58   234 use strict;
  58         79  
  58         98740  
5              
6             our $VERSION = "1.039";
7              
8             # the aim here is that we can:
9             # - add file based types in one place: here
10             # - make sure we only attempt to create types that exist
11             # - give reasonable defaults
12             # - give the user some control over which types get used
13             my %drivers =
14             (
15             tt=>{
16             class=>'Imager::Font::Truetype',
17             module=>'Imager/Font/Truetype.pm',
18             files=>'.*\.ttf$',
19             description => 'FreeType 1.x',
20             checktype => 1,
21             },
22             t1=>{
23             class=>'Imager::Font::T1',
24             module=>'Imager/Font/T1.pm',
25             files=>'.*\.pfb$',
26             description => 'T1Lib',
27             },
28             ft2=>{
29             class=>'Imager::Font::FT2',
30             module=>'Imager/Font/FT2.pm',
31             files=>'.*\.(pfa|pfb|otf|ttf|fon|fnt|dfont|pcf(\.gz)?)$',
32             description => 'FreeType 2.x',
33             },
34             ifs=>{
35             class=>'Imager::Font::Image',
36             module=>'Imager/Font/Image.pm',
37             files=>'.*\.ifs$',
38             },
39             w32=>{
40             class=>'Imager::Font::W32',
41             module=>'Imager/Font/W32.pm',
42             description => 'Win32 GDI Fonts',
43             },
44             );
45              
46             # this currently should only contain file based types, don't add w32
47             my @priority = qw(ft2 ifs);
48              
49             sub new {
50 2     2 1 20 my $class = shift;
51 2         4 my $self = {};
52 2         5 my ($file, $type, $id);
53 2         14 my %hsh=(color => Imager::Color->new(255,0,0,255),
54             size => 15,
55             @_);
56              
57 2         7 bless $self,$class;
58              
59 2 50       7 if ($hsh{'file'}) {
    0          
60 2         4 $file = $hsh{'file'};
61              
62 2         4 $type = $hsh{'type'};
63 2 50       7 if (defined $type) {
64 0 0       0 unless ($drivers{$type}) {
65 0         0 Imager->_set_error("Unknown font type $type");
66 0         0 return;
67             }
68              
69 0 0       0 unless ($Imager::formats{$type}) {
70 0         0 Imager->_set_error("The $type {$drivers{$type}) font driver is not installed");
71 0         0 return;
72             }
73             }
74             else {
75 2         5 for my $drv (@priority) {
76 4         9 undef $type;
77 4 50       14 my $re = $drivers{$drv}{files} or next;
78 4 100       64 if ($file =~ /$re/i) {
79 3 100 33     23 if (eval { require $drivers{$drv}{module}; 1 } and !( $drivers{$drv}{checktype} && !$Imager::formats{$drv} )) {
  3   66     649  
  1         133  
80 1         4 $type = $drv;
81 1         2 last;
82             }
83             }
84             }
85             }
86 2 100       21 if (!defined($type)) {
87             # some types we can support, but the driver isn't available
88             # work out which drivers support it, so we can provide the user
89             # some useful information on how to get it working
90 1         2 my @not_here;
91 1         4 for my $driver_name (keys %drivers) {
92 5         8 my $driver = $drivers{$driver_name};
93             push @not_here, "$driver_name ($driver->{description})"
94 5 100 100     71 if $driver->{files} && $file =~ /$driver->{files}/i;
95             }
96 1 50       4 if (@not_here) {
97 1         5 $Imager::ERRSTR = "No font drivers enabled that can support this file, rebuild Imager with any of ".join(", ", @not_here)." to use this font file";
98             }
99             else {
100 0         0 $Imager::ERRSTR = "No font type found for $hsh{'file'}";
101             }
102 1         13 return;
103             }
104             } elsif ($hsh{face}) {
105 0         0 $type = "w32";
106             } else {
107 0         0 $Imager::ERRSTR="No font file specified";
108 0         0 return;
109             }
110              
111 1 50 33     5 if ($drivers{$type}{checktype} && !$Imager::formats{$type}) {
112 0         0 $Imager::ERRSTR = "`$type' not enabled";
113 0         0 return;
114             }
115              
116             # here we should have the font type or be dead already.
117              
118 1         5 require $drivers{$type}{module};
119 1         8 return $drivers{$type}{class}->new(%hsh);
120             }
121              
122             # returns first defined parameter
123             sub _first {
124 0     0   0 for (@_) {
125 0 0       0 return $_ if defined $_;
126             }
127 0         0 return undef;
128             }
129              
130             sub draw {
131 0     0 1 0 my $self = shift;
132 0         0 my %input = ('x' => 0, 'y' => 0, @_);
133 0 0       0 unless ($input{image}) {
134 0         0 $Imager::ERRSTR = 'No image supplied to $font->draw()';
135 0         0 return;
136             }
137 0         0 my $image = $input{image};
138 0         0 $input{string} = _first($input{string}, $input{text});
139 0 0       0 unless (defined $input{string}) {
140 0         0 $image->_set_error("Missing required parameter 'string'");
141 0         0 return;
142             }
143 0         0 $input{aa} = _first($input{aa}, $input{antialias}, $self->{aa}, 1);
144             # the original draw code worked this out but didn't use it
145 0         0 $input{align} = _first($input{align}, $self->{align});
146 0         0 $input{color} = _first($input{color}, $self->{color});
147 0         0 $input{color} = Imager::_color($input{'color'});
148              
149 0         0 $input{size} = _first($input{size}, $self->{size});
150 0 0       0 unless (defined $input{size}) {
151 0         0 $image->_set_error("No font size provided");
152 0         0 return undef;
153             }
154 0         0 $input{align} = _first($input{align}, 1);
155 0         0 $input{utf8} = _first($input{utf8}, $self->{utf8}, 0);
156 0         0 $input{vlayout} = _first($input{vlayout}, $self->{vlayout}, 0);
157              
158 0         0 my $result = $self->_draw(%input);
159 0 0       0 unless ($result) {
160 0         0 $image->_set_error($image->_error_as_msg());
161             }
162              
163 0         0 return $result;
164             }
165              
166             sub align {
167 0     0 1 0 my $self = shift;
168 0         0 my %input = ( halign => 'left', valign => 'baseline',
169             'x' => 0, 'y' => 0, @_ );
170              
171             # image needs to be supplied, but can be supplied as undef
172 0 0       0 unless (exists $input{image}) {
173 0         0 Imager->_set_error("Missing required parameter 'image'");
174 0         0 return;
175             }
176              
177 0   0     0 my $errors_to = $input{image} || 'Imager';
178              
179 0         0 my $text = _first($input{string}, $input{text});
180 0 0       0 unless (defined $text) {
181 0         0 $errors_to->_set_error("Missing required parameter 'string'");
182 0         0 return;
183             }
184              
185 0         0 my $size = _first($input{size}, $self->{size});
186 0         0 my $utf8 = _first($input{utf8}, 0);
187              
188 0         0 my $bbox = $self->bounding_box(string=>$text, size=>$size, utf8=>$utf8);
189 0         0 my $valign = $input{valign};
190 0 0 0     0 $valign = 'baseline'
191             unless $valign && $valign =~ /^(?:top|center|bottom|baseline)$/;
192              
193 0         0 my $halign = $input{halign};
194 0 0 0     0 $halign = 'start'
195             unless $halign && $halign =~ /^(?:left|start|center|end|right)$/;
196              
197 0         0 my $x = $input{'x'};
198 0         0 my $y = $input{'y'};
199              
200 0 0       0 if ($valign eq 'top') {
    0          
    0          
201 0         0 $y += $bbox->ascent;
202             }
203             elsif ($valign eq 'center') {
204 0         0 $y += $bbox->ascent - $bbox->text_height / 2;
205             }
206             elsif ($valign eq 'bottom') {
207 0         0 $y += $bbox->descent;
208             }
209             # else baseline is the default
210              
211 0 0       0 if ($halign eq 'left') {
    0          
    0          
    0          
    0          
212 0         0 $x -= $bbox->start_offset;
213             }
214             elsif ($halign eq 'start') {
215             # nothing to do
216             }
217             elsif ($halign eq 'center') {
218 0         0 $x -= $bbox->start_offset + $bbox->total_width / 2;
219             }
220             elsif ($halign eq 'end') {
221 0         0 $x -= $bbox->advance_width;
222             }
223             elsif ($halign eq 'right') {
224 0         0 $x -= $bbox->advance_width - $bbox->right_bearing;
225             }
226 0         0 $x = int($x);
227 0         0 $y = int($y);
228              
229 0 0       0 if ($input{image}) {
230 0         0 delete @input{qw/x y/};
231 0 0       0 $self->draw(%input, 'x' => $x, 'y' => $y, align=>1)
232             or return;
233             }
234              
235 0         0 return ($x+$bbox->start_offset, $y-$bbox->ascent,
236             $x+$bbox->end_offset, $y-$bbox->descent+1);
237             }
238              
239             sub bounding_box {
240 0     0 1 0 my $self=shift;
241 0         0 my %input=@_;
242              
243 0 0       0 if (!exists $input{'string'}) {
244 0         0 $Imager::ERRSTR='string parameter missing';
245 0         0 return;
246             }
247 0   0     0 $input{size} ||= $self->{size};
248 0         0 $input{sizew} = _first($input{sizew}, $self->{sizew}, 0);
249 0         0 $input{utf8} = _first($input{utf8}, $self->{utf8}, 0);
250              
251 0 0       0 my @box = $self->_bounding_box(%input)
252             or return;
253              
254 0 0       0 if (wantarray) {
255 0 0 0     0 if(@box && exists $input{'x'} and exists $input{'y'}) {
    0 0        
      0        
256 0         0 my($gdescent, $gascent)=@box[1,3];
257 0         0 $box[1]=$input{'y'}-$gascent; # top = base - ascent (Y is down)
258 0         0 $box[3]=$input{'y'}-$gdescent; # bottom = base - descent (Y is down, descent is negative)
259 0         0 $box[0]+=$input{'x'};
260 0         0 $box[2]+=$input{'x'};
261             } elsif (@box && $input{'canon'}) {
262 0         0 $box[3]-=$box[1]; # make it canonical (ie (0,0) - (width, height))
263 0         0 $box[2]-=$box[0];
264             }
265 0         0 return @box;
266             }
267             else {
268 0         0 require Imager::Font::BBox;
269              
270 0         0 return Imager::Font::BBox->new(@box);
271             }
272             }
273              
274             sub dpi {
275 0     0 1 0 my $self = shift;
276              
277             # I'm assuming a default of 72 dpi
278 0         0 my @old = (72, 72);
279 0 0       0 if (@_) {
280 0         0 $Imager::ERRSTR = "Setting dpi not implemented for this font type";
281 0         0 return;
282             }
283              
284 0         0 return @old;
285             }
286              
287             sub transform {
288 0     0 1 0 my $self = shift;
289              
290 0         0 my %hsh = @_;
291              
292             # this is split into transform() and _transform() so we can
293             # implement other tags like: degrees=>12, which would build a
294             # 12 degree rotation matrix
295             # but I'll do that later
296 0 0       0 unless ($hsh{matrix}) {
297 0         0 $Imager::ERRSTR = "You need to supply a matrix";
298 0         0 return;
299             }
300              
301 0         0 return $self->_transform(%hsh);
302             }
303              
304             sub _transform {
305 0     0   0 $Imager::ERRSTR = "This type of font cannot be transformed";
306 0         0 return;
307             }
308              
309             sub utf8 {
310 0     0 1 0 return 0;
311             }
312              
313             sub priorities {
314 1     1 1 2 my $self = shift;
315 1         3 my @old = @priority;
316              
317 1 50       4 if (@_) {
318 1         3 @priority = @_;
319             }
320 1         3 return @old;
321             }
322              
323             sub register {
324 5     5 1 88 my ($self, %opts) = @_;
325              
326 5         10 my $type = delete $opts{type};
327 5         8 my $class = delete $opts{class};
328 5         8 my $files = delete $opts{files};
329 5   66     20 my $description = delete $opts{description} || $class;
330              
331 5 100       14 defined $type
332             or return Imager->_set_error("No type parameter supplied to Imager::Font->regster");
333              
334 4 100       8 defined $class
335             or return Imager->_set_error("No class parameter supplied to Imager::Font->register");
336              
337 3 50       8 if ($files) {
338 3 100       3 eval { qr/$files/ }
  3         32  
339             or return Imager->_set_error("files isn't a valid regexp");
340             }
341              
342 2 50 33     10 if ($drivers{$type} && $drivers{$type}{class} ne $class) {
343 0         0 Imager->_set_error("Font type $type already registered as $drivers{$type}{class}");
344 0         0 return;
345             }
346              
347 2         6 (my $module = $class . ".pm") =~ s(::)(/)g;
348              
349 2         6 my $driver =
350             {
351             class => $class,
352             module => $module,
353             description => $description,
354             };
355 2 50       5 $files and $driver->{files} = $files;
356              
357 2         4 $drivers{$type} = $driver;
358              
359 2         9 1;
360             }
361              
362             1;
363              
364             __END__