pointer to a description of how unsharp mask works
[imager.git] / lib / Imager / Font.pm
CommitLineData
02d1d628
AMH
1package Imager::Font;
2
3use Imager::Color;
4use strict;
96190b6e 5use File::Spec;
02d1d628
AMH
6
7# This class is a container
8# and works for both truetype and t1 fonts.
9
10
02d1d628
AMH
11# search method
12# 1. start by checking if file is the parameter
13# 1a. if so qualify path and compare to the cache.
14# 2a. if in cache - take it's id from there and increment count.
15#
16
17sub new {
18 my $class = shift;
19 my $self ={};
20 my ($file,$type,$id);
21 my %hsh=(color=>Imager::Color->new(255,0,0,0),
22 size=>15,
23 @_);
24
25 bless $self,$class;
26
27 if ($hsh{'file'}) {
28 $file=$hsh{'file'};
29 if ( $file !~ m/^\// ) {
30 $file='./'.$file;
31 if (! -e $file) {
32 $Imager::ERRSTR="Font $file not found";
33 return();
34 }
35 }
36
37 $type=$hsh{'type'};
38 if (!defined($type) or $type !~ m/^(t1|tt)/) {
39 $type='tt' if $file =~ m/\.ttf$/i;
40 $type='t1' if $file =~ m/\.pfb$/i;
41 }
42 if (!defined($type)) {
43 $Imager::ERRSTR="Font type not found";
44 return;
45 }
46 } else {
47 $Imager::ERRSTR="No font file specified";
48 return;
49 }
50
51 if (!$Imager::formats{$type}) {
52 $Imager::ERRSTR="`$type' not enabled";
53 return;
54 }
55
56 # here we should have the font type or be dead already.
57
58 if ($type eq 't1') {
96190b6e
TC
59 require 'Imager/Font/Type1.pm';
60 return Imager::Font::Type1->new(%hsh);
02d1d628
AMH
61 }
62
63 if ($type eq 'tt') {
96190b6e
TC
64 require 'Imager/Font/Truetype.pm';
65 return Imager::Font::Truetype->new(%hsh);
02d1d628 66 }
96190b6e
TC
67 # it would be nice to have some generic mechanism to select the
68 # class
69
70 return undef;
02d1d628
AMH
71}
72
96190b6e
TC
73# returns first defined parameter
74sub _first {
75 for (@_) {
76 return $_ if defined $_;
77 }
78 return undef;
79}
02d1d628 80
96190b6e
TC
81sub draw {
82 my $self = shift;
83 my %input = (x => 0, 'y' => 0, @_);
84 unless ($input{image}) {
85 $Imager::ERRSTR = 'No image supplied to $font->draw()';
86 return;
87 }
88 $input{string} = _first($input{string}, $input{text});
89 unless (defined $input{string}) {
90 $Imager::ERRSTR = "Missing require parameter 'string'";
91 return;
92 }
93 $input{aa} = _first($input{aa}, $input{antialias}, $self->{aa}, 1);
94 # the original draw code worked this out but didn't use it
95 $input{align} = _first($input{align}, $self->{align});
96 $input{color} = _first($input{color}, $self->{color});
97 $input{size} = _first($input{size}, $self->{size});
98 unless (defined $input{size}) {
99 $input{image}{ERRSTR} = "No font size provided";
100 return undef;
101 }
102 $input{align} = _first($input{align}, 1);
103 $self->_draw(%input);
104}
02d1d628
AMH
105
106sub bounding_box {
107 my $self=shift;
108 my %input=@_;
02d1d628 109
96190b6e
TC
110 if (!exists $input{'string'}) {
111 $Imager::ERRSTR='string parameter missing';
112 return;
02d1d628 113 }
96190b6e
TC
114 $input{size} ||= $self->{size};
115
116 my @box = $self->_bounding_box(%input);
02d1d628
AMH
117
118 if(exists $input{'x'} and exists $input{'y'}) {
119 my($gdescent, $gascent)=@box[1,3];
120 $box[1]=$input{'y'}-$gascent; # top = base - ascent (Y is down)
121 $box[3]=$input{'y'}-$gdescent; # bottom = base - descent (Y is down, descent is negative)
122 $box[0]+=$input{'x'};
123 $box[2]+=$input{'x'};
124 } elsif (exists $input{'canon'}) {
125 $box[3]-=$box[1]; # make it cannoical (ie (0,0) - (width, height))
126 $box[2]-=$box[0];
127 }
128 return @box;
129}
130
1311;
132
02d1d628
AMH
133__END__
134
135=head1 NAME
136
137Imager::Font - Font handling for Imager.
138
139=head1 SYNOPSIS
140
141 $t1font = Imager::Font->new(file => 'pathtofont.pfb');
142 $ttfont = Imager::Font->new(file => 'pathtofont.ttf');
143
144 $blue = Imager::Color->new("#0000FF");
145 $font = Imager::Font->new(file => 'pathtofont.ttf',
146 color => $blue,
147 size => 30);
148
149 ($neg_width,
150 $global_descent,
151 $pos_width,
152 $global_ascent,
153 $descent,
154 $ascent) = $font->bounding_box(string=>"Foo");
155
156 $logo = $font->logo(text => "Slartibartfast Enterprises",
157 size => 40,
158 border => 5,
159 color => $green);
160 # logo is proposed - doesn't exist yet
161
162
163 $img->string(font => $font,
164 text => "Model-XYZ",
165 x => 15,
166 y => 40,
167 size => 40,
5b0d044f 168 color => $red,
02d1d628
AMH
169 aa => 1);
170
171 # Documentation in Imager.pm
172
173=head1 DESCRIPTION
174
175This module handles creating Font objects used by imager. The module
176also handles querying fonts for sizes and such. If both T1lib and
177freetype were avaliable at the time of compilation then Imager should
178be able to work with both truetype fonts and t1 postscript fonts. To
179check if Imager is t1 or truetype capable you can use something like
180this:
181
182 use Imager;
183 print "Has truetype" if $Imager::formats{tt};
184 print "Has t1 postscript" if $Imager::formats{t1};
185
186
187=over 4
188
189=item new
190
191This creates a font object to pass to functions that take a font argument.
192
193 $font = Imager::Font->new(file => 'denmark.ttf',
194 color => $blue,
195 size => 30,
196 aa => 1);
197
198This creates a font which is the truetype font denmark.ttf. It's
199default color is $blue, default size is 30 pixels and it's rendered
200antialised by default. Imager can see which type of font a file is by
201looking at the suffix of the filename for the font. A suffix of 'ttf'
202is taken to mean a truetype font while a suffix of 'pfb' is taken to
203mean a t1 postscript font. If Imager cannot tell which type a font is
204you can tell it explicitly by using the C<type> parameter:
205
206 $t1font = Imager::Font->new(file => 'fruitcase', type => 't1');
207 $ttfont = Imager::Font->new(file => 'arglebarf', type => 'tt');
208
209If any of the C<color>, C<size> or C<aa> parameters are omitted when
210calling C<Imager::Font->new()> the they take the following values:
211
212
213 color => Imager::Color->new(255, 0, 0, 0); # this default should be changed
214 size => 15
215 aa => 0
216
217=item bounding_box
218Returns the bounding box for the specified string. Example:
219
220 ($neg_width,
221 $global_descent,
222 $pos_width,
223 $global_ascent,
224 $descent,
225 $ascent) = $font->bounding_box(string => "A Fool");
226
227The C<$neg_width> is the relative start of a the string. In some
228cases this can be a negative number, in that case the first letter
229stretches to the left of the starting position that is specified in
230the string method of the Imager class. <$global_descent> is the how
231far down the lowest letter of the entire font reaches below the
232baseline (this is often j). C<$pos_width> is how wide the string from
233from the starting position is. The total width of the string is
234C<$pos_width-$neg_width>. C<$descent> and C<$ascent> are the as
235<$global_descent> and <$global_ascent> except that they are only for
236the characters that appear in the string. Obviously we can stuff all
237the results into an array just as well:
238
239 @metrics = $font->bounding_box(string => "testing 123");
240
241Note that extra values may be added, so $metrics[-1] isn't supported.
242It's possible to translate the output by a passing coordinate to the
243bounding box method:
244
245 @metrics = $font->bounding_box(string => "testing 123", x=>45, y=>34);
246
247This gives the bounding box as if the string had been put down at C<(x,y)>
248By giving bounding_box 'canon' as a true value it's possible to measure
249the space needed for the string:
250
251 @metrics = $font->bounding_box(string=>"testing",size=>15,canon=>1);
252
253This returns tha same values in $metrics[0] and $metrics[1],
254but:
255
256 $bbox[2] - horizontal space taken by glyphs
257 $bbox[3] - vertical space taken by glyphs
258
259
260
261=item string
262
263This is a method of the Imager class but because it's described in
264here since it belongs to the font routines. Example:
265
266 $img=Imager->new();
5b0d044f 267 $img->read(file=>"test.jpg");
02d1d628
AMH
268 $img->string(font=>$t1font,
269 text=>"Model-XYZ",
270 x=>0,
271 y=>40,
272 size=>40,
273 color=>$red);
274 $img->write(file=>"testout.jpg");
275
276This would put a 40 pixel high text in the top left corner of an
277image. If you measure the actuall pixels it varies since the fonts
278usually do not use their full height. It seems that the color and
279size can be specified twice. When a font is created only the actual
280font specified matters. It his however convenient to store default
281values in a font, such as color and size. If parameters are passed to
282the string function they are used instead of the defaults stored in
283the font.
284
285If string() is called with the C<channel> parameter then the color
286isn't used and the font is drawn in only one channel of the image.
287This can be quite handy to create overlays. See the examples for tips
288about this.
289
290Sometimes it is necessary to know how much space a string takes before
291rendering it. The bounding_box() method described earlier can be used
292for that.
293
294
295=item logo
296
297This method doesn't exist yet but is under consideration. It would mostly
298be helpful for generating small tests and such. Its proposed interface is:
299
300 $img = $font->logo(string=>"Plan XYZ", color=>$blue, border=>7);
301
302This would be nice for writing (admittedly multiline) one liners like:
303
304Imager::Font->new(file=>"arial.ttf", color=>$blue, aa=>1)
305 ->string(text=>"Plan XYZ", border=>5)
306 ->write(file=>"xyz.png");
307
308
309
310=back
311
312=head1 AUTHOR
313
314Arnar M. Hrafnkelsson, addi@umich.edu
315And a great deal of help from others - see the README for a complete
316list.
317
96190b6e
TC
318=head1 BUGS
319
320You need to modify this class to add new font types.
321
02d1d628
AMH
322=head1 SEE ALSO
323
324Imager(3)
325http://www.eecs.umich.edu/~addi/perl/Imager/
326
327=cut
328
329