]> git.imager.perl.org - imager.git/commitdiff
- added trivial multiple master support via the FT2 driver
authorTony Cook <tony@develop=help.com>
Sat, 1 Jan 2005 13:18:03 +0000 (13:18 +0000)
committerTony Cook <tony@develop=help.com>
Sat, 1 Jan 2005 13:18:03 +0000 (13:18 +0000)
Changes
Imager.xs
MANIFEST
freetyp2.c
image.h
lib/Imager/Font.pm
lib/Imager/Font/FreeType2.pm
t/t38ft2font.t

diff --git a/Changes b/Changes
index 29112ae167b03c44ba4757647b4b387710a97d17..c74a5f0329ef8e4fa66ca39269f838b6536483ad 100644 (file)
--- a/Changes
+++ b/Changes
@@ -977,6 +977,7 @@ Revision history for Perl extension Imager.
 - 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
 
 =================================================================
 
index d1eb260f321fcc03aae1a69c0b3c39ddebde1419..2827069097b578c8c24b06c8ad7ac96919fc294e 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -4314,6 +4314,58 @@ int
 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_
index f4fb9dfda4145d52785350ebaf86d221e26509fe..4529c889ff948098ebc3257dd61765f37eb18c78 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -40,6 +40,7 @@ fontfiles/ExistenceTest.afm     please edit ExistenceTest.sfd in CVS
 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
index f68ab73fea5b37cde43da1febeb56b79050a9272..f2cb6fca586508b3de7b8b179dc6b294697858ea 100644 (file)
@@ -38,6 +38,12 @@ Truetype, Type1 and Windows FNT.
 #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);
 
@@ -74,6 +80,12 @@ struct FT2_Fonthandle {
 
   /* 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 */
@@ -166,6 +178,27 @@ i_ft2_new(char *name, int index) {
   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;
 }
 
@@ -1011,6 +1044,77 @@ i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
 #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
 
diff --git a/image.h b/image.h
index 07c7a73d66548ba6f8b3ca78c8ca73eb5202bde4..2f7046acb255896fa1bf1fb2f9543f49e0b57827 100644 (file)
--- a/image.h
+++ b/image.h
@@ -245,6 +245,30 @@ float i_img_diff   (i_img *im1,i_img *im2);
 
 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>
 
@@ -316,6 +340,12 @@ extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch,
 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
index a101d98e9bc76bfe569fb38c07bb25964080a8d9..c0f388c7da625359f806f9e61b53be0d0da418a7 100644 (file)
@@ -727,6 +727,54 @@ included.
 
 =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:
index 9471e504107d85641709dfdc5cb25953eab3cea7..d480209439cc06b8d639a16524a6429b8aa2c2ce 100644 (file)
@@ -143,6 +143,36 @@ sub glyph_names {
   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__
index 5f33a25c231e69aa2badbcbed6614c02c03b58ba..9f4947930a8ba0f884e5306b6c5e2f0d7dfc9f87 100644 (file)
@@ -7,18 +7,18 @@
 # 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";
@@ -26,7 +26,7 @@ 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;
 }
@@ -320,6 +320,55 @@ else {
   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) = @_;