eliminate use vars
[imager.git] / FT2 / FT2.pm
1 package Imager::Font::FT2;
2 use 5.006;
3 use strict;
4 use Imager;
5 use Scalar::Util ();
6 our @ISA = qw(Imager::Font);
7
8 our $VERSION;
9
10 BEGIN {
11   $VERSION = "0.98";
12
13   require XSLoader;
14   XSLoader::load('Imager::Font::FT2', $VERSION);
15 }
16
17 *_first = \&Imager::Font::_first;
18
19 sub new {
20   my $class = shift;
21   my %hsh=(color=>Imager::Color->new(255,0,0,255),
22            size=>15,
23            @_);
24
25   unless ($hsh{file}) {
26     $Imager::ERRSTR = "No font file specified";
27     return;
28   }
29   unless (-e $hsh{file}) {
30     $Imager::ERRSTR = "Font file $hsh{file} not found";
31     return;
32   }
33   unless ($Imager::formats{ft2}) {
34     $Imager::ERRSTR = "Freetype2 not supported in this build";
35     return;
36   }
37   my $id = i_ft2_new($hsh{file}, $hsh{'index'} || 0);
38   unless ($id) { # the low-level code may miss some error handling
39     $Imager::ERRSTR = Imager::_error_as_msg();
40     return;
41   }
42   return bless {
43                 id       => $id,
44                 aa       => $hsh{aa} || 0,
45                 file     => $hsh{file},
46                 type     => 't1',
47                 size     => $hsh{size},
48                 color    => $hsh{color},
49                 utf8     => $hsh{utf8},
50                 vlayout  => $hsh{vlayout},
51                }, $class;
52 }
53
54 sub _draw {
55   my $self = shift;
56
57   $self->_valid
58     or return;
59
60   my %input = @_;
61   if (exists $input{channel}) {
62     i_ft2_cp($self->{id}, $input{image}{IMG}, $input{'x'}, $input{'y'},
63              $input{channel}, $input{size}, $input{sizew} || 0,
64              $input{string}, , $input{align}, $input{aa}, $input{vlayout},
65              $input{utf8});
66   } else {
67     i_ft2_text($self->{id}, $input{image}{IMG},
68                $input{'x'}, $input{'y'},
69                $input{color}, $input{size}, $input{sizew} || 0,
70                $input{string}, $input{align}, $input{aa}, $input{vlayout},
71                $input{utf8});
72   }
73 }
74
75 sub _bounding_box {
76   my $self = shift;
77   my %input = @_;
78
79   $self->_valid
80     or return;
81
82   my @result =  i_ft2_bbox($self->{id}, $input{size}, $input{sizew},
83                            $input{string}, $input{utf8});
84   unless (@result) {
85     Imager->_set_error(Imager->_error_as_msg);
86     return;
87   }
88
89   return @result;
90 }
91
92 sub dpi {
93   my $self = shift;
94
95   $self->_valid
96     or return;
97
98   my @old = i_ft2_getdpi($self->{id});
99   if (@_) {
100     my %hsh = @_;
101     my $result;
102     unless ($hsh{xdpi} && $hsh{ydpi}) {
103       if ($hsh{dpi}) {
104         $hsh{xdpi} = $hsh{ydpi} = $hsh{dpi};
105       }
106       else {
107         $Imager::ERRSTR = "dpi method requires xdpi and ydpi or just dpi";
108         return;
109       }
110       i_ft2_setdpi($self->{id}, $hsh{xdpi}, $hsh{ydpi}) or return;
111     }
112   }
113   
114   return @old;
115 }
116
117 sub hinting {
118   my ($self, %opts) = @_;
119
120   $self->_valid
121     or return;
122
123   i_ft2_sethinting($self->{id}, $opts{hinting} || 0);
124 }
125
126 sub _transform {
127   my $self = shift;
128
129   $self->_valid
130     or return;
131
132   my %hsh = @_;
133   my $matrix = $hsh{matrix} or return undef;
134
135   return i_ft2_settransform($self->{id}, $matrix)
136 }
137
138 sub utf8 {
139   return 1;
140 }
141
142 # check if the font has the characters in the given string
143 sub has_chars {
144   my ($self, %hsh) = @_;
145
146   $self->_valid
147     or return;
148
149   unless (defined $hsh{string} && length $hsh{string}) {
150     $Imager::ERRSTR = "No string supplied to \$font->has_chars()";
151     return;
152   }
153   if (wantarray) {
154     my @result =  i_ft2_has_chars($self->{id}, $hsh{string}, 
155                                   _first($hsh{'utf8'}, $self->{utf8}, 0));
156     unless (@result) {
157       Imager->_set_error(Imager->_error_as_msg);
158       return;
159     }
160
161     return @result;
162   }
163   else {
164     my $result =  i_ft2_has_chars($self->{id}, $hsh{string}, 
165                                   _first($hsh{'utf8'}, $self->{utf8}, 0));
166     unless (defined $result) {
167       Imager->_set_error(Imager->_error_as_msg);
168       return;
169     }
170     return $result;
171   }
172 }
173
174 sub face_name {
175   my ($self) = @_;
176
177   $self->_valid
178     or return;
179
180   i_ft2_face_name($self->{id});
181 }
182
183 sub can_glyph_names {
184   my ($self) = @_;
185
186   i_ft2_can_do_glyph_names()
187     or return;
188
189   if (ref $self) {
190     $self->_valid
191       or return;
192
193     i_ft2_face_has_glyph_names($self->{id})
194       or return;
195   }
196
197   return 1;
198 }
199
200 sub glyph_names {
201   my ($self, %input) = @_;
202
203   $self->_valid
204     or return;
205
206   my $string = $input{string};
207   defined $string
208     or return Imager->_set_error("no string parameter passed to glyph_names");
209   my $utf8 = _first($input{utf8}, 0);
210   my $reliable_only = _first($input{reliable_only}, 1);
211
212   my @names = i_ft2_glyph_name($self->{id}, $string, $utf8, $reliable_only);
213   @names or return Imager->_set_error(Imager->_error_as_msg);
214
215   return @names if wantarray;
216   return pop @names;
217 }
218
219 sub is_mm {
220   my ($self) = @_;
221
222   $self->_valid
223     or return;
224
225   i_ft2_is_multiple_master($self->{id});
226 }
227
228 sub mm_axes {
229   my ($self) = @_;
230
231   $self->_valid
232     or return;
233
234   my ($num_axis, $num_design, @axes) =
235     i_ft2_get_multiple_masters($self->{id})
236       or return Imager->_set_error(Imager->_error_as_msg);
237
238   return @axes;
239 }
240
241 sub set_mm_coords {
242   my ($self, %opts) = @_;
243
244   $self->_valid
245     or return;
246
247   $opts{coords}
248     or return Imager->_set_error("Missing coords parameter");
249   ref($opts{coords}) && $opts{coords} =~ /ARRAY\(0x[\da-f]+\)$/
250     or return Imager->_set_error("coords parameter must be an ARRAY ref");
251
252   i_ft2_set_mm_coords($self->{id}, @{$opts{coords}})
253     or return Imager->_set_error(Imager->_error_as_msg);
254
255   return 1;
256 }
257
258 # objects may be invalidated on thread creation (or Win32 fork emulation)
259 sub _valid {
260   my $self = shift;
261
262   unless ($self->{id} && Scalar::Util::blessed($self->{id})) {
263     Imager->_set_error("font object was created in another thread");
264     return;
265   }
266
267   return 1;
268 }
269
270 1;
271
272 __END__
273
274 =head1 NAME
275
276 Imager::Font::FT2 - font support using FreeType 2
277
278 =head1 SYNOPSIS
279
280   use Imager;
281
282   my $img = Imager->new;
283   my $font = Imager::Font->new(file => "foo.ttf", type => "ft2");
284
285   $img->string(... font => $font);
286
287 =head1 DESCRIPTION
288
289 This provides font support on FreeType 2.
290
291 =head1 CAVEATS
292
293 Unfortunately, older versions of Imager would install
294 C<Imager::Font::FreeType2> even if FreeType 2 wasn't available, and if
295 no font was created would succeed in loading the module.  This means
296 that an existing C<FreeType2.pm> could cause a probe success for
297 supported font files, so I've renamed it.
298
299 =head1 AUTHOR
300
301 Tony Cook <tonyc@cpan.org>
302
303 =head1 SEE ALSO
304
305 Imager, Imager::Font.
306
307 =cut