5 use Imager::Test qw(diff_text_with_nul is_color3 is_image isnt_image);
7 use Cwd qw(getcwd abs_path);
13 ok($Imager::formats{t1}, "must have t1");
15 -d "testout" or mkdir "testout";
16 ok(-d "testout", "make output directory");
18 init_log("testout/t10type1.log",1);
20 my $deffont = 'fontfiles/dcr10.pfb';
22 my $fontname_pfb=$ENV{'T1FONTTESTPFB'}||$deffont;
23 my $fontname_afm=$ENV{'T1FONTTESTAFM'}||'./fontfiles/dcr10.afm';
26 or skip_all("cannot find fontfile for type 1 test $fontname_pfb");
28 or skip_all("cannot find fontfile for type 1 test $fontname_afm");
36 unlink "t1lib.log"; # lose it if it exists
38 ok(!-e("t1lib.log"), "disable t1log");
40 ok(-e("t1lib.log"), "enable t1log");
44 my $fnum=Imager::Font::T1xs->new($fontname_pfb,$fontname_afm); # this will load the pfb font
45 unless (ok($fnum >= 0, "load font $fontname_pfb")) {
46 skip("without the font I can't do a thing", 90);
49 my $bgcolor=Imager::Color->new(255,0,0,0);
50 my $overlay=Imager::ImgRaw::new(200,70,3);
52 ok($fnum->cp($overlay,5,50,1,50.0,'XMCLH',1), "i_t1_cp");
54 i_line($overlay,0,50,100,50,$bgcolor,1);
56 my @bbox=$fnum->bbox(50.0,'XMCLH');
57 is(@bbox, 8, "i_t1_bbox");
58 print "# bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
60 open(FH,">testout/t30t1font.ppm") || die "cannot open testout/t35t1font.ppm\n";
61 binmode(FH); # for os2
62 my $IO = Imager::io_new_fd( fileno(FH) );
63 i_writeppm_wiol($overlay,$IO);
66 $bgcolor=Imager::Color::set($bgcolor,200,200,200,0);
67 my $backgr=Imager::ImgRaw::new(280,300,3);
69 ok($fnum->text($backgr,10,100,$bgcolor,150.0,'test',1,2), "i_t1_text");
72 # for perl < 5.6 we can hand-encode text
73 # since T1 doesn't support over 256 chars in an encoding we just drop
75 # the following is "A\xA1\x{2010}A"
77 my $text = pack("C*", 0x41, 0xC2, 0xA1, 0xE2, 0x80, 0x90, 0x41);
78 my $alttext = "A\xA1A";
80 my @utf8box = $fnum->bbox(50.0, $text, 1);
81 is(@utf8box, 8, "utf8 bbox element count");
82 my @base = $fnum->bbox(50.0, $alttext, 0);
83 is(@base, 8, "alt bbox element count");
84 my $maxdiff = $fontname_pfb eq $deffont ? 0 : $base[2] / 3;
85 print "# (@utf8box vs @base)\n";
86 ok(abs($utf8box[2] - $base[2]) <= $maxdiff,
87 "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)");
89 # hand-encoded UTF8 drawing
90 ok($fnum->text($backgr, 10, 140, $bgcolor, 32, $text, 1,1), "draw hand-encoded UTF8");
92 ok($fnum->cp($backgr, 80, 140, 1, 32, $text, 1, 1),
93 "cp hand-encoded UTF8");
95 # ok, try native perl UTF8 if available
98 $] >= 5.006 or skip("perl too old to test native UTF8 support", 5);
100 # we need to do this in eval to prevent compile time errors in older
102 eval q{$text = "A\xA1\x{2010}A"}; # A, a with ogonek, HYPHEN, A in our test font
103 #$text = "A".chr(0xA1).chr(0x2010)."A"; # this one works too
104 ok($fnum->text($backgr, 10, 180, $bgcolor, 32, $text, 1),
106 ok($fnum->cp($backgr, 80, 180, 1, 32, $text, 1),
108 @utf8box = $fnum->bbox(50.0, $text, 0);
109 is(@utf8box, 8, "native utf8 bbox element count");
110 ok(abs($utf8box[2] - $base[2]) <= $maxdiff,
111 "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)");
112 eval q{$text = "A\xA1\xA2\x01\x1F\x{0100}A"};
113 ok($fnum->text($backgr, 10, 220, $bgcolor, 32, $text, 0, 1, 0, "uso"),
114 "more complex output");
117 open(FH,">testout/t30t1font2.ppm") || die "cannot open testout/t35t1font.ppm\n";
119 $IO = Imager::io_new_fd( fileno(FH) );
120 i_writeppm_wiol($backgr, $IO);
125 # character existance tests - uses the special ExistenceTest font
126 my $exists_font = 'fontfiles/ExistenceTest.pfb';
127 my $exists_afm = 'fontfiles/ExistenceText.afm';
129 -e $exists_font or die "$exists_font not found";
131 my $font_num = Imager::Font::T1xs->new($exists_font, $exists_afm);
133 ok($font_num >= 0, 'loading test font')
134 or skip('Could not load test font', 6);
135 # first the list interface
136 my @exists = $font_num->has_chars("!A");
137 is(@exists, 2, "return count from has_chars");
138 ok($exists[0], "we have an exclamation mark");
139 ok(!$exists[1], "we have no uppercase A");
141 # then the scalar interface
142 my $exists = $font_num->has_chars("!A");
143 is(length($exists), 2, "return scalar length");
144 ok(ord(substr($exists, 0, 1)), "we have an exclamation mark");
145 ok(!ord(substr($exists, 1, 1)), "we have no upper-case A");
149 my $font = Imager::Font->new(file=>$exists_font, type=>'t1');
152 ok($font, "loaded OO font")
153 or skip("Could not load test font", 24);
154 my @exists = $font->has_chars(string=>"!A");
155 is(@exists, 2, "return count from has_chars");
156 ok($exists[0], "we have an exclamation mark");
157 ok(!$exists[1], "we have no uppercase A");
159 # then the scalar interface
160 my $exists = $font->has_chars(string=>"!A");
161 is(length($exists), 2, "return scalar length");
162 ok(ord(substr($exists, 0, 1)), "we have an exclamation mark");
163 ok(!ord(substr($exists, 1, 1)), "we have no upper-case A");
165 # check the advance width
166 my @bbox = $font->bounding_box(string=>'/', size=>100);
168 isnt($bbox[2], $bbox[5], "different advance to pos_width");
171 my $face_name = $font->{t1font}->face_name();
172 print "# face $face_name\n";
173 is($face_name, 'ExistenceTest', "face name");
174 $face_name = $font->face_name;
175 is($face_name, 'ExistenceTest', "face name");
177 my @glyph_names = $font->glyph_names(string=>"!J/");
178 is($glyph_names[0], 'exclam', "check exclam name OO");
179 ok(!defined($glyph_names[1]), "check for no J name OO");
180 is($glyph_names[2], 'slash', "check slash name OO");
182 # this character chosen since when it's truncated to one byte it
183 # becomes 0x21 or '!' which the font does define
184 my $text = pack("C*", 0xE2, 0x80, 0xA1); # "\x{2021}" as utf-8
185 @glyph_names = $font->glyph_names(string=>$text, utf8=>1);
186 is($glyph_names[0], undef, "expect no glyph_name for \\x{20A1}");
188 # make sure a missing string parameter is handled correctly
190 $font->glyph_names();
192 is($@, "", "correct error handling");
193 cmp_ok(Imager->errstr, '=~', qr/no string parameter/, "error message");
195 # test extended bounding box results
196 # the test font is known to have a shorter advance width for that char
197 @bbox = $font->bounding_box(string=>"/", size=>100);
198 is(@bbox, 8, "should be 8 entries");
199 isnt($bbox[6], $bbox[2], "different advance width");
200 my $bbox = $font->bounding_box(string=>"/", size=>100);
201 cmp_ok($bbox->pos_width, '>', $bbox->advance_width, "OO check");
203 cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
205 cmp_ok($bbox->display_width, '>', $bbox->advance_width,
206 "check display width (roughly)");
208 # check with a char that fits inside the box
209 $bbox = $font->bounding_box(string=>"!", size=>100);
210 print "# pos width ", $bbox->pos_width, "\n";
212 # they aren't the same historically for the type 1 driver
213 isnt($bbox->pos_width, $bbox->advance_width,
214 "check backwards compatibility");
215 cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
216 cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
217 cmp_ok($bbox->display_width, '<', $bbox->advance_width,
218 "display smaller than advance");
222 { print "# alignment tests\n";
223 my $font = Imager::Font->new(file=>$deffont, type=>'t1');
224 ok($font, "loaded deffont OO")
225 or skip("could not load font:".Imager->errstr, 4);
226 my $im = Imager->new(xsize=>140, ysize=>150);
233 $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
234 $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
235 $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
236 for my $args ([ x=>5, text=>"A", color=>"white" ],
237 [ x=>40, text=>"y", color=>"white" ],
238 [ x=>75, text=>"A", channel=>1 ],
239 [ x=>110, text=>"y", channel=>1 ]) {
240 ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
241 ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
242 ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
244 ok($im->write(file=>'testout/t30align.ppm'), "save align image");
249 # see http://rt.cpan.org/Ticket/Display.html?id=20555
250 print "# bounding box around spaces\n";
251 # SpaceTest contains 3 characters, space, ! and .undef
252 # only characters that define character zero seem to illustrate
253 # the problem we had with spaces
254 my $space_fontfile = "fontfiles/SpaceTest.pfb";
255 my $font = Imager::Font->new(file => $space_fontfile, type => 't1');
256 ok($font, "loaded $space_fontfile")
257 or skip("failed to load $space_fontfile" . Imager->errstr, 13);
258 my $bbox = $font->bounding_box(string => "", size => 36);
259 print "# empty string bbox: @$bbox\n";
260 is($bbox->start_offset, 0, "empty string start_offset");
261 is($bbox->end_offset, 0, "empty string end_offset");
262 is($bbox->advance_width, 0, "empty string advance_width");
263 is($bbox->ascent, 0, "empty string ascent");
264 is($bbox->descent, 0, "empty string descent");
267 my $bbox_space = $font->bounding_box(string => " ", size => 36);
268 print "# space bbox: @$bbox_space\n";
269 is($bbox_space->start_offset, 0, "single space start_offset");
270 is($bbox_space->end_offset, $bbox_space->advance_width,
271 "single space end_offset");
272 cmp_ok($bbox_space->ascent, '>=', $bbox_space->descent,
273 "single space ascent/descent");
275 my $bbox_bang = $font->bounding_box(string => "!", size => 36);
276 print "# '!' bbox: @$bbox_bang\n";
279 my $bbox_spbangsp = $font->bounding_box(string => " ! ", size => 36);
280 print "# ' ! ' bbox: @$bbox_spbangsp\n";
281 my $exp_advance = $bbox_bang->advance_width + 2 * $bbox_space->advance_width;
282 is($bbox_spbangsp->advance_width, $exp_advance, "sp ! sp advance_width");
283 is($bbox_spbangsp->start_offset, 0, "sp ! sp start_offset");
284 is($bbox_spbangsp->end_offset, $exp_advance, "sp ! sp end_offset");
288 { # http://rt.cpan.org/Ticket/Display.html?id=20554
289 # this is "A\xA1\x{2010}A"
290 # the t1 driver is meant to ignore any UTF8 characters over 0xff
291 print "# issue 20554\n";
292 my $text = pack("C*", 0x41, 0xC2, 0xA1, 0xE2, 0x80, 0x90, 0x41);
293 my $tran_text = "A\xA1A";
294 my $font = Imager::Font->new(file => 'fontfiles/dcr10.pfb', type => 't1');
296 or skip("cannot load font fontfiles/fcr10.pfb:".Imager->errstr, 1);
297 my $bbox_utf8 = $font->bounding_box(string => $text, utf8 => 1, size => 36);
298 my $bbox_tran = $font->bounding_box(string => $tran_text, size => 36);
299 is($bbox_utf8->advance_width, $bbox_tran->advance_width,
300 "advance widths should match");
302 { # string output cut off at NUL ('\0')
303 # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
304 my $font = Imager::Font->new(file => 'fontfiles/dcr10.pfb', type => 't1');
305 ok($font, "loaded dcr10.pfb");
307 diff_text_with_nul("a\\0b vs a", "a\0b", "a",
308 font => $font, color => '#FFFFFF');
309 diff_text_with_nul("a\\0b vs a", "a\0b", "a",
310 font => $font, channel => 1);
313 my $pound = pack("C*", 0xC2, 0xBF);
314 diff_text_with_nul("utf8 pound\0pound vs pound", "$pound\0$pound", $pound,
315 font => $font, color => '#FFFFFF', utf8 => 1);
316 diff_text_with_nul("utf8 dash\0dash vs dash", "$pound\0$pound", $pound,
317 font => $font, channel => 1, utf8 => 1);
322 # when rendering to a transparent image the coverage should be
323 # expressed in terms of the alpha channel rather than the color
324 my $font = Imager::Font->new(file=>'fontfiles/dcr10.pfb', type=>'t1');
325 my $im = Imager->new(xsize => 40, ysize => 20, channels => 4);
326 ok($im->string(string => "AB", size => 20, aa => 2, color => '#F00',
327 x => 0, y => 15, font => $font),
328 "draw to transparent image");
329 my $im_noalpha = $im->convert(preset => 'noalpha');
330 my $im_pal = $im->to_paletted(make_colors => 'mediancut');
331 my @colors = $im_pal->getcolors;
332 is(@colors, 2, "should be only 2 colors");
333 @colors = sort { ($a->rgba)[0] <=> ($b->rgba)[0] } @colors;
334 is_color3($colors[0], 0, 0, 0, "check we got black");
335 is_color3($colors[1], 255, 0, 0, "and red");
340 # checks that a c:foo or c:\foo path is handled correctly on win32
342 $^O eq "MSWin32" || $^O eq "cygwin"
343 or skip("only for win32", 2);
345 or skip("Cannot get cwd", 2);
346 if ($^O eq "cygwin") {
347 $dir = Cygwin::posix_to_win_path($dir);
349 my $abs_path = abs_path($deffont);
350 my $font = Imager::Font->new(file => $abs_path, type => $type);
351 ok($font, "found font by absolute path")
352 or print "# path $abs_path\n";
356 and skip("cygwin doesn't support drive relative DOSsish paths", 1);
357 my ($drive) = $dir =~ /^([a-z]:)/i
358 or skip("cwd has no drive letter", 2);
359 my $drive_path = $drive . $deffont;
360 $font = Imager::Font->new(file => $drive_path, type => $type);
361 ok($font, "found font by drive relative path")
362 or print "# path $drive_path\n";
366 Imager->log("Testing aa levels", 1);
367 my $f1 = Imager::Font->new(file => $deffont, type => "t1");
368 is($f1->{t1aa}, 2, "should have default aa level");
369 my $imbase = Imager->new(xsize => 100, ysize => 20);
370 ok($imbase->string(text => "test", size => 18, x => 5, y => 18,
371 color => "#FFF", font => $f1, aa => 1),
372 "draw text with def aa level");
373 ok(Imager::Font::T1->set_aa_level(1), "set aa level to 1");
374 my $f2 = Imager::Font->new(file => $deffont, type => "t1");
375 is($f2->{t1aa}, 1, "new font has new aa level");
376 my $imaa1 = Imager->new(xsize => 100, ysize => 20);
377 ok($imaa1->string(text => "test", size => 18, x => 5, y => 18,
378 color => "#FFF", font => $f2, aa => 1),
379 "draw text with non-def aa level");
380 isnt_image($imbase, $imaa1, "images should differ");
381 ok($f2->set_aa_level(2), "set aa level of font");
382 is($f2->{t1aa}, 2, "check new aa level");
383 my $imaa2 = Imager->new(xsize => 100, ysize => 20);
384 ok($imaa2->string(text => "test", size => 18, x => 5, y => 18,
385 color => "#FFF", font => $f2, aa => 1),
386 "draw text with non-def but 2 aa level");
387 is_image($imbase, $imaa2, "check images match");
390 { # error handling check
391 my $im = Imager->new(xsize => 100, ysize => 20);
392 my $fnum = Imager::Font->new(file => $deffont, type => "t1");
393 ok(!$im->string(font => $fnum, string => "text", size => -10),
395 is($im->errstr, "i_t1_text(): T1_AASetString failed: Invalid Argument in Function Call",
396 "check error message");
400 { # check magic is handled correctly
401 # https://rt.cpan.org/Ticket/Display.html?id=83438
402 skip("no native UTF8 support in this version of perl", 10)
404 my $font = Imager::Font->new(file=>$deffont, type=>'t1');
405 ok($font, "loaded deffont OO")
406 or skip("could not load font:".Imager->errstr, 4);
407 Imager->log("utf8 magic tests\n");
408 my $over = bless {}, "OverUtf8";
409 my $text = chr(0x2010)."A";
410 my $white = Imager::Color->new("#FFF");
411 my $base_draw = Imager->new(xsize => 80, ysize => 20);
412 ok($base_draw->string(font => $font,
419 "magic: make a base image");
420 my $test_draw = Imager->new(xsize => 80, ysize => 20);
421 ok($test_draw->string(font => $font,
428 "magic: draw with overload");
429 is_image($base_draw, $test_draw, "check they match");
430 $test_draw->write(file => "testout/utf8tdr.ppm");
431 $base_draw->write(file => "testout/utf8bdr.ppm");
433 my $base_cp = Imager->new(xsize => 80, ysize => 20);
434 $base_cp->box(filled => 1, color => "#808080");
435 my $test_cp = $base_cp->copy;
436 ok($base_cp->string(font => $font,
443 "magic: make a base image (channel)");
444 Imager->log("magic: draw to channel with overload\n");
445 ok($test_cp->string(font => $font,
452 "magic: draw with overload (channel)");
453 is_image($test_cp, $base_cp, "check they match");
454 #$test_cp->write(file => "testout/utf8tcp.ppm");
455 #$base_cp->write(file => "testout/utf8bcp.ppm");
457 Imager->log("magic: has_chars");
458 is_deeply([ $font->has_chars(string => $text) ], [ '', 1 ],
459 "magic: has_chars with normal utf8 text");
460 is_deeply([ $font->has_chars(string => $over) ], [ '', 1 ],
461 "magic: has_chars with magic utf8 text");
463 Imager->log("magic: bounding_box\n");
464 my @base_bb = $font->bounding_box(string => $text, size => 30);
465 is_deeply([ $font->bounding_box(string => $over, size => 30) ],
467 "check bounding box magic");
471 Imager->log("magic: glyph_names\n");
472 is_deeply([ $font->glyph_names(string => $text, reliable_only => 0) ],
474 "magic: glyph_names with normal utf8 text");
475 is_deeply([ $font->glyph_names(string => $over, reliable_only => 0) ],
477 "magic: glyph_names with magic utf8 text");
486 use overload '""' => sub { chr(0x2010)."A" };