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