- added concept index to Imager.pm's POD
- the gradgen filter now does the same color value conversion that
Imager's drawing functions do.
+- added trivial multiple master support via the FT2 driver
=================================================================
i_ft2_face_has_glyph_names(handle)
Imager::Font::FT2 handle
+int
+i_ft2_is_multiple_master(handle)
+ Imager::Font::FT2 handle
+
+void
+i_ft2_get_multiple_masters(handle)
+ Imager::Font::FT2 handle
+ PREINIT:
+ i_font_mm mm;
+ int i;
+ PPCODE:
+ if (i_ft2_get_multiple_masters(handle, &mm)) {
+ EXTEND(SP, 2+mm.num_axis);
+ PUSHs(sv_2mortal(newSViv(mm.num_axis)));
+ PUSHs(sv_2mortal(newSViv(mm.num_designs)));
+ for (i = 0; i < mm.num_axis; ++i) {
+ AV *av = newAV();
+ SV *sv;
+ av_extend(av, 3);
+ sv = newSVpv(mm.axis[i].name, strlen(mm.axis[i].name));
+ SvREFCNT_inc(sv);
+ av_store(av, 0, sv);
+ sv = newSViv(mm.axis[i].minimum);
+ SvREFCNT_inc(sv);
+ av_store(av, 1, sv);
+ sv = newSViv(mm.axis[i].maximum);
+ SvREFCNT_inc(sv);
+ av_store(av, 2, sv);
+ PUSHs(newRV_noinc((SV *)av));
+ }
+ }
+
+undef_int
+i_ft2_set_mm_coords(handle, ...)
+ Imager::Font::FT2 handle
+ PROTOTYPE: DISABLE
+ PREINIT:
+ long *coords;
+ int ix_coords, i;
+ CODE:
+ /* T_ARRAY handling by xsubpp seems to be busted in 5.6.1, so
+ transfer the array manually */
+ ix_coords = items-1;
+ coords = mymalloc(sizeof(long) * ix_coords);
+ for (i = 0; i < ix_coords; ++i) {
+ coords[i] = (long)SvIV(ST(1+i));
+ }
+ RETVAL = i_ft2_set_mm_coords(handle, ix_coords, coords);
+ myfree(coords);
+ OUTPUT:
+ RETVAL
+
#endif
MODULE = Imager PACKAGE = Imager::FillHandle PREFIX=IFILL_
fontfiles/ExistenceTest.pfb to change these files, edited and
fontfiles/ExistenceTest.ttf generated using pfaedit
fontfiles/ImUgly.ttf
+fontfiles/MMOne.pfb multiple master test font
fontfiles/NameTest.ttf test glyph_names() - see t38ft2font.t
fontfiles/dcr10.afm
fontfiles/dcr10.pfb
#include <stdio.h>
#include <ft2build.h>
#include FT_FREETYPE_H
+#ifdef FT_MULTIPLE_MASTERS_H
+#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT
+#define IM_FT2_MM
+#include FT_MULTIPLE_MASTERS_H
+#endif
+#endif
static void ft2_push_message(int code);
/* used to adjust so we can align the draw point to the top-left */
double matrix[6];
+
+#ifdef IM_FT2_MM
+ /* Multiple master data if any */
+ int has_mm;
+ FT_Multi_Master mm;
+#endif
};
/* the following is used to select a "best" encoding */
result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0;
result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0;
+#ifdef IM_FT2_MM
+ {
+ FT_Multi_Master *mm = &result->mm;
+ int i;
+
+ if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0
+ && (error = FT_Get_Multi_Master(face, mm)) == 0) {
+ mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs));
+ for (i = 0; i < mm->num_axis; ++i) {
+ mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name,
+ (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum)));
+ }
+ result->has_mm = 1;
+ }
+ else {
+ mm_log((2, "No multiple masters\n"));
+ result->has_mm = 0;
+ }
+ }
+#endif
+
return result;
}
#endif
}
+int
+i_ft2_is_multiple_master(FT2_Fonthandle *handle) {
+ i_clear_error();
+#ifdef IM_FT2_MM
+ return handle->has_mm;
+#else
+ return 0;
+#endif
+}
+
+int
+i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) {
+#ifdef IM_FT2_MM
+ int i;
+ FT_Multi_Master *mms = &handle->mm;
+
+ i_clear_error();
+ if (!handle->has_mm) {
+ i_push_error(0, "Font has no multiple masters");
+ return 0;
+ }
+ mm->num_axis = mms->num_axis;
+ mm->num_designs = mms->num_designs;
+ for (i = 0; i < mms->num_axis; ++i) {
+ mm->axis[i].name = mms->axis[i].name;
+ mm->axis[i].minimum = mms->axis[i].minimum;
+ mm->axis[i].maximum = mms->axis[i].maximum;
+ }
+
+ return 1;
+#else
+ i_clear_error();
+ i_push_error(0, "Multiple master functions unavailable");
+ return 0;
+#endif
+}
+
+int
+i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, long *coords) {
+#ifdef IM_FT2_MM
+ int i;
+ FT_Long ftcoords[T1_MAX_MM_AXIS];
+ FT_Error error;
+
+ i_clear_error();
+ if (!handle->has_mm) {
+ i_push_error(0, "Font has no multiple masters");
+ return 0;
+ }
+ if (coord_count != handle->mm.num_axis) {
+ i_push_error(0, "Number of MM coords doesn't match MM axis count");
+ return 0;
+ }
+ for (i = 0; i < coord_count; ++i)
+ ftcoords[i] = coords[i];
+
+ error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords);
+ if (error) {
+ ft2_push_message(error);
+ return 0;
+ }
+
+ return 1;
+#else
+ i_clear_error();
+ i_push_error(0, "Multiple master functions unavailable");
+
+ return 0;
+#endif
+}
+
/*
=back
undef_int i_init_fonts( int t1log );
+/*
+ describes an axis of a MM font.
+ Modelled on FT2's FT_MM_Axis.
+ It would be nice to have a default entry too, but FT2
+ doesn't support it.
+*/
+typedef struct i_font_mm_axis_tag {
+ char const *name;
+ int minimum;
+ int maximum;
+} i_font_mm_axis;
+
+#define IM_FONT_MM_MAX_AXES 4
+
+/*
+ multiple master information for a font, if any
+ modelled on FT2's FT_Multi_Master.
+*/
+typedef struct i_font_mm_tag {
+ int num_axis;
+ int num_designs; /* provided but not necessarily useful */
+ i_font_mm_axis axis[IM_FONT_MM_MAX_AXES];
+} i_font_mm;
+
#ifdef HAVE_LIBT1
#include <t1lib.h>
extern int i_ft2_can_do_glyph_names(void);
extern int i_ft2_face_has_glyph_names(FT2_Fonthandle *handle);
+extern int i_ft2_get_multiple_masters(FT2_Fonthandle *handle,
+ i_font_mm *mm);
+extern int
+i_ft2_is_multiple_master(FT2_Fonthandle *handle);
+extern int
+i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, long *coords);
#endif
#ifdef WIN32
=back
+=head1 MULTIPLE MASTER FONTS
+
+The Freetype 2 driver supports multiple master fonts:
+
+=over
+
+=item is_mm()
+
+Test if the font is a multiple master font.
+
+=item mm_axes()
+
+Returns a list of the axes that can be changes in the font. Each
+entry is an array reference which contains:
+
+=over
+
+=item 1.
+
+Name of the axis.
+
+=item 2.
+
+minimum value for this axis.
+
+=item 3.
+
+maximum value for this axis
+
+=back
+
+=item set_mm_coords(coords=>\@values)
+
+Blends an interpolated design from the master fonts. @values must
+contain as many values as there are axes in the font.
+
+=back
+
+For example, to select the minimum value in each axis:
+
+ my @axes = $font->mm_axes;
+ my @coords = map $_->[1], @axes;
+ $font->set_mm_coords(coords=>\@coords);
+
+It's possible other drivers will support multiple master fonts in the
+future, check if your selected font object supports the is_mm() method
+using the can() method.
+
=head1 UTF8
There are 2 ways of rendering Unicode characters with Imager:
return pop @names;
}
+sub is_mm {
+ my ($self) = @_;
+
+ i_ft2_is_multiple_master($self->{id});
+}
+
+sub mm_axes {
+ my ($self) = @_;
+
+ my ($num_axis, $num_design, @axes) =
+ i_ft2_get_multiple_masters($self->{id})
+ or return Imager->_set_error(Imager->_error_as_msg);
+
+ return @axes;
+}
+
+sub set_mm_coords {
+ my ($self, %opts) = @_;
+
+ $opts{coords}
+ or return Imager->_set_error("Missing coords parameter");
+ ref($opts{coords}) && $opts{coords} =~ /ARRAY\(0x[\da-f]+\)$/
+ or return Imager->_set_error("coords parameter must be an ARRAY ref");
+
+ i_ft2_set_mm_coords($self->{id}, @{$opts{coords}})
+ or return Imager->_set_error(Imager->_error_as_msg);
+
+ return 1;
+}
+
1;
__END__
# Change 1..1 below to 1..last_test_to_print .
# (It may become useful if the test is moved to ./t subdirectory.)
-BEGIN { $| = 1; print "1..120\n"; }
+BEGIN { $| = 1; print "1..138\n"; }
END {print "not ok 1\n" unless $loaded;}
use Imager qw(:all);
-require "t/testtools.pl";
+BEGIN { require "t/testtools.pl"; }
$loaded = 1;
okx(1, "loaded");
init_log("testout/t38ft2font.log",2);
if (!(i_has_format("ft2")) ) {
- skipx(119, "No freetype2 library found");
+ skipx(137, "No freetype2 library found");
exit;
}
print "# has ft2\n";
$fontname=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf';
if (! -f $fontname) {
- skipx(119, "cannot find fontfile $fontname");
+ skipx(137, "cannot find fontfile $fontname");
malloc_state();
exit;
}
skipx(9, "FT2 compiled without glyph names support");
}
+# check that error codes are translated correctly
+my $errfont = Imager::Font->new(file=>"t/t38ft2font.t", type=>"ft2");
+isx($errfont, undef, "new font vs non font");
+matchx(Imager->errstr, qr/unknown file format/, "check error message");
+
+# Multiple Master tests
+# we check a non-MM font errors correctly
+print "# check that the methods act correctly for a non-MM font\n";
+okx(!$exfont->is_mm, "exfont not MM");
+okx((() = $exfont->mm_axes) == 0, "exfont has no MM axes");
+matchx(Imager->errstr, qr/no multiple masters/,
+ "and returns correct error when we ask");
+okx(!$exfont->set_mm_coords(coords=>[0, 0]), "fail setting axis on exfont");
+matchx(Imager->errstr, qr/no multiple masters/,
+ "and returns correct error when we ask");
+
+# try a MM font now - test font only has A defined
+print "# Try a multiple master font\n";
+my $mmfont = Imager::Font->new(file=>"fontfiles/MMOne.pfb", type=>"ft2",
+ color=>"white", aa=>1, size=>60);
+okx($mmfont, "loaded MM font");
+okx($mmfont->is_mm, "font is multiple master");
+my @axes = $mmfont->mm_axes;
+isx(@axes, 2, "check we got both axes");
+isx($axes[0][0], "Weight", "name of first axis");
+isx($axes[0][1], 50, "min for first axis");
+isx($axes[0][2], 999, "max for first axis");
+isx($axes[1][0], "Slant", "name of second axis");
+isx($axes[1][1], 0, "min for second axis");
+isx($axes[1][2], 999, "max for second axis");
+my $mmim = Imager->new(xsize=>200, ysize=>200);
+$mmim->string(font=>$mmfont, x=>0, y=>50, text=>"A");
+okx($mmfont->set_mm_coords(coords=>[ 700, 0 ]), "set to bold, unsloped");
+$mmim->string(font=>$mmfont, x=>0, y=>100, text=>"A", color=>'blue');
+my @weights = qw(50 260 525 760 999);
+my @slants = qw(0 333 666 999);
+for my $windex (0 .. $#weights) {
+ my $weight = $weights[$windex];
+ for my $sindex (0 .. $#slants) {
+ my $slant = $slants[$sindex];
+ $mmfont->set_mm_coords(coords=>[ $weight, $slant ]);
+ $mmim->string(font=>$mmfont, x=>30+32*$windex, 'y'=>50+45*$sindex,
+ text=>"A");
+ }
+}
+
+okx($mmim->write(file=>"testout/t38mm.ppm"), "save MM output");
+
+
sub align_test {
my ($h, $v, $x, $y, $f, $img) = @_;