- the FT2 glyph_names() method didn't do correct error handling
authorTony Cook <tony@develop=help.com>
Mon, 18 Oct 2004 05:34:03 +0000 (05:34 +0000)
committerTony Cook <tony@develop=help.com>
Mon, 18 Oct 2004 05:34:03 +0000 (05:34 +0000)
  when the string parameter wasn't supplied
- i_ft2_glyph_name() accepted only an unsigned char as the
  character code to get the name for, which meant it
  didn't work for unicode characters \x{100} or above
- the XS for i_ft2_glyph_name() had a similar problem
- added NameTest.ttf to be used in checking unicode glyph
  names
- added reliable_only optional parameter to the glyph_names()
  method so you can ignore theresult of FT_Has_PS_Glyph_Names()
- partly resolves https://rt.cpan.org/Ticket/Display.html?id=7949
- handle errors given by i_ft2_glyph_name() a bit more
  correctly
- the FT1 glyph_names() method didn't do correct error handling
  when the string parameter wasn't supplied
- some memory allocated when creating a callback IO object (io_new_cb)
  wasn't being released (detected with valgrind)
- the testtools.pl match[nx]() functions escapes the test string on
  test failure a bit better

12 files changed:
Changes
Imager.xs
MANIFEST
fontfiles/NameTest.sfd [new file with mode: 0644]
fontfiles/NameTest.ttf [new file with mode: 0644]
freetyp2.c
image.h
lib/Imager/Font.pm
lib/Imager/Font/FreeType2.pm
lib/Imager/Font/Truetype.pm
t/t38ft2font.t
t/testtools.pl

diff --git a/Changes b/Changes
index 24edb66..967dbd1 100644 (file)
--- a/Changes
+++ b/Changes
@@ -848,11 +848,30 @@ Revision history for Perl extension Imager.
 
 - the changes to scale() had some problems with integer vs floating point
   calculations (only caught in tests under perl 5.8.5 <sigh>)
+- the FT2 glyph_names() method didn't do correct error handling
+  when the string parameter wasn't supplied
+- i_ft2_glyph_name() accepted only an unsigned char as the
+  character code to get the name for, which meant it
+  didn't work for unicode characters \x{100} or above
+- the XS for i_ft2_glyph_name() had a similar problem
+- added NameTest.ttf to be used in checking unicode glyph
+  names
+- added reliable_only optional parameter to the glyph_names()
+  method so you can ignore theresult of FT_Has_PS_Glyph_Names()
+- partly resolves https://rt.cpan.org/Ticket/Display.html?id=7949
+- handle errors given by i_ft2_glyph_name() a bit more
+  correctly
+- the FT1 glyph_names() method didn't do correct error handling 
+  when the string parameter wasn't supplied
+- some memory allocated when creating a callback IO object (io_new_cb)
+  wasn't being released (detected with valgrind)
+- the testtools.pl match[nx]() functions escapes the test string on 
+  test failure a bit better
 
 =================================================================
 
         For latest versions check the Imager-devel pages:
-        http://imager.perl.org/~addi/perl/Imager/
+        http://imager.perl.org/
 
 =================================================================
 
index 2fad7b0..2e5baba 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -493,6 +493,7 @@ static void io_destroyer(void *p) {
   SvREFCNT_dec(cbd->readcb);
   SvREFCNT_dec(cbd->seekcb);
   SvREFCNT_dec(cbd->closecb);
+  myfree(cbd);
 }
 
 struct value_name {
@@ -4265,10 +4266,11 @@ undef_int
 i_ft2_can_face_name()
 
 void
-i_ft2_glyph_name(handle, text_sv, utf8 = 0)
+i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
         Imager::Font::FT2 handle
         SV *text_sv
         int utf8
+        int reliable_only
       PREINIT:
         char const *text;
         STRLEN work_len;
@@ -4283,7 +4285,7 @@ i_ft2_glyph_name(handle, text_sv, utf8 = 0)
         text = SvPV(text_sv, work_len);
         len = work_len;
         while (len) {
-          unsigned char ch;
+          unsigned long ch;
           if (utf8) {
             ch = i_utf8_advance(&text, &len);
             if (ch == ~0UL) {
@@ -4296,7 +4298,8 @@ i_ft2_glyph_name(handle, text_sv, utf8 = 0)
             --len;
           }
           EXTEND(SP, 1);
-          if (outsize = i_ft2_glyph_name(handle, ch, name, sizeof(name))) {
+          if (outsize = i_ft2_glyph_name(handle, ch, name, sizeof(name), 
+                                         reliable_only)) {
             PUSHs(sv_2mortal(newSVpv(name, 0)));
           }
           else {
index c26cfdf..741fa4d 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -56,6 +56,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/NameTest.ttf          test glyph_names() - see t38ft2font.t
 fontfiles/dcr10.afm
 fontfiles/dcr10.pfb
 fontfiles/dodge.ttf
diff --git a/fontfiles/NameTest.sfd b/fontfiles/NameTest.sfd
new file mode 100644 (file)
index 0000000..a9ff6c7
--- /dev/null
@@ -0,0 +1,72 @@
+SplineFontDB: 1.0
+FontName: NameTest
+FullName: NameTest
+FamilyName: NameTest
+Weight: Medium
+Copyright: Created by Tony Cook - test font for the glyph_names() method
+Comments: 2004-10-13: Created.
+Version: 001.000
+ItalicAngle: 0
+UnderlinePosition: -100
+UnderlineWidth: 50
+Ascent: 800
+Descent: 200
+XUID: [1021 123 2012976297 155577]
+FSType: 0
+OS2WinAscent: 0
+OS2WinAOffset: 1
+OS2WinDescent: 0
+OS2WinDOffset: 1
+HheadAscent: 0
+HheadAOffset: 0
+HheadDescent: 0
+HheadDOffset: 0
+ScriptLang: 1
+ 1 latn 1 dflt 
+TtfTable: cvt  4
+!$MDh
+EndTtf
+Encoding: Custom
+UnicodeInterp: none
+DisplaySize: -24
+AntiAlias: 1
+FitToEm: 1
+WinInfo: 0 21 12
+BeginChars: 65536 2
+StartChar: exclam
+Encoding: 33 33 65535
+Width: 295
+Flags: W
+HStem: 30 60<141 143>
+Fore
+111 60 m 0
+ 111 76.5596 124.44 90 141 90 c 0
+ 157.56 90 171 76.5596 171 60 c 0
+ 171 43.4404 157.56 30 141 30 c 0
+ 124.44 30 111 43.4404 111 60 c 0
+165 171 m 2
+ 165 157.2 153.8 146 140 146 c 0
+ 126.2 146 115 157.2 115 171 c 0
+ 115 228.6 115 728.006 115 747 c 0
+ 115 760.8 126.2 772 140 772 c 0
+ 153.8 772 165 760.8 165 747 c 2
+ 165 171 l 2
+EndSplineSet
+EndChar
+StartChar: hyphentwo
+Encoding: 8208 8208 65535
+Width: 487
+Flags: W
+HStem: 308 50<114 378>
+Fore
+372 358 m 2
+ 385.8 358 397 346.8 397 333 c 0
+ 397 319.2 385.8 308 372 308 c 0
+ 346.2 308 122.395 308 114 308 c 0
+ 100.2 308 89 319.2 89 333 c 0
+ 89 346.8 100.2 358 114 358 c 2
+ 372 358 l 2
+EndSplineSet
+EndChar
+EndChars
+EndSplineFont
diff --git a/fontfiles/NameTest.ttf b/fontfiles/NameTest.ttf
new file mode 100644 (file)
index 0000000..a28723a
Binary files /dev/null and b/fontfiles/NameTest.ttf differ
index 8d202d5..268c133 100644 (file)
@@ -944,8 +944,8 @@ i_ft2_can_face_name(void) {
 #endif
 
 int
-i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch, char *name_buf, 
-                 size_t name_buf_size) {
+i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf, 
+                 size_t name_buf_size, int reliable_only) {
 #ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
   i_clear_error();
   *name_buf = '\0';
@@ -953,35 +953,41 @@ i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch, char *name_buf,
 
   return 0;
 #else
+  FT_UInt index;
+
   i_clear_error();
 
-  if (FT_Has_PS_Glyph_Names(handle->face)) {
-    FT_UInt index = FT_Get_Char_Index(handle->face, ch);
+  if (!FT_HAS_GLYPH_NAMES(handle->face)) {
+    i_push_error(0, "no glyph names in font");
+    *name_buf = '\0';
+    return 0;
+  }
+  if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) {
+    i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway");
+    *name_buf = '\0';
+    return 0;
+  }
 
-    if (index) {
-      FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
-                                         name_buf_size);
-      if (error) {
-        ft2_push_message(error);
-        *name_buf = '\0';
-        return;
-      }
-      if (*name_buf) {
-        return strlen(name_buf) + 1;
-      }
-      else {
-        return 0;
-      }
+  index = FT_Get_Char_Index(handle->face, ch);
+  
+  if (index) {
+    FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf, 
+                                       name_buf_size);
+    if (error) {
+      ft2_push_message(error);
+      *name_buf = '\0';
+      return;
+    }
+    if (*name_buf) {
+      return strlen(name_buf) + 1;
     }
     else {
-      i_push_error(0, "no glyph for that character");
-      *name_buf = 0;
       return 0;
     }
   }
   else {
-    i_push_error(0, "no glyph names in font");
-    *name_buf = '\0';
+    i_push_error(0, "no glyph for that character");
+    *name_buf = 0;
     return 0;
   }
 #endif
diff --git a/image.h b/image.h
index 97628cb..87912b2 100644 (file)
--- a/image.h
+++ b/image.h
@@ -308,8 +308,9 @@ extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len,
 extern int i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, 
                            size_t name_buf_size);
 extern int i_ft2_can_face_name(void);
-extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch, 
-                            char *name_buf, size_t name_buf_size);
+extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, 
+                            char *name_buf, size_t name_buf_size,
+                            int reliable_only);
 extern int i_ft2_can_do_glyph_names(void);
 extern int i_ft2_face_has_glyph_names(FT2_Fonthandle *handle);
 
index b21ae81..710063f 100644 (file)
@@ -700,7 +700,7 @@ Imager::Font->new(file=>"arial.ttf", color=>$blue, aa=>1)
 Returns the internal name of the face.  Not all font types support
 this method yet.
 
-=item glyph_names(string=>$string [, utf8=>$utf8 ] );
+=item glyph_names(string=>$string [, utf8=>$utf8 ][, reliable_only=>0 ] );
 
 Returns a list of glyph names for each of the characters in the
 string.  If the character has no name then C<undef> is returned for
@@ -710,6 +710,13 @@ Some font files do not include glyph names, in this case Freetype 2
 will not return any names.  Freetype 1 can return standard names even
 if there are no glyph names in the font.
 
+Freetype 2 has an API function that returns true only if the font has
+"reliable glyph names", unfortunately this always returns false for
+TTF fonts.  This can avoid the check of this API by supplying
+C<reliable_only> as 0.  The consequences of using this on an unknown
+font may be unpredictable, since the Freetype documentation doesn't
+say how those name tables are unreliable, or how FT2 handles them.
+
 Both Freetype 1.x and 2.x allow support for glyph names to not be
 included.
 
index 9b1bbb4..9471e50 100644 (file)
@@ -132,10 +132,15 @@ sub glyph_names {
 
   my $string = $input{string};
   defined $string
-    or return Imager->_seterror("no string parameter passed to glyph_names");
-  my $utf8 = _first($input{utf8} || 0);
+    or return Imager->_set_error("no string parameter passed to glyph_names");
+  my $utf8 = _first($input{utf8}, 0);
+  my $reliable_only = _first($input{reliable_only}, 1);
 
-  i_ft2_glyph_name($self->{id}, $string, $utf8);
+  my @names = i_ft2_glyph_name($self->{id}, $string, $utf8, $reliable_only);
+  @names or return Imager->_set_error(Imager->_error_as_msg);
+
+  return @names if wantarray;
+  return pop @names;
 }
 
 1;
index ebb5ce6..ca147c4 100644 (file)
@@ -92,7 +92,7 @@ sub glyph_names {
 
   my $string = $input{string};
   defined $string
-    or return Imager->_seterror("no string parameter passed to glyph_names");
+    or return Imager->_set_error("no string parameter passed to glyph_names");
   my $utf8 = _first($input{utf8} || 0);
 
   Imager::i_tt_glyph_name($self->{id}, $string, $utf8);
index 3f3e510..067d5d9 100644 (file)
@@ -7,7 +7,7 @@
 # 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..116\n"; }
+BEGIN { $| = 1; print "1..120\n"; }
 END {print "not ok 1\n" unless $loaded;}
 use Imager qw(:all);
 
@@ -264,7 +264,8 @@ else {
 }
 
 if (Imager::Font::FreeType2->can_glyph_names) {
-  # pfaedit doesn't seem to save glyph names into TTF files
+  # FT2 considers POST tables in TTF fonts unreliable, so use
+  # a type 1 font, see below for TTF test 
   my $exfont = Imager::Font->new(file=>'fontfiles/ExistenceTest.pfb',
                                type=>'ft2');
   if (okx($exfont, "load Type 1 via FT2")) {
@@ -272,22 +273,51 @@ if (Imager::Font::FreeType2->can_glyph_names) {
       Imager::Font::FreeType2::i_ft2_glyph_name($exfont->{id}, "!J/");
     #use Data::Dumper;
     #print Dumper \@glyph_names;
-    okx($glyph_names[0] eq 'exclam', "check exclam name");
+    isx($glyph_names[0], 'exclam', "check exclam name");
     okx(!defined($glyph_names[1]), "check for no J name");
-    okx($glyph_names[2] eq 'slash', "check slash name");
+    isx($glyph_names[2], 'slash', "check slash name");
 
     # oo interfaces
     @glyph_names = $exfont->glyph_names(string=>"!J/");
-    okx($glyph_names[0] eq 'exclam', "check exclam name OO");
+    isx($glyph_names[0], 'exclam', "check exclam name OO");
     okx(!defined($glyph_names[1]), "check for no J name OO");
-    okx($glyph_names[2] eq 'slash', "check slash name OO");
+    isx($glyph_names[2], 'slash', "check slash name OO");
+
+    # make sure a missing string parameter is handled correctly
+    eval {
+      $exfont->glyph_names();
+    };
+    isx($@, "", "correct error handling");
+    matchx(Imager->errstr, qr/no string parameter/, "error message");
   }
   else {
-    skipx(6, "couldn't load type 1 with FT2");
+    skipx(8, "couldn't load type 1 with FT2");
+  }
+
+  # freetype 2 considers truetype glyph name tables unreliable
+  # due to some specific fonts, supplying reliable_only=>0 bypasses
+  # that check and lets us get the glyph names even for truetype fonts
+  # so we can test this stuff <sigh>
+  # we can't use ExistenceTest.ttf since that's generated with 
+  # AppleStandardEncoding since the same .sfd needs to generate
+  # a .pfb file, NameTest.ttf uses a Unicode encoding
+
+  # we were using an unsigned char to store a unicode character
+  # https://rt.cpan.org/Ticket/Display.html?id=7949
+  $exfont = Imager::Font->new(file=>'fontfiles/NameTest.ttf',
+                               type=>'ft2');
+  if (okx($exfont, "load TTF via FT2")) {
+    my $text = pack("C*", 0xE2, 0x80, 0x90); # "\x{2010}" as utf-8
+    my @names = $exfont->glyph_names(string=>$text,
+                                     utf8=>1, reliable_only=>0);
+    isx($names[0], "hyphentwo", "check utf8 glyph name");
+  }
+  else {
+    skipx(1, "could not load TTF with FT2");
   }
 }
 else {
-  skipx(7, "FT2 compiled without glyph names support");
+  skipx(9, "FT2 compiled without glyph names support");
 }
 
 sub align_test {
index 9821c1a..d5b2a24 100644 (file)
@@ -85,13 +85,11 @@ sub requireokx {
 sub matchn($$$$) {
   my ($num, $str, $re, $comment) = @_;
 
-  my $match = $str =~ $re;
+  my $match = defined($str) && $str =~ $re;
   okn($num, $match, $comment);
   unless ($match) {
-    $str =~ s/\\/\\\\/g;
-    $str =~ s/[^\x20-\x7E]/"\\x".sprintf("%02X", ord($1))/ge;
-    print "# The string '$str'\n";
-    print "# did not match '$re'\n";
+    print "# The value: ",_sv_str($str),"\n";
+    print "# did not match: qr/$re/\n";
   }
   return $match;
 }