fixed some minor test code hiccups
[imager.git] / lib / Imager / Font.pm
CommitLineData
02d1d628
AMH
1package Imager::Font;
2
3use Imager::Color;
4use strict;
02d1d628 5
faa9b3e7
TC
6# the aim here is that we can:
7# - add file based types in one place: here
8# - make sure we only attempt to create types that exist
9# - give reasonable defaults
10# - give the user some control over which types get used
11my %drivers =
12 (
13 tt=>{
14 class=>'Imager::Font::Truetype',
15 module=>'Imager/Font/Truetype.pm',
16 files=>'.*\.ttf$',
17 },
18 t1=>{
19 class=>'Imager::Font::Type1',
20 module=>'Imager/Font/Type1.pm',
21 files=>'.*\.pfb$',
22 },
23 ft2=>{
24 class=>'Imager::Font::FreeType2',
25 module=>'Imager/Font/FreeType2.pm',
8c3f7e59 26 files=>'.*\.(pfa|pfb|otf|ttf|fon|fnt)$',
faa9b3e7
TC
27 },
28 w32=>{
29 class=>'Imager::Font::Win32',
30 module=>'Imager/Font/Win32.pm',
31 },
32 );
33
34# this currently should only contain file based types, don't add w32
35my @priority = qw(t1 tt ft2);
36
37# when Imager::Font is loaded, Imager.xs has not been bootstrapped yet
38# this function is called from Imager.pm to finish initialization
39sub __init {
40 @priority = grep Imager::i_has_format($_), @priority;
41 delete @drivers{grep !Imager::i_has_format($_), keys %drivers};
42}
02d1d628 43
02d1d628
AMH
44# search method
45# 1. start by checking if file is the parameter
46# 1a. if so qualify path and compare to the cache.
47# 2a. if in cache - take it's id from there and increment count.
48#
49
50sub new {
51 my $class = shift;
52 my $self ={};
53 my ($file,$type,$id);
54 my %hsh=(color=>Imager::Color->new(255,0,0,0),
55 size=>15,
56 @_);
57
58 bless $self,$class;
59
60 if ($hsh{'file'}) {
61 $file=$hsh{'file'};
62 if ( $file !~ m/^\// ) {
63 $file='./'.$file;
64 if (! -e $file) {
65 $Imager::ERRSTR="Font $file not found";
66 return();
67 }
68 }
69
70 $type=$hsh{'type'};
faa9b3e7
TC
71 if (!defined($type) or !$drivers{$type}) {
72 for my $drv (@priority) {
73 undef $type;
74 my $re = $drivers{$drv}{files} or next;
75 if ($file =~ /$re/i) {
76 $type = $drv;
77 last;
78 }
79 }
02d1d628
AMH
80 }
81 if (!defined($type)) {
82 $Imager::ERRSTR="Font type not found";
83 return;
84 }
faa9b3e7
TC
85 } elsif ($hsh{face}) {
86 $type = "w32";
02d1d628
AMH
87 } else {
88 $Imager::ERRSTR="No font file specified";
89 return;
90 }
91
92 if (!$Imager::formats{$type}) {
93 $Imager::ERRSTR="`$type' not enabled";
94 return;
95 }
96
97 # here we should have the font type or be dead already.
98
faa9b3e7
TC
99 require $drivers{$type}{module};
100 return $drivers{$type}{class}->new(%hsh);
02d1d628
AMH
101}
102
96190b6e
TC
103# returns first defined parameter
104sub _first {
105 for (@_) {
106 return $_ if defined $_;
107 }
108 return undef;
109}
02d1d628 110
96190b6e
TC
111sub draw {
112 my $self = shift;
9d540150 113 my %input = ('x' => 0, 'y' => 0, @_);
96190b6e
TC
114 unless ($input{image}) {
115 $Imager::ERRSTR = 'No image supplied to $font->draw()';
116 return;
117 }
118 $input{string} = _first($input{string}, $input{text});
119 unless (defined $input{string}) {
120 $Imager::ERRSTR = "Missing require parameter 'string'";
121 return;
122 }
123 $input{aa} = _first($input{aa}, $input{antialias}, $self->{aa}, 1);
124 # the original draw code worked this out but didn't use it
125 $input{align} = _first($input{align}, $self->{align});
126 $input{color} = _first($input{color}, $self->{color});
127 $input{size} = _first($input{size}, $self->{size});
128 unless (defined $input{size}) {
129 $input{image}{ERRSTR} = "No font size provided";
130 return undef;
131 }
132 $input{align} = _first($input{align}, 1);
faa9b3e7
TC
133 $input{utf8} = _first($input{utf8}, $self->{utf8}, 0);
134 $input{vlayout} = _first($input{vlayout}, $self->{vlayout}, 0);
135
96190b6e
TC
136 $self->_draw(%input);
137}
02d1d628
AMH
138
139sub bounding_box {
140 my $self=shift;
141 my %input=@_;
02d1d628 142
96190b6e
TC
143 if (!exists $input{'string'}) {
144 $Imager::ERRSTR='string parameter missing';
145 return;
02d1d628 146 }
96190b6e 147 $input{size} ||= $self->{size};
90654457 148 $input{sizew} = _first($input{sizew}, $self->{sizew}, 0);
fec2a434 149 $input{utf8} = _first($input{utf8}, $self->{utf8}, 0);
96190b6e
TC
150
151 my @box = $self->_bounding_box(%input);
02d1d628 152
5cb9270b 153 if(@box && exists $input{'x'} and exists $input{'y'}) {
02d1d628
AMH
154 my($gdescent, $gascent)=@box[1,3];
155 $box[1]=$input{'y'}-$gascent; # top = base - ascent (Y is down)
156 $box[3]=$input{'y'}-$gdescent; # bottom = base - descent (Y is down, descent is negative)
157 $box[0]+=$input{'x'};
158 $box[2]+=$input{'x'};
5cb9270b 159 } elsif (@box && $input{'canon'}) {
02d1d628
AMH
160 $box[3]-=$box[1]; # make it cannoical (ie (0,0) - (width, height))
161 $box[2]-=$box[0];
162 }
163 return @box;
164}
165
faa9b3e7
TC
166sub dpi {
167 my $self = shift;
168
169 # I'm assuming a default of 72 dpi
170 my @old = (72, 72);
171 if (@_) {
172 $Imager::ERRSTR = "Setting dpi not implemented for this font type";
173 return;
174 }
175
176 return @old;
177}
178
179sub transform {
180 my $self = shift;
181
182 my %hsh = @_;
183
184 # this is split into transform() and _transform() so we can
185 # implement other tags like: degrees=>12, which would build a
186 # 12 degree rotation matrix
187 # but I'll do that later
188 unless ($hsh{matrix}) {
189 $Imager::ERRSTR = "You need to supply a matrix";
190 return;
191 }
192
193 return $self->_transform(%hsh);
194}
195
196sub _transform {
197 $Imager::ERRSTR = "This type of font cannot be transformed";
198 return;
199}
200
201sub utf8 {
202 return 0;
203}
204
205sub priorities {
206 my $self = shift;
207 my @old = @priority;
208
209 if (@_) {
210 @priority = grep Imager::i_has_format($_), @_;
211 }
212 return @old;
213}
214
02d1d628
AMH
2151;
216
02d1d628
AMH
217__END__
218
219=head1 NAME
220
221Imager::Font - Font handling for Imager.
222
223=head1 SYNOPSIS
224
225 $t1font = Imager::Font->new(file => 'pathtofont.pfb');
226 $ttfont = Imager::Font->new(file => 'pathtofont.ttf');
faa9b3e7 227 $w32font = Imager::Font->new(face => 'Times New Roman');
02d1d628
AMH
228
229 $blue = Imager::Color->new("#0000FF");
230 $font = Imager::Font->new(file => 'pathtofont.ttf',
231 color => $blue,
232 size => 30);
233
234 ($neg_width,
235 $global_descent,
236 $pos_width,
237 $global_ascent,
238 $descent,
239 $ascent) = $font->bounding_box(string=>"Foo");
240
241 $logo = $font->logo(text => "Slartibartfast Enterprises",
242 size => 40,
243 border => 5,
244 color => $green);
245 # logo is proposed - doesn't exist yet
246
247
248 $img->string(font => $font,
249 text => "Model-XYZ",
250 x => 15,
251 y => 40,
252 size => 40,
5b0d044f 253 color => $red,
02d1d628
AMH
254 aa => 1);
255
256 # Documentation in Imager.pm
257
258=head1 DESCRIPTION
259
260This module handles creating Font objects used by imager. The module
261also handles querying fonts for sizes and such. If both T1lib and
262freetype were avaliable at the time of compilation then Imager should
263be able to work with both truetype fonts and t1 postscript fonts. To
264check if Imager is t1 or truetype capable you can use something like
265this:
266
267 use Imager;
268 print "Has truetype" if $Imager::formats{tt};
269 print "Has t1 postscript" if $Imager::formats{t1};
faa9b3e7
TC
270 print "Has Win32 fonts" if $Imager::formats{w32};
271 print "Has Freetype2" if $Imager::formats{ft2};
02d1d628
AMH
272
273=over 4
274
275=item new
276
277This creates a font object to pass to functions that take a font argument.
278
279 $font = Imager::Font->new(file => 'denmark.ttf',
280 color => $blue,
281 size => 30,
282 aa => 1);
283
284This creates a font which is the truetype font denmark.ttf. It's
285default color is $blue, default size is 30 pixels and it's rendered
286antialised by default. Imager can see which type of font a file is by
287looking at the suffix of the filename for the font. A suffix of 'ttf'
288is taken to mean a truetype font while a suffix of 'pfb' is taken to
289mean a t1 postscript font. If Imager cannot tell which type a font is
290you can tell it explicitly by using the C<type> parameter:
291
292 $t1font = Imager::Font->new(file => 'fruitcase', type => 't1');
293 $ttfont = Imager::Font->new(file => 'arglebarf', type => 'tt');
294
295If any of the C<color>, C<size> or C<aa> parameters are omitted when
296calling C<Imager::Font->new()> the they take the following values:
297
298
299 color => Imager::Color->new(255, 0, 0, 0); # this default should be changed
300 size => 15
301 aa => 0
302
faa9b3e7
TC
303To use Win32 fonts supply the facename of the font:
304
305 $font = Imager::Font->new(face=>'Arial Bold Italic');
306
307There isn't any access to other logical font attributes, but this
308typically isn't necessary for Win32 TrueType fonts, since you can
309contruct the full name of the font as above.
310
311Other logical font attributes may be added if there is sufficient demand.
312
02d1d628 313=item bounding_box
faa9b3e7 314
02d1d628
AMH
315Returns the bounding box for the specified string. Example:
316
317 ($neg_width,
318 $global_descent,
319 $pos_width,
320 $global_ascent,
321 $descent,
322 $ascent) = $font->bounding_box(string => "A Fool");
323
324The C<$neg_width> is the relative start of a the string. In some
325cases this can be a negative number, in that case the first letter
326stretches to the left of the starting position that is specified in
327the string method of the Imager class. <$global_descent> is the how
328far down the lowest letter of the entire font reaches below the
0c822d0c 329baseline (this is often j). C<$pos_width> is how wide the string
02d1d628 330from the starting position is. The total width of the string is
f6922906 331C<$pos_width-$neg_width>. C<$descent> and C<$ascent> are the same as
02d1d628
AMH
332<$global_descent> and <$global_ascent> except that they are only for
333the characters that appear in the string. Obviously we can stuff all
334the results into an array just as well:
335
336 @metrics = $font->bounding_box(string => "testing 123");
337
338Note that extra values may be added, so $metrics[-1] isn't supported.
339It's possible to translate the output by a passing coordinate to the
340bounding box method:
341
342 @metrics = $font->bounding_box(string => "testing 123", x=>45, y=>34);
343
344This gives the bounding box as if the string had been put down at C<(x,y)>
345By giving bounding_box 'canon' as a true value it's possible to measure
346the space needed for the string:
347
348 @metrics = $font->bounding_box(string=>"testing",size=>15,canon=>1);
349
350This returns tha same values in $metrics[0] and $metrics[1],
351but:
352
353 $bbox[2] - horizontal space taken by glyphs
354 $bbox[3] - vertical space taken by glyphs
355
356
357
358=item string
359
360This is a method of the Imager class but because it's described in
361here since it belongs to the font routines. Example:
362
363 $img=Imager->new();
5b0d044f 364 $img->read(file=>"test.jpg");
02d1d628
AMH
365 $img->string(font=>$t1font,
366 text=>"Model-XYZ",
367 x=>0,
368 y=>40,
369 size=>40,
370 color=>$red);
371 $img->write(file=>"testout.jpg");
372
373This would put a 40 pixel high text in the top left corner of an
374image. If you measure the actuall pixels it varies since the fonts
375usually do not use their full height. It seems that the color and
376size can be specified twice. When a font is created only the actual
377font specified matters. It his however convenient to store default
378values in a font, such as color and size. If parameters are passed to
379the string function they are used instead of the defaults stored in
380the font.
381
faa9b3e7
TC
382The following parameters can be supplied to the string() method:
383
384=over
385
386=item string
387
388The text to be rendered. If this isn't present the 'text' parameter
389is used. If neither is present the call will fail.
390
391=item aa
392
393If non-zero the output will be anti-aliased.
394
395=item x
396
397=item y
398
399The start point for rendering the text. See the align parameter.
400
401=item align
402
403If non-zero the point supplied in (x,y) will be on the base-line, if
404zero then (x,y) will be at the top-left of the first character.
405
406=item channel
407
408If present, the text will be written to the specified channel of the
409image and the color parameter will be ignore.
410
411=item color
412
413The color to draw the text in.
414
415=item size
416
417The point-size to draw the text at.
418
419=item sizew
420
421For drivers that support it, the width to draw the text at. Defaults
422to be value of the 'size' parameter.
423
424=item utf8
425
426For drivers that support it, treat the string as UTF8 encoded. For
427versions of perl that support Unicode (5.6 and later), this will be
428enabled automatically if the 'string' parameter is already a UTF8
429string. See L<UTF8> for more information.
430
431=item vlayout
432
433For drivers that support it, draw the text vertically. Note: I
434haven't found a font that has the appropriate metrics yet.
435
436=back
437
02d1d628
AMH
438If string() is called with the C<channel> parameter then the color
439isn't used and the font is drawn in only one channel of the image.
440This can be quite handy to create overlays. See the examples for tips
441about this.
442
443Sometimes it is necessary to know how much space a string takes before
444rendering it. The bounding_box() method described earlier can be used
445for that.
446
faa9b3e7
TC
447=item dpi()
448
449=item dpi(xdpi=>$xdpi, ydpi=>$ydpi)
450
451=item dpi(dpi=>$dpi)
452
453Set retrieve the spatial resolution of the image in dots per inch.
454The default is 72 dpi.
455
456This isn't implemented for all font types yet.
457
458=item transform(matrix=>$matrix)
459
460Applies a transformation to the font, where matrix is an array ref of
461numbers representing a 2 x 3 matrix:
462
463 [ $matrix->[0], $matrix->[1], $matrix->[2],
464 $matrix->[3], $matrix->[4], $matrix->[5] ]
465
466Not all font types support transformations, these will return false.
467
468It's possible that a driver will disable hinting if you use a
469transformation, to prevent discontinuities in the transformations.
470See the end of the test script t/t38ft2font.t for an example.
02d1d628 471
3dec2c92
TC
472=item has_chars(string=>$text)
473
474Checks if the characters in $text are defined by the font.
475
476In a list context returns a list of true or false value corresponding
477to the characters in $text, true if the character is defined, false if
478not. In scalar context returns a string of NUL or non-NUL
10461f9a 479characters. Supports UTF8 where the font driver supports UTF8.
3dec2c92
TC
480
481Not all fonts support this method (use $font->can("has_chars") to
482check.)
483
02d1d628
AMH
484=item logo
485
486This method doesn't exist yet but is under consideration. It would mostly
487be helpful for generating small tests and such. Its proposed interface is:
488
489 $img = $font->logo(string=>"Plan XYZ", color=>$blue, border=>7);
490
491This would be nice for writing (admittedly multiline) one liners like:
492
493Imager::Font->new(file=>"arial.ttf", color=>$blue, aa=>1)
494 ->string(text=>"Plan XYZ", border=>5)
495 ->write(file=>"xyz.png");
496
faa9b3e7
TC
497=back
498
499=head1 UTF8
500
501There are 2 ways of rendering Unicode characters with Imager:
02d1d628 502
faa9b3e7
TC
503=over
504
505=item *
506
507For versions of perl that support it, use perl's native UTF8 strings.
508This is the simplest method.
509
510=item *
511
512Hand build your own UTF8 encoded strings. Only recommended if your
513version of perl has no UTF8 support.
02d1d628
AMH
514
515=back
516
faa9b3e7
TC
517Imager won't construct characters for you, so if want to output
518unicode character 00C3 "LATIN CAPITAL LETTER A WITH DIAERESIS", and
519your font doesn't support it, Imager will I<not> build it from 0041
520"LATIN CAPITAL LETTER A" and 0308 "COMBINING DIAERESIS".
521
522=head2 Native UTF8 Support
523
524If your version of perl supports UTF8 and the driver supports UTF8,
525just use the $im->string() method, and it should do the right thing.
526
527=head2 Build your own
528
529In this case you need to build your own UTF8 encoded characters.
530
531For example:
532
533 $x = pack("C*", 0xE2, 0x80, 0x90); # character code 0x2010 HYPHEN
534
535You need to be be careful with versions of perl that have UTF8
536support, since your string may end up doubly UTF8 encoded.
537
538For example:
539
540 $x = "A\xE2\x80\x90\x41\x{2010}";
541 substr($x, -1, 0) = "";
542 # at this point $x is has the UTF8 flag set, but has 5 characters,
543 # none, of which is the constructed UTF8 character
544
545The test script t/t38ft2font.t has a small example of this after the
546comment:
547
548 # an attempt using emulation of UTF8
549
550=head1 DRIVER CONTROL
551
552If you don't supply a 'type' parameter to Imager::Font->new(), but you
553do supply a 'file' parameter, Imager will attempt to guess which font
554driver to used based on the extension of the font file.
555
556Since some formats can be handled by more than one driver, a priority
557list is used to choose which one should be used, if a given format can
558be handled by more than one driver.
559
560The current priority can be retrieved with:
561
562 @drivers = Imager::Font->priorities();
563
564You can set new priorities and save the old priorities with:
565
566 @old = Imager::Font->priorities(@drivers);
567
568If you supply driver names that are not currently supported, they will
569be ignored.
570
571Imager supports both T1Lib and Freetype2 for working with Type 1
572fonts, but currently only T1Lib does any caching, so by default T1Lib
573is given a higher priority. Since Imager's Freetype2 support can also
574do font transformations, you may want to give that a higher priority:
575
576 my @old = Imager::Font->priorities(qw(tt ft2 t1));
577
02d1d628
AMH
578=head1 AUTHOR
579
580Arnar M. Hrafnkelsson, addi@umich.edu
581And a great deal of help from others - see the README for a complete
582list.
583
96190b6e
TC
584=head1 BUGS
585
586You need to modify this class to add new font types.
587
02d1d628
AMH
588=head1 SEE ALSO
589
412e7a35
AMH
590Imager(3), Imager::Font::FreeType2(3), Imager::Font::Type1(3),
591Imager::Font::Win32(3), Imager::Font::Truetype(3)
592
593
02d1d628
AMH
594http://www.eecs.umich.edu/~addi/perl/Imager/
595
596=cut
597
598