move win32 font support in a new directory, and use Imager::Probe
authorTony Cook <tony@develop=help.com>
Thu, 16 Sep 2010 12:34:38 +0000 (12:34 +0000)
committerTony Cook <tony@develop=help.com>
Thu, 16 Sep 2010 12:34:38 +0000 (12:34 +0000)
change i_utf8_advance to accept the working length as a size_t and
propagate that change through anything that uses it

25 files changed:
Imager.pm
Imager.xs
MANIFEST
Makefile.PL
TIFF/TIFF.pm
W32/Makefile.PL [new file with mode: 0644]
W32/W32.pm [new file with mode: 0644]
W32/W32.xs [new file with mode: 0644]
W32/fontfiles/ExistenceTest.ttf [new file with mode: 0644]
W32/imw32.h [new file with mode: 0644]
W32/lib/Imager/Font/Win32.pm [new file with mode: 0644]
W32/t/t10win32.t [new file with mode: 0644]
W32/win32.c [new file with mode: 0644]
font.c
freetyp2.c
imager.h
imext.c
imext.h
imexttypes.h
imio.h
io.c
lib/Imager/APIRef.pod
lib/Imager/Font/Win32.pm [deleted file]
t/t37w32font.t [deleted file]
win32.c [deleted file]

index 080b790..7349469 100644 (file)
--- a/Imager.pm
+++ b/Imager.pm
@@ -174,6 +174,7 @@ my %format_classes =
    gif => "Imager::File::GIF",
    tiff => "Imager::File::TIFF",
    jpeg => "Imager::File::JPEG",
+   w32 => "Imager::Font::W32",
   );
 
 tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
@@ -3906,6 +3907,8 @@ sub _check {
 sub FETCH {
   my ($self, $key) = @_;
 
+  $DB::single = 1;
+
   exists $self->[IX_FORMATS]{$key} and return $self->[IX_FORMATS]{$key};
 
   $self->[IX_CLASSES]{$key} or return undef;
index d910b06..734a307 100644 (file)
--- a/Imager.xs
+++ b/Imager.xs
@@ -2129,7 +2129,7 @@ i_t1_glyph_name(handle, text_sv, utf8 = 0)
       PREINIT:
         char const *text;
         STRLEN work_len;
-        int len;
+        size_t len;
         char name[255];
       PPCODE:
 #ifdef SvUTF8
@@ -2323,7 +2323,7 @@ i_tt_glyph_name(handle, text_sv, utf8 = 0)
       PREINIT:
         char const *text;
         STRLEN work_len;
-        int len;
+        size_t len;
         int outsize;
         char name[255];
       PPCODE:
@@ -3808,92 +3808,6 @@ i_tags_count(im)
       OUTPUT:
         RETVAL
 
-#ifdef HAVE_WIN32
-
-void
-i_wf_bbox(face, size, text_sv, utf8=0)
-       char *face
-       int size
-       SV *text_sv
-       int utf8
-      PREINIT:
-       int cords[BOUNDING_BOX_COUNT];
-        int rc, i;
-       char const *text;
-         STRLEN text_len;
-      PPCODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-        if (rc = i_wf_bbox(face, size, text, text_len, cords, utf8)) {
-          EXTEND(SP, rc);  
-          for (i = 0; i < rc; ++i) 
-            PUSHs(sv_2mortal(newSViv(cords[i])));
-        }
-
-undef_int
-i_wf_text(face, im, tx, ty, cl, size, text_sv, align, aa, utf8 = 0)
-       char *face
-       Imager::ImgRaw im
-       int tx
-       int ty
-       Imager::Color cl
-       int size
-       SV *text_sv
-       int align
-       int aa
-       int utf8
-      PREINIT:
-       char const *text;
-       STRLEN text_len;
-      CODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-       RETVAL = i_wf_text(face, im, tx, ty, cl, size, text, text_len, 
-                          align, aa, utf8);
-      OUTPUT:
-       RETVAL
-
-undef_int
-i_wf_cp(face, im, tx, ty, channel, size, text_sv, align, aa, utf8 = 0)
-       char *face
-       Imager::ImgRaw im
-       int tx
-       int ty
-       int channel
-       int size
-       SV *text_sv
-       int align
-       int aa
-       int utf8
-      PREINIT:
-       char const *text;
-       STRLEN text_len;
-      CODE:
-        text = SvPV(text_sv, text_len);
-#ifdef SvUTF8
-        if (SvUTF8(text_sv))
-          utf8 = 1;
-#endif
-       RETVAL = i_wf_cp(face, im, tx, ty, channel, size, text, text_len, 
-                        align, aa, utf8);
-      OUTPUT:
-       RETVAL
-
-undef_int
-i_wf_addfont(font)
-        char *font
-
-undef_int
-i_wf_delfont(font)
-        char *font
-
-#endif
 
 #ifdef HAVE_FT2
 
@@ -4147,7 +4061,7 @@ i_ft2_glyph_name(handle, text_sv, utf8 = 0, reliable_only = 1)
       PREINIT:
         char const *text;
         STRLEN work_len;
-        int len;
+        size_t len;
         char name[255];
       PPCODE:
 #ifdef SvUTF8
index d6df085..1663c43 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -145,6 +145,14 @@ TIFF/testimg/srgba16.tif
 TIFF/testimg/srgba32.tif
 TIFF/testimg/srgbaa.tif        RGB with 2 alpha
 TIFF/testimg/tiffwarn.tif      Generates a warning while being read
+W32/fontfiles/ExistenceTest.ttf
+W32/imw32.h
+W32/lib/Imager/Font/Win32.pm
+W32/t/t10win32.t       Tests Win32 GDI font support
+W32/win32.c            Implements font support through Win32 GDI
+W32/Makefile.PL
+W32/W32.pm
+W32/W32.xs
 apidocs.perl    Build lib/Imager/APIRef.pm
 bigtest.perl   Library selection tester
 bmp.c           Reading and writing Windows BMP files
@@ -236,7 +244,6 @@ lib/Imager/Font/FreeType2.pm
 lib/Imager/Font/Image.pm
 lib/Imager/Font/Truetype.pm
 lib/Imager/Font/Type1.pm
-lib/Imager/Font/Win32.pm
 lib/Imager/Font/Wrap.pm
 lib/Imager/Fountain.pm
 lib/Imager/Handy.pod
@@ -322,7 +329,6 @@ t/t21draw.t             Basic drawing tests
 t/t30t1font.t
 t/t35ttfont.t
 t/t36oofont.t
-t/t37w32font.t         Tests Win32 GDI font support
 t/t38ft2font.t         Tests freetype2 support
 t/t40scale.t
 t/t50basicoo.t
@@ -424,4 +430,3 @@ tga.c           Reading and writing Targa files
 trans2.c
 transform.perl Shell interface to Imager::Transform
 typemap
-win32.c                Implements font support through Win32 GDI
index 4f4df4c..46a1e0d 100644 (file)
@@ -626,20 +626,20 @@ visual quality esp on low resultions. The freetype library is
 used to rasterize for us. The only drawback is that there
 are alot of badly designed fonts out there.}
                       };
-  $formats{'w32'} = {
-                    order=>40,
-                    def=>'HAVE_WIN32',
-                    inccheck=>sub { -e catfile($_[0], 'windows.h') },
-                    libcheck=>sub { lc $_[0] eq 'gdi32.lib' 
-                                      || lc $_[0] eq 'libgdi32.a' },
-                    libfiles=>$^O eq 'cygwin' ? '-lgdi32' : '',
-                    objfiles=>'win32.o',
-                    docs => <<DOCS
-Uses the Win32 GDI for rendering text.
-
-This currently only works on under normal Win32 and cygwin.
-DOCS
-                   };
+  $formats{'w32'} = {
+#                   order=>40,
+#                   def=>'HAVE_WIN32',
+#                   inccheck=>sub { -e catfile($_[0], 'windows.h') },
+#                   libcheck=>sub { lc $_[0] eq 'gdi32.lib' 
+#                                     || lc $_[0] eq 'libgdi32.a' },
+#                   libfiles=>$^O eq 'cygwin' ? '-lgdi32' : '',
+#                   objfiles=>'win32.o',
+#                   docs => <<DOCS
+Uses the Win32 GDI for rendering text.
+
+This currently only works on under normal Win32 and cygwin.
+DOCS
+#                  };
   $formats{'freetype2'} = 
   {
    order=>'29',
index cbc42e3..cc56e46 100644 (file)
@@ -11,7 +11,6 @@ BEGIN {
     XSLoader::load('Imager::File::TIFF', $VERSION);
     1;
   } or do {
-print STDERR "Falling back to DynaLoader ($@)\n";
     require DynaLoader;
     push @ISA, 'DynaLoader';
     bootstrap Imager::File::TIFF $VERSION;
diff --git a/W32/Makefile.PL b/W32/Makefile.PL
new file mode 100644 (file)
index 0000000..af63e26
--- /dev/null
@@ -0,0 +1,117 @@
+#!perl -w
+use strict;
+use ExtUtils::MakeMaker qw(WriteMakefile WriteEmptyMakefile);
+use Getopt::Long;
+use Config;
+
+my $verbose = $ENV{IM_VERBOSE};
+my @libpaths;
+my @incpaths;
+
+GetOptions("incpath=s", \@incpaths,
+           "libpath=s" => \@libpaths,
+           "verbose|v" => \$verbose);
+
+our $BUILDING_IMAGER;
+
+$DB::single = 1;
+
+my $MM_ver = eval $ExtUtils::MakeMaker::VERSION;
+
+my %opts = 
+  (
+   NAME => 'Imager::Font::W32',
+   VERSION_FROM => 'W32.pm',
+   OBJECT => 'W32.o win32.o',
+  );
+
+my @inc;
+if ($BUILDING_IMAGER) {
+  push @inc, "-I..";
+  unshift @INC, "../lib";
+}
+else {
+  unshift @INC, "inc";
+  print "Win32: building independently\n";
+  require Imager::ExtUtils;
+  push @inc, Imager::ExtUtils->includes;
+  $opts{TYPEMAPS} = [ Imager::ExtUtils->typemap ];
+
+  # Imager required configure through use
+  my @Imager_req = ( Imager => "0.77" );
+  if ($MM_ver >= 6.46) {
+    $opts{META_MERGE} =
+      {
+       configure_requires => 
+       {
+       @Imager_req,
+       },
+       build_requires => 
+       {
+       @Imager_req,
+       "Test::More" => "0.47",
+       },
+       resources =>
+       {
+       homepage => "http://imager.perl.org/",
+       repository =>
+       {
+        url => "http://imager.perl.org/svn/trunk/Imager-Font-W32",
+        web => "http://imager.perl.org/svnweb/public/browse/trunk/Imager-Font-W32",
+        type => "svn",
+       },
+       },
+      };
+    $opts{PREREQ_PM} =
+      {
+       @Imager_req,
+      };
+  }
+}
+
+require Imager::Probe;
+
+my %probe =
+  (
+   name => "Win32",
+   inccheck => sub { -e File::Spec->catfile($_[0], "windows.h") },
+   libbase => "gdi32",
+   testcode => _win32_test_code(),
+   testcodeheaders => [ "stdio.h", "string.h", "windows.h" ],
+   incpath => join($Config{path_sep}, @incpaths),
+   libpath => join($Config{path_sep}, @libpaths),
+  );
+
+my $probe_res = Imager::Probe->probe(\%probe);
+if ($probe_res) {
+  push @inc, $probe_res->{INC};
+  $opts{LIBS} = $probe_res->{LIBS};
+
+  $opts{INC} = "@inc";
+
+  if ($MM_ver > 6.06) {
+    $opts{AUTHOR} = 'Tony Cook <tony@imager.perl.org>';
+    $opts{ABSTRACT} = 'Win32 font file support for Imager';
+  }
+  
+  WriteMakefile(%opts);
+}
+else {
+  if ($BUILDING_IMAGER) {
+    WriteEmptyMakefile(%opts);
+  }
+  else {
+    # fail in good way
+    die "OS unsupported: Win32 libraries or headers not found\n";
+  }
+}
+
+sub _win32_test_code {
+  return <<'CODE';
+HDC dc = GetDC(NULL);
+HDC bmpDc = CreateCompatibleDC(dc);
+DeleteDC(bmpDc);
+ReleaseDC(NULL, dc);
+return 0;
+CODE
+}
diff --git a/W32/W32.pm b/W32/W32.pm
new file mode 100644 (file)
index 0000000..f27ebd5
--- /dev/null
@@ -0,0 +1,94 @@
+package Imager::Font::W32;
+use strict;
+use Imager;
+use vars qw($VERSION @ISA);
+@ISA = qw(Imager::Font);
+
+BEGIN {
+  $VERSION = "0.77";
+
+  eval {
+    require XSLoader;
+    XSLoader::load('Imager::Font::W32', $VERSION);
+    1;
+  } or do {
+    require DynaLoader;
+    push @ISA, 'DynaLoader';
+    bootstrap Imager::Font::W32 $VERSION;
+  };
+}
+
+# called by Imager::Font::new()
+# since Win32's HFONTs include the size information this
+# is just a stub
+sub new {
+  my ($class, %opts) = @_;
+
+  return bless \%opts, $class;
+}
+
+sub _bounding_box {
+  my ($self, %opts) = @_;
+  
+  my @bbox = i_wf_bbox($self->{face}, $opts{size}, $opts{string}, $opts{utf8});
+}
+
+sub _draw {
+  my $self = shift;
+
+  my %input = @_;
+  if (exists $input{channel}) {
+    i_wf_cp($self->{face}, $input{image}{IMG}, $input{x}, $input{'y'},
+           $input{channel}, $input{size},
+           $input{string}, $input{align}, $input{aa}, $input{utf8});
+  }
+  else {
+    i_wf_text($self->{face}, $input{image}{IMG}, $input{x}, 
+             $input{'y'}, $input{color}, $input{size}, 
+             $input{string}, $input{align}, $input{aa}, $input{utf8});
+  }
+}
+
+
+sub utf8 {
+  return 1;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+Imager::Font::W32 - font support using C<GDI> on Win32
+
+=head1 SYNOPSIS
+
+  use Imager;
+
+  my $img = Imager->new;
+  my $font = Imager::Font->new(face => "Arial", type => "w32");
+
+  $img->string(... font => $font);
+
+=head1 DESCRIPTION
+
+This provides font support on Win32.
+
+=head1 CAVEATS
+
+Unfortunately, older versions of Imager would install
+Imager::Font::Win32 even if Win32 wasn't available, and if no font was
+created would succeed in loading the module.  This means that an
+existing Win32.pm could cause a probe success for Win32 fonts, so I've
+renamed it.
+
+=head1 AUTHOR
+
+Tony Cook <tony@imager.perl.org>
+
+=head1 SEE ALSO
+
+Imager, Imager::Font.
+
+=cut
diff --git a/W32/W32.xs b/W32/W32.xs
new file mode 100644 (file)
index 0000000..9e0020d
--- /dev/null
@@ -0,0 +1,101 @@
+#define PERL_NO_GET_CONTEXT
+#ifdef __cplusplus
+extern "C" {
+#endif
+#include "EXTERN.h"
+#include "perl.h"
+#include "XSUB.h"
+#include "imext.h"
+#include "imperl.h"
+#include "imw32.h"
+
+DEFINE_IMAGER_CALLBACKS;
+
+MODULE = Imager::Font::W32  PACKAGE = Imager::Font::W32
+
+void
+i_wf_bbox(face, size, text_sv, utf8=0)
+       char *face
+       int size
+       SV *text_sv
+       int utf8
+      PREINIT:
+       int cords[BOUNDING_BOX_COUNT];
+        int rc, i;
+       char const *text;
+         STRLEN text_len;
+      PPCODE:
+        text = SvPV(text_sv, text_len);
+#ifdef SvUTF8
+        if (SvUTF8(text_sv))
+          utf8 = 1;
+#endif
+        if (rc = i_wf_bbox(face, size, text, text_len, cords, utf8)) {
+          EXTEND(SP, rc);  
+          for (i = 0; i < rc; ++i) 
+            PUSHs(sv_2mortal(newSViv(cords[i])));
+        }
+
+undef_int
+i_wf_text(face, im, tx, ty, cl, size, text_sv, align, aa, utf8 = 0)
+       char *face
+       Imager::ImgRaw im
+       int tx
+       int ty
+       Imager::Color cl
+       int size
+       SV *text_sv
+       int align
+       int aa
+       int utf8
+      PREINIT:
+       char const *text;
+       STRLEN text_len;
+      CODE:
+        text = SvPV(text_sv, text_len);
+#ifdef SvUTF8
+        if (SvUTF8(text_sv))
+          utf8 = 1;
+#endif
+       RETVAL = i_wf_text(face, im, tx, ty, cl, size, text, text_len, 
+                          align, aa, utf8);
+      OUTPUT:
+       RETVAL
+
+undef_int
+i_wf_cp(face, im, tx, ty, channel, size, text_sv, align, aa, utf8 = 0)
+       char *face
+       Imager::ImgRaw im
+       int tx
+       int ty
+       int channel
+       int size
+       SV *text_sv
+       int align
+       int aa
+       int utf8
+      PREINIT:
+       char const *text;
+       STRLEN text_len;
+      CODE:
+        text = SvPV(text_sv, text_len);
+#ifdef SvUTF8
+        if (SvUTF8(text_sv))
+          utf8 = 1;
+#endif
+       RETVAL = i_wf_cp(face, im, tx, ty, channel, size, text, text_len, 
+                        align, aa, utf8);
+      OUTPUT:
+       RETVAL
+
+undef_int
+i_wf_addfont(font)
+        char *font
+
+undef_int
+i_wf_delfont(font)
+        char *font
+
+
+BOOT:
+       PERL_INITIALIZE_IMAGER_CALLBACKS;
diff --git a/W32/fontfiles/ExistenceTest.ttf b/W32/fontfiles/ExistenceTest.ttf
new file mode 100644 (file)
index 0000000..a4a7e56
Binary files /dev/null and b/W32/fontfiles/ExistenceTest.ttf differ
diff --git a/W32/imw32.h b/W32/imw32.h
new file mode 100644 (file)
index 0000000..848aa54
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef IMAGER_IMW32_H
+#define IMAGER_IMW32_H
+
+#include "imdatatypes.h"
+
+extern int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox, int utf8);
+extern int i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, 
+                    int size, const char *text, int len, int align, int aa, int utf8);
+extern int i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, 
+                  int size, const char *text, int len, int align, int aa, int utf8);
+extern int i_wf_addfont(char const *file);
+extern int i_wf_delfont(char const *file);
+
+#endif
diff --git a/W32/lib/Imager/Font/Win32.pm b/W32/lib/Imager/Font/Win32.pm
new file mode 100644 (file)
index 0000000..8d1785b
--- /dev/null
@@ -0,0 +1,26 @@
+package Imager::Font::Win32;
+use strict;
+use vars qw(@ISA);
+@ISA = qw(Imager::Font::W32);
+
+require Imager::Font::W32;
+
+1;
+
+__END__
+
+=head1 NAME
+
+=for stopwords GDI
+
+Imager::Font::Win32 - uses Win32 GDI services for text output
+
+=head1 SYNOPSIS
+
+  my $font = Imager::Font->new(face=>"Arial");
+
+=head1 DESCRIPTION
+
+This module is obsolete.
+
+=cut
diff --git a/W32/t/t10win32.t b/W32/t/t10win32.t
new file mode 100644 (file)
index 0000000..ad3b363
--- /dev/null
@@ -0,0 +1,210 @@
+#!perl -w
+use strict;
+use Test::More tests => 55;
+use Imager qw(:all);
+use Imager::Test qw(diff_text_with_nul);
+++$|;
+
+ok(-d "testout" or mkdir "testout", "testout directory");
+
+ok($Imager::formats{w32}, "\$formats{w32} populated");
+
+init_log("testout/t10w32font.log",1);
+
+SKIP:
+{
+  print "# has w32\n";
+
+  my $fontname=$ENV{'TTFONTTEST'} || 'Times New Roman Bold';
+  
+  # i_init_fonts(); # unnecessary for Win32 font support
+
+  my $bgcolor=i_color_new(255,0,0,0);
+  my $overlay=Imager::ImgRaw::new(200,70,3);
+  
+  my @bbox=Imager::Font::W32::i_wf_bbox($fontname, 50.0,'XMCLH');
+  print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
+  
+  ok(Imager::Font::W32::i_wf_cp($fontname,$overlay,5,50,1,50.0,'XMCLH',1,1),
+     "i_wf_cp smoke test");
+  i_line($overlay,0,50,100,50,$bgcolor, 1);
+  
+  open(FH,">testout/t10font.ppm") || die "cannot open testout/t10font.ppm\n";
+  binmode(FH);
+  my $io = Imager::io_new_fd(fileno(FH));
+  i_writeppm_wiol($overlay,$io);
+  close(FH);
+  
+  $bgcolor=i_color_set($bgcolor,200,200,200,0);
+  my $backgr=Imager::ImgRaw::new(500,300,3);
+  
+  ok(Imager::Font::W32::i_wf_text($fontname,$backgr,100,100,$bgcolor,100,'MAW.',1, 1),
+     "i_wf_text smoke test");
+  i_line($backgr,0, 100, 499, 100, NC(0, 0, 255), 1);
+  
+  open(FH,">testout/t10font2.ppm") || die "cannot open testout/t10font2.ppm\n";
+  binmode(FH);
+  $io = Imager::io_new_fd(fileno(FH));
+  i_writeppm_wiol($backgr,$io);
+  close(FH);
+  
+  my $img = Imager->new(xsize=>200, ysize=>200);
+  my $font = Imager::Font->new(face=>$fontname, size=>20);
+  ok($img->string('x'=>30, 'y'=>30, string=>"Imager", color=>NC(255, 0, 0), 
+              font=>$font),
+     "string with win32 smoke test")
+    or print "# ",$img->errstr,"\n";
+  $img->write(file=>'testout/t10_oo.ppm') or print "not ";
+  my @bbox2 = $font->bounding_box(string=>'Imager');
+  is(@bbox2, 8, "got 8 values from bounding_box");
+
+  # this only applies while the Win32 driver returns 6 values
+  # at this point we don't return the advance width from the low level
+  # bounding box function, so the Imager::Font::BBox advance method should
+  # return end_offset, check it does
+  my $bbox = $font->bounding_box(string=>"some text");
+  ok($bbox, "got the bounding box object");
+  is($bbox->advance_width, $bbox->end_offset, 
+     "check advance_width fallback correct");
+
+ SKIP:
+  {
+    $^O eq 'cygwin' and skip("Too hard to get correct directory for test font on cygwin", 13);
+    ok(Imager::Font::W32::i_wf_addfont("fontfiles/ExistenceTest.ttf"), "add test font")
+      or print "# ",Imager::_error_as_msg(),"\n";
+    
+    my $namefont = Imager::Font->new(face=>"ExistenceTest");
+    ok($namefont, "create font based on added font");
+    
+    # the test font is known to have a shorter advance width for that char
+    @bbox = $namefont->bounding_box(string=>"/", size=>100);
+    print "# / box: @bbox\n";
+    is(@bbox, 8, "should be 8 entries");
+    isnt($bbox[6], $bbox[2], "different advance width");
+    $bbox = $namefont->bounding_box(string=>"/", size=>100);
+    ok($bbox->pos_width != $bbox->advance_width, "OO check");
+    
+    cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
+  
+    cmp_ok($bbox->display_width, '>', $bbox->advance_width,
+          "check display width (roughly)");
+    
+    my $im = Imager->new(xsize=>200, ysize=>200);
+    $im->box(filled => 1, color => '#202020');
+    $im->box(box => [ 20 + $bbox->neg_width, 100-$bbox->ascent,
+                     20+$bbox->advance_width-$bbox->right_bearing, 100-$bbox->descent ],
+            color => '#101010', filled => 1);
+    $im->line(color=>'blue', x1=>20, y1=>0, x2=>20, y2=>199);
+    my $right = 20 + $bbox->advance_width;
+    $im->line(color=>'blue', x1=>$right, y1=>0, x2=>$right, y2=>199);
+    $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100);
+    ok($im->string(font=>$namefont, text=>"/", x=>20, y=>100, color=>'white', size=>100),
+       "draw / from ExistenceText")
+       or print "# ", $im->errstr, "\n";
+    $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red');
+    $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red');
+    $im->write(file=>'testout/t10_slash.ppm');
+    
+    # check with a char that fits inside the box
+    $bbox = $namefont->bounding_box(string=>"!", size=>100);
+    print "# pos width ", $bbox->pos_width, "\n";
+    print "# ! box: @$bbox\n";
+    is($bbox->pos_width, $bbox->advance_width, 
+     "check backwards compatibility");
+    cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
+    cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
+    cmp_ok($bbox->display_width, '<', $bbox->advance_width,
+          "display smaller than advance");
+
+    $im = Imager->new(xsize=>200, ysize=>200);
+    $im->box(filled => 1, color => '#202020');
+    $im->box(box => [ 20 + $bbox->neg_width, 100-$bbox->ascent,
+                     20+$bbox->advance_width-$bbox->right_bearing, 100-$bbox->descent ],
+            color => '#101010', filled => 1);
+    $im->line(color=>'blue', x1=>20, y1=>0, x2=>20, y2=>199);
+    $right = 20 + $bbox->advance_width;
+    $im->line(color=>'blue', x1=>$right, y1=>0, x2=>$right, y2=>199);
+    $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100);
+    ok($im->string(font=>$namefont, text=>"!", x=>20, y=>100, color=>'white', size=>100),
+       "draw / from ExistenceText")
+       or print "# ", $im->errstr, "\n";
+    $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red');
+    $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red');
+    $im->write(file=>'testout/t10_bang.ppm');
+
+    Imager::Font::W32::i_wf_delfont("fontfiles/ExistenceTest.ttf");
+  }
+
+ SKIP:
+  { print "# alignment tests\n";
+    my $font = Imager::Font->new(face=>"Arial");
+    ok($font, "loaded Arial OO")
+      or skip("could not load font:".Imager->errstr, 4);
+    my $im = Imager->new(xsize=>140, ysize=>150);
+    my %common = 
+      (
+       font=>$font, 
+       size=>40, 
+       aa=>1,
+      );
+    $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
+    $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
+    $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
+    for my $args ([ x=>5,   text=>"A", color=>"white" ],
+                  [ x=>40,  text=>"y", color=>"white" ],
+                  [ x=>75,  text=>"A", channel=>1 ],
+                  [ x=>110, text=>"y", channel=>1 ]) {
+      ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
+      ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
+      ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
+    }
+    ok($im->write(file=>'testout/t10align.ppm'), "save align image");
+  }
+  { print "# utf 8 support\n";
+    my $font = Imager::Font->new(face => "Arial");
+    ok($font, "created font");
+    my $im = Imager->new(xsize => 100, ysize => 100);
+    ok($im->string(string => "\xE2\x98\xBA", size => 80, aa => 1, utf8 => 1, 
+                  color => "white", font => $font, x => 5, y => 80),
+       "draw in utf8 (hand encoded)")
+       or print "# ", $im->errstr, "\n";
+    ok($im->write(file=>'testout/t10utf8.ppm'), "save utf8 image");
+
+    # native perl utf8
+    # Win32 only supported on 5.6+
+    # since this gets compiled even on older perls we need to be careful 
+    # creating the string
+    my $text;
+    eval q{$text = "\x{263A}"}; # A, HYPHEN, A in our test font
+    my $im2 = Imager->new(xsize => 100, ysize => 100);
+    ok($im2->string(string => $text, size => 80, aa => 1,
+                   color => 'white', font => $font, x => 5, y => 80),
+       "draw in utf8 (perl utf8)")
+       or print "# ", $im->errstr, "\n";
+    ok($im2->write(file=>'testout/t10utf8b.ppm'), "save utf8 image");
+    is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0,
+       "check result is the same");
+
+    # bounding box
+    cmp_ok($font->bounding_box(string=>$text, size => 80)->advance_width, '<', 100,
+          "check we only get width of single char rather than 3");
+  }
+
+  { # string output cut off at NUL ('\0')
+    # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
+    my $font = Imager::Font->new(face=>'Arial', type=>'w32');
+    ok($font, "loaded Arial");
+
+    diff_text_with_nul("a\\0b vs a", "a\0b - color", "a", 
+                      font => $font, color => '#FFFFFF');
+    diff_text_with_nul("a\\0b vs a", "a\0b - channel", "a", 
+                      font => $font, channel => 1);
+
+    # UTF8 encoded \x{2010}
+    my $dash = pack("C*", 0xE2, 0x80, 0x90);
+    diff_text_with_nul("utf8 dash\0dash vs dash - color", "$dash\0$dash", $dash,
+                      font => $font, color => '#FFFFFF', utf8 => 1);
+    diff_text_with_nul("utf8 dash\0dash vs dash - channel", "$dash\0$dash", $dash,
+                      font => $font, channel => 1, utf8 => 1);
+  }
+}
diff --git a/W32/win32.c b/W32/win32.c
new file mode 100644 (file)
index 0000000..ced1285
--- /dev/null
@@ -0,0 +1,555 @@
+#define _WIN32_WINNT 0x500
+#include "imw32.h"
+#define STRICT
+#include <windows.h>
+#include "imext.h"
+
+/*
+=head1 NAME
+
+win32.c - implements some win32 specific code, specifically Win32 font support.
+
+=head1 SYNOPSIS
+
+   int bbox[6];
+   if (i_wf_bbox(facename, size, text, text_len, bbox)) {
+     // we have the bbox
+   }
+   i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa, utf8);
+   i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa, utf8)
+
+=head1 DESCRIPTION
+
+An Imager interface to font output using the Win32 GDI.
+
+=over
+
+=cut
+*/
+
+#define fixed(x) ((x).value + ((x).fract) / 65536.0)
+
+static void set_logfont(const char *face, int size, LOGFONT *lf);
+
+static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
+                          HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8);
+static LPWSTR utf8_to_wide_string(char const *text, int text_len, int *wide_chars);
+
+/*
+=item i_wf_bbox(face, size, text, length, bbox, utf8)
+
+Calculate a bounding box for the text.
+
+=cut
+*/
+
+int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox,
+             int utf8) {
+  LOGFONT lf;
+  HFONT font, oldFont;
+  HDC dc;
+  SIZE sz;
+  TEXTMETRIC tm;
+  ABC first, last;
+  GLYPHMETRICS gm;
+  MAT2 mat;
+  int ascent, descent, max_ascent = -size, min_descent = size;
+  const char *workp;
+  int work_len;
+  int got_first_ch = 0;
+  unsigned long first_ch, last_ch;
+
+  mm_log((1, "i_wf_bbox(face %s, size %d, text %p, length %d, bbox %p, utf8 %d)\n", face, size, text, length, bbox, utf8));
+
+  set_logfont(face, size, &lf);
+  font = CreateFontIndirect(&lf);
+  if (!font) 
+    return 0;
+  dc = GetDC(NULL);
+  oldFont = (HFONT)SelectObject(dc, font);
+
+  {
+    char facename[100];
+    if (GetTextFace(dc, sizeof(facename), facename)) {
+      mm_log((1, "  face: %s\n", facename));
+    }
+  }
+
+  workp = text;
+  work_len = length;
+  while (work_len > 0) {
+    unsigned long c;
+    unsigned char cp;
+
+    if (utf8) {
+      c = i_utf8_advance(&workp, &work_len);
+      if (c == ~0UL) {
+        i_push_error(0, "invalid UTF8 character");
+        return 0;
+      }
+    }
+    else {
+      c = (unsigned char)*workp++;
+      --work_len;
+    }
+    if (!got_first_ch) {
+      first_ch = c;
+      ++got_first_ch;
+    }
+    last_ch = c;
+
+    cp = c > '~' ? '.' : c < ' ' ? '.' : c;
+    
+    memset(&mat, 0, sizeof(mat));
+    mat.eM11.value = 1;
+    mat.eM22.value = 1;
+    if (GetGlyphOutline(dc, (UINT)c, GGO_METRICS, &gm, 0, NULL, &mat) != GDI_ERROR) {
+      mm_log((2, "  glyph '%c' (%02x): bbx (%u,%u) org (%d,%d) inc(%d,%d)\n",
+             cp, c, gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
+               gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY));
+
+      ascent = gm.gmptGlyphOrigin.y;
+      descent = ascent - gm.gmBlackBoxY;
+      if (ascent > max_ascent) max_ascent = ascent;
+      if (descent < min_descent) min_descent = descent;
+    }
+    else {
+       mm_log((1, "  glyph '%c' (%02x): error %d\n", cp, c, GetLastError()));
+    }
+  }
+
+  if (utf8) {
+    int wide_chars;
+    LPWSTR wide_text = utf8_to_wide_string(text, length, &wide_chars);
+
+    if (!wide_text)
+      return 0;
+
+    if (!GetTextExtentPoint32W(dc, wide_text, wide_chars, &sz)
+       || !GetTextMetrics(dc, &tm)) {
+      SelectObject(dc, oldFont);
+      ReleaseDC(NULL, dc);
+      DeleteObject(font);
+      return 0;
+    }
+
+    myfree(wide_text);
+  }
+  else {
+    if (!GetTextExtentPoint32(dc, text, length, &sz)
+       || !GetTextMetrics(dc, &tm)) {
+      SelectObject(dc, oldFont);
+      ReleaseDC(NULL, dc);
+      DeleteObject(font);
+      return 0;
+    }
+  }
+  bbox[BBOX_GLOBAL_DESCENT] = -tm.tmDescent;
+  bbox[BBOX_DESCENT] = min_descent == size ? -tm.tmDescent : min_descent;
+  bbox[BBOX_POS_WIDTH] = sz.cx;
+  bbox[BBOX_ADVANCE_WIDTH] = sz.cx;
+  bbox[BBOX_GLOBAL_ASCENT] = tm.tmAscent;
+  bbox[BBOX_ASCENT] = max_ascent == -size ? tm.tmAscent : max_ascent;
+  
+  if (length
+      && GetCharABCWidths(dc, first_ch, first_ch, &first)
+      && GetCharABCWidths(dc, last_ch, last_ch, &last)) {
+    mm_log((1, "first: %d A: %d  B: %d  C: %d\n", first_ch,
+           first.abcA, first.abcB, first.abcC));
+    mm_log((1, "last: %d A: %d  B: %d  C: %d\n", last_ch,
+           last.abcA, last.abcB, last.abcC));
+    bbox[BBOX_NEG_WIDTH] = first.abcA;
+    bbox[BBOX_RIGHT_BEARING] = last.abcC;
+    if (last.abcC < 0)
+      bbox[BBOX_POS_WIDTH] -= last.abcC;
+  }
+  else {
+    bbox[BBOX_NEG_WIDTH] = 0;
+    bbox[BBOX_RIGHT_BEARING] = 0;
+  }
+
+  SelectObject(dc, oldFont);
+  ReleaseDC(NULL, dc);
+  DeleteObject(font);
+
+  mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
+
+  return BBOX_RIGHT_BEARING + 1;
+}
+
+/*
+=item i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa)
+
+Draws the text in the given color.
+
+=cut
+*/
+
+int
+i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, int size, 
+         const char *text, int len, int align, int aa, int utf8) {
+  unsigned char *bits;
+  HBITMAP bm;
+  SIZE sz;
+  int line_width;
+  int x, y;
+  int ch;
+  TEXTMETRIC tm;
+  int top;
+  int bbox[BOUNDING_BOX_COUNT];
+
+  mm_log((1, "i_wf_text(face %s, im %p, tx %d, ty %d, cl %p, size %d, text %p, length %d, align %d, aa %d,  utf8 %d)\n", face, im, tx, ty, cl, size, text, len, align, aa, aa, utf8));
+
+  if (!i_wf_bbox(face, size, text, len, bbox, utf8))
+    return 0;
+
+  bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
+  if (!bits)
+    return 0;
+  
+  tx += bbox[BBOX_NEG_WIDTH];
+  line_width = sz.cx * 3;
+  line_width = (line_width + 3) / 4 * 4;
+  top = ty;
+  if (align) {
+    top -= tm.tmAscent;
+  }
+  else {
+    top -= tm.tmAscent - bbox[BBOX_ASCENT];
+  }
+
+  for (y = 0; y < sz.cy; ++y) {
+    for (x = 0; x < sz.cx; ++x) {
+      i_color pel;
+      int scale = bits[3 * x];
+      i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
+      for (ch = 0; ch < im->channels; ++ch) {
+       pel.channel[ch] = 
+         ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255;
+      }
+      i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
+    }
+    bits += line_width;
+  }
+  DeleteObject(bm);
+
+  return 1;
+}
+
+/*
+=item i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
+
+Draws the text in the given channel.
+
+=cut
+*/
+
+int
+i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, int size, 
+         const char *text, int len, int align, int aa, int utf8) {
+  unsigned char *bits;
+  HBITMAP bm;
+  SIZE sz;
+  int line_width;
+  int x, y;
+  TEXTMETRIC tm;
+  int top;
+  int bbox[BOUNDING_BOX_COUNT];
+
+  mm_log((1, "i_wf_cp(face %s, im %p, tx %d, ty %d, channel %d, size %d, text %p, length %d, align %d, aa %d,  utf8 %d)\n", face, im, tx, ty, channel, size, text, len, align, aa, aa, utf8));
+
+  if (!i_wf_bbox(face, size, text, len, bbox, utf8))
+    return 0;
+
+  bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
+  if (!bits)
+    return 0;
+  
+  line_width = sz.cx * 3;
+  line_width = (line_width + 3) / 4 * 4;
+  top = ty;
+  if (align) {
+    top -= tm.tmAscent;
+  }
+  else {
+    top -= tm.tmAscent - bbox[BBOX_ASCENT];
+  }
+
+  for (y = 0; y < sz.cy; ++y) {
+    for (x = 0; x < sz.cx; ++x) {
+      i_color pel;
+      int scale = bits[3 * x];
+      i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
+      pel.channel[channel] = scale;
+      i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
+    }
+    bits += line_width;
+  }
+  DeleteObject(bm);
+
+  return 1;
+}
+
+static HMODULE gdi_dll;
+typedef BOOL (CALLBACK *AddFontResourceExA_t)(LPCSTR, DWORD, PVOID);
+static AddFontResourceExA_t AddFontResourceExAp;
+typedef BOOL (CALLBACK *RemoveFontResourceExA_t)(LPCSTR, DWORD, PVOID);
+static RemoveFontResourceExA_t RemoveFontResourceExAp;
+
+/*
+=item i_wf_addfont(char const *filename, char const *resource_file)
+
+Adds a TTF font file as usable by the application.
+
+Where possible the font is added as private to the application.
+
+Under Windows 95/98/ME the font is added globally, since we don't have
+any choice.  In either case call i_wf_delfont() to remove it.
+
+=cut
+ */
+int
+i_wf_addfont(char const *filename) {
+  i_clear_error();
+
+  if (!gdi_dll) {
+    gdi_dll = GetModuleHandle("GDI32");
+    if (gdi_dll) {
+      AddFontResourceExAp = (AddFontResourceExA_t)GetProcAddress(gdi_dll, "AddFontResourceExA");
+      RemoveFontResourceExAp = (RemoveFontResourceExA_t)GetProcAddress(gdi_dll, "RemoveFontResourceExA");
+    }
+  }
+
+  if (AddFontResourceExAp && RemoveFontResourceExAp
+      && AddFontResourceExAp(filename, FR_PRIVATE, 0)) {
+    return 1;
+  }
+  else if (AddFontResource(filename)) {
+    return 1;
+  }
+  else {
+    i_push_errorf(0, "Could not add resource: %ld", GetLastError());
+    return 0;
+  }
+}
+
+/*
+=item i_wf_delfont(char const *filename, char const *resource_file)
+
+Deletes a TTF font file as usable by the application.
+
+=cut
+ */
+int
+i_wf_delfont(char const *filename) {
+  i_clear_error();
+
+  if (AddFontResourceExAp && RemoveFontResourceExAp
+      && RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) {
+    return 1;
+  }
+  else if (RemoveFontResource(filename)) {
+    return 1;
+  }
+  else {
+    i_push_errorf(0, "Could not remove resource: %ld", GetLastError());
+    return 0;
+  }
+}
+
+/*
+=back
+
+=head1 INTERNAL FUNCTIONS
+
+=over
+
+=item set_logfont(face, size, lf)
+
+Fills in a LOGFONT structure with reasonable defaults.
+
+=cut
+*/
+
+static void set_logfont(const char *face, int size, LOGFONT *lf) {
+  memset(lf, 0, sizeof(LOGFONT));
+
+  lf->lfHeight = -size; /* character height rather than cell height */
+  lf->lfCharSet = DEFAULT_CHARSET;
+  lf->lfOutPrecision = OUT_TT_PRECIS;
+  lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
+  lf->lfQuality = PROOF_QUALITY;
+  strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
+  /* NUL terminated by the memset at the top */
+}
+
+/*
+=item render_text(face, size, text, length, aa, pbm, psz, tm)
+
+renders the text to an in-memory RGB bitmap 
+
+It would be nice to render to greyscale, but Windows doesn't have
+native greyscale bitmaps.
+
+=cut
+*/
+static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
+                  HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8) {
+  BITMAPINFO bmi;
+  BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
+  HDC dc, bmpDc;
+  LOGFONT lf;
+  HFONT font, oldFont;
+  SIZE sz;
+  HBITMAP bm, oldBm;
+  LPVOID bits;
+  int wide_count;
+  LPWSTR wide_text;
+
+  dc = GetDC(NULL);
+  set_logfont(face, size, &lf);
+
+#ifdef ANTIALIASED_QUALITY
+  /* See KB article Q197076
+     "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
+  */
+  lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
+#endif
+
+  if (utf8) {
+    wide_text = utf8_to_wide_string(text, length, &wide_count);
+  }
+  else {
+    wide_text = NULL;
+  }
+
+  bmpDc = CreateCompatibleDC(dc);
+  if (bmpDc) {
+    font = CreateFontIndirect(&lf);
+    if (font) {
+      oldFont = SelectObject(bmpDc, font);
+
+      GetTextMetrics(bmpDc, tm);
+      sz.cx = bbox[BBOX_ADVANCE_WIDTH] - bbox[BBOX_NEG_WIDTH] + bbox[BBOX_POS_WIDTH];
+      sz.cy = bbox[BBOX_GLOBAL_ASCENT] - bbox[BBOX_GLOBAL_DESCENT];
+      
+      memset(&bmi, 0, sizeof(bmi));
+      bmih->biSize = sizeof(*bmih);
+      bmih->biWidth = sz.cx;
+      bmih->biHeight = sz.cy;
+      bmih->biPlanes = 1;
+      bmih->biBitCount = 24;
+      bmih->biCompression = BI_RGB;
+      bmih->biSizeImage = 0;
+      bmih->biXPelsPerMeter = (LONG)(72 / 2.54 * 100);
+      bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
+      bmih->biClrUsed = 0;
+      bmih->biClrImportant = 0;
+      
+      bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
+
+      if (bm) {
+       oldBm = SelectObject(bmpDc, bm);
+       SetTextColor(bmpDc, RGB(255, 255, 255));
+       SetBkColor(bmpDc, RGB(0, 0, 0));
+       if (utf8) {
+         TextOutW(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, wide_text, wide_count);
+       }
+       else {
+         TextOut(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, text, length);
+       }
+       SelectObject(bmpDc, oldBm);
+      }
+      else {
+       i_push_errorf(0, "Could not create DIB section for render: %ld",
+                     GetLastError());
+       SelectObject(bmpDc, oldFont);
+       DeleteObject(font);
+       DeleteDC(bmpDc);
+       ReleaseDC(NULL, dc);
+       if (wide_text)
+         myfree(wide_text);
+       return NULL;
+      }
+      SelectObject(bmpDc, oldFont);
+      DeleteObject(font);
+    }
+    else {
+      if (wide_text)
+       myfree(wide_text);
+      i_push_errorf(0, "Could not create logical font: %ld",
+                   GetLastError());
+      DeleteDC(bmpDc);
+      ReleaseDC(NULL, dc);
+      return NULL;
+    }
+    DeleteDC(bmpDc);
+  }
+  else {
+    if (wide_text)
+      myfree(wide_text);
+    i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
+    ReleaseDC(NULL, dc);
+    return NULL;
+  }
+
+  if (wide_text)
+    myfree(wide_text);
+
+  ReleaseDC(NULL, dc);
+
+  *pbm = bm;
+  *psz = sz;
+
+  return bits;
+}
+
+/*
+=item utf8_to_wide_string(text, text_len, wide_chars)
+
+=cut
+*/
+
+static
+LPWSTR
+utf8_to_wide_string(char const *text, int text_len, int *wide_chars) {
+  int wide_count = MultiByteToWideChar(CP_UTF8, 0, text, text_len, NULL, 0);
+  LPWSTR result;
+
+  if (wide_count < 0) {
+    i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
+    return NULL;
+  }
+  ++wide_count;
+  result = mymalloc(sizeof(WCHAR) * wide_count);
+  if (MultiByteToWideChar(CP_UTF8, 0, text, text_len, result, wide_count) < 0) {
+    i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
+    return NULL;
+  }
+
+  result[wide_count-1] = (WCHAR)'\0';
+  *wide_chars = wide_count - 1;
+
+  return result;
+}
+
+
+/*
+=back
+
+=head1 BUGS
+
+Should really use a structure so we can set more attributes.
+
+Should support UTF8
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=head1 SEE ALSO
+
+Imager(3)
+
+=cut
+*/
diff --git a/font.c b/font.c
index 706f06d..19df993 100644 (file)
--- a/font.c
+++ b/font.c
@@ -76,7 +76,7 @@ i_init_fonts(int t1log) {
 #ifdef HAVE_LIBT1
 
 static int t1_get_flags(char const *flags);
-static char *t1_from_utf8(char const *in, int len, int *outlen);
+static char *t1_from_utf8(char const *in, size_t len, int *outlen);
 
 static void t1_push_error(void);
 
@@ -254,7 +254,7 @@ Interface to text rendering into a single channel in an image
 */
 
 undef_int
-i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,int len,int align, int utf8, char const *flags) {
+i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,size_t len,int align, int utf8, char const *flags) {
   GLYPH *glyph;
   int xsize,ysize,x,y;
   i_color val;
@@ -301,7 +301,7 @@ i_t1_cp(i_img *im,int xb,int yb,int channel,int fontnum,float points,char* str,i
 }
 
 static void
-t1_fix_bbox(BBox *bbox, const char *str, int len, int advance, 
+t1_fix_bbox(BBox *bbox, const char *str, size_t len, int advance, 
            int space_position) {
   /* never called with len == 0 */
   if (str[0] == space_position && bbox->llx > 0)
@@ -328,7 +328,7 @@ function to get a strings bounding box given the font id and sizes
 */
 
 int
-i_t1_bbox(int fontnum,float points,const char *str,int len,int cords[6], int utf8,char const *flags) {
+i_t1_bbox(int fontnum,float points,const char *str,size_t len,int cords[6], int utf8,char const *flags) {
   BBox bbox;
   BBox gbbox;
   int mod_flags = t1_get_flags(flags);
@@ -406,7 +406,7 @@ Interface to text rendering in a single color onto an image
 */
 
 undef_int
-i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,int len,int align, int utf8, char const *flags) {
+i_t1_text(i_img *im,int xb,int yb,const i_color *cl,int fontnum,float points,const char* str,size_t len,int align, int utf8, char const *flags) {
   GLYPH *glyph;
   int xsize,ysize,y;
   int mod_flags = t1_get_flags(flags);
@@ -473,7 +473,7 @@ t1_get_flags(char const *flags) {
 }
 
 /*
-=item t1_from_utf8(char const *in, int len, int *outlen)
+=item t1_from_utf8(char const *in, size_t len, int *outlen)
 
 Produces an unencoded version of I<in> by dropping any Unicode
 character over 255.
@@ -485,7 +485,7 @@ Sets *outlen to the number of bytes used in the output string.
 */
 
 static char *
-t1_from_utf8(char const *in, int len, int *outlen) {
+t1_from_utf8(char const *in, size_t len, int *outlen) {
   /* at this point len is from a perl SV, so can't approach MAXINT */
   char *out = mymalloc(len+1); /* checked 5Nov05 tonyc */
   char *p = out;
@@ -527,7 +527,7 @@ Returns the number of characters that were checked.
 */
 
 int
-i_t1_has_chars(int font_num, const char *text, int len, int utf8,
+i_t1_has_chars(int font_num, const char *text, size_t len, int utf8,
                char *out) {
   int count = 0;
   
@@ -841,13 +841,13 @@ i_tt_render_glyph( TT_Glyph glyph, TT_Glyph_Metrics* gmetrics,
 static int
 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit, 
                         TT_Raster_Map *small_bit, int cords[6], 
-                        char const* txt, int len, int smooth, int utf8 );
+                        char const* txt, size_t len, int smooth, int utf8 );
 static void i_tt_dump_raster_map2( i_img* im, TT_Raster_Map* bit, int xb, int yb, const i_color *cl, int smooth );
 static void i_tt_dump_raster_map_channel( i_img* im, TT_Raster_Map* bit, int xb, int yb, int channel, int smooth );
 static  int
 i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], 
-                float points, char const* txt, int len, int smooth, int utf8 );
-static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 );
+                float points, char const* txt, size_t len, int smooth, int utf8 );
+static undef_int i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[6], int utf8 );
 
 
 /* static globals needed */
@@ -1337,7 +1337,7 @@ Returns the number of characters that were checked.
 */
 
 int
-i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
+i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8,
                char *out) {
   int count = 0;
   mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
@@ -1471,7 +1471,7 @@ static
 int
 i_tt_render_all_glyphs( TT_Fonthandle *handle, int inst, TT_Raster_Map *bit,
                         TT_Raster_Map *small_bit, int cords[6], 
-                        char const* txt, int len, int smooth, int utf8 ) {
+                        char const* txt, size_t len, int smooth, int utf8 ) {
   unsigned long j;
   TT_F26Dot6 x,y;
   
@@ -1648,7 +1648,7 @@ interface for generating single channel raster of text (internal)
 
 static
 int
-i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, int len, int smooth, int utf8 ) {
+i_tt_rasterize( TT_Fonthandle *handle, TT_Raster_Map *bit, int cords[6], float points, char const* txt, size_t len, int smooth, int utf8 ) {
   int inst;
   int width, height;
   TT_Raster_Map small_bit;
@@ -1709,7 +1709,7 @@ Interface to text rendering into a single channel in an image
 */
 
 undef_int
-i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8, int align ) {
+i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, size_t len, int smooth, int utf8, int align ) {
 
   int cords[BOUNDING_BOX_COUNT];
   int ascent, st_offset, y;
@@ -1747,7 +1747,7 @@ Interface to text rendering in a single color onto an image
 */
 
 undef_int
-i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align) {
+i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align) {
   int cords[BOUNDING_BOX_COUNT];
   int ascent, st_offset, y;
   TT_Raster_Map bit;
@@ -1783,7 +1783,7 @@ Function to get texts bounding boxes given the instance of the font (internal)
 
 static
 undef_int
-i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, size_t len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
   int upm, casc, cdesc, first;
   
   int start    = 0;
@@ -1883,7 +1883,7 @@ Interface to get a strings bounding box
 */
 
 undef_int
-i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8) {
+i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8) {
   int inst;
 
   i_clear_error();
index d2e3c06..5076040 100644 (file)
@@ -313,7 +313,7 @@ int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting) {
 }
 
 /*
-=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int *bbox)
+=item i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int *bbox)
 
 Retrieves bounding box information for the font at the given 
 character width and height.  This ignores the transformation matrix.
@@ -324,7 +324,7 @@ Returns non-zero on success.
 */
 int
 i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, 
-           char const *text, int len, int *bbox, int utf8) {
+           char const *text, size_t len, int *bbox, int utf8) {
   FT_Error error;
   int width;
   int index;
@@ -469,7 +469,7 @@ static void expand_bounds(int bbox[4], int bbox2[4]) {
 }
 
 /*
-=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, int len, int vlayout, int utf8, int *bbox)
+=item i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, char *text, size_t len, int vlayout, int utf8, int *bbox)
 
 Retrieves bounding box information for the font at the given 
 character width and height.
@@ -490,7 +490,7 @@ Returns non-zero on success.
 */
 int
 i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
-           char const *text, int len, int vlayout, int utf8, int *bbox) {
+           char const *text, size_t len, int vlayout, int utf8, int *bbox) {
   FT_Error error;
   int width;
   int index;
@@ -619,7 +619,7 @@ static int
 make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
 
 /*
-=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, int len, int align, int aa)
+=item i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, double cheight, double cwidth, char *text, size_t len, int align, int aa)
 
 Renders I<text> to (I<tx>, I<ty>) in I<im> using color I<cl> at the given 
 I<cheight> and I<cwidth>.
@@ -637,8 +637,8 @@ Returns non-zero on success.
 */
 int
 i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
-           double cheight, double cwidth, char const *text, int len, int align,
-           int aa, int vlayout, int utf8) {
+           double cheight, double cwidth, char const *text, size_t len,
+          int align, int aa, int vlayout, int utf8) {
   FT_Error error;
   int index;
   FT_Glyph_Metrics *gm;
@@ -767,7 +767,7 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, const i_color *cl,
 }
 
 /*
-=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, int len, int align, int aa)
+=item i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, double cheight, double cwidth, char *text, size_t len, int align, int aa)
 
 Renders I<text> to (I<tx>, I<ty>) in I<im> to I<channel> at the given 
 I<cheight> and I<cwidth>.
@@ -786,7 +786,7 @@ Returns non-zero on success.
 
 int
 i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
-         double cheight, double cwidth, char const *text, int len, int align,
+         double cheight, double cwidth, char const *text, size_t len, int align,
          int aa, int vlayout, int utf8) {
   int bbox[8];
   i_img *work;
@@ -830,7 +830,7 @@ i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel,
 }
 
 /*
-=item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
+=item i_ft2_has_chars(handle, char *text, size_t len, int utf8, char *out)
 
 Check if the given characters are defined by the font.
 
@@ -838,7 +838,7 @@ Returns the number of characters that were checked.
 
 =cut
 */
-int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len, 
+int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len, 
                     int utf8, char *out) {
   int count = 0;
   mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", 
index 770a69e..a58c802 100644 (file)
--- a/imager.h
+++ b/imager.h
@@ -229,12 +229,12 @@ undef_int i_init_fonts( int t1log );
 undef_int i_init_t1( int t1log );
 int       i_t1_new( char *pfb, char *afm );
 int       i_t1_destroy( int font_id );
-undef_int i_t1_cp( i_img *im, int xb, int yb, int channel, int fontnum, float points, char* str, int len, int align, int utf8, char const *flags );
-undef_int i_t1_text( i_img *im, int xb, int yb, const i_color *cl, int fontnum, float points, const char* str, int len, int align, int utf8, char const *flags );
-int      i_t1_bbox( int fontnum, float point, const char *str, int len, int cords[6], int utf8, char const *flags );
+undef_int i_t1_cp( i_img *im, int xb, int yb, int channel, int fontnum, float points, char* str, size_t len, int align, int utf8, char const *flags );
+undef_int i_t1_text( i_img *im, int xb, int yb, const i_color *cl, int fontnum, float points, const char* str, size_t len, int align, int utf8, char const *flags );
+int      i_t1_bbox( int fontnum, float point, const char *str, size_t len, int cords[6], int utf8, char const *flags );
 void      i_t1_set_aa( int st );
 void      close_t1( void );
-int       i_t1_has_chars(int font_num, char const *text, int len, int utf8, char *out);
+int       i_t1_has_chars(int font_num, char const *text, size_t len, int utf8, char *out);
 extern int i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size);
 extern int i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, 
                            size_t name_buf_size);
@@ -245,10 +245,10 @@ extern int i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf,
 undef_int i_init_tt( void );
 TT_Fonthandle* i_tt_new(const char *fontname);
 void i_tt_destroy( TT_Fonthandle *handle );
-undef_int i_tt_cp( TT_Fonthandle *handle,i_img *im,int xb,int yb,int channel,float points,char const* txt,int len,int smooth, int utf8, int align);
-undef_int i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, int len, int smooth, int utf8, int align);
-undef_int i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,int len,int cords[6], int utf8);
-int i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8, char *out);
+undef_int i_tt_cp( TT_Fonthandle *handle,i_img *im,int xb,int yb,int channel,float points,char const* txt,size_t len,int smooth, int utf8, int align);
+undef_int i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, const i_color *cl, float points, char const* txt, size_t len, int smooth, int utf8, int align);
+undef_int i_tt_bbox( TT_Fonthandle *handle, float points,const char *txt,size_t len,int cords[6], int utf8);
+int i_tt_has_chars(TT_Fonthandle *handle, char const *text, size_t len, int utf8, char *out);
 void i_tt_dump_names(TT_Fonthandle *handle);
 int i_tt_face_name(TT_Fonthandle *handle, char *name_buf, 
                    size_t name_buf_size);
@@ -267,18 +267,18 @@ extern int i_ft2_getdpi(FT2_Fonthandle *handle, int *xdpi, int *ydpi);
 extern int i_ft2_settransform(FT2_Fonthandle *handle, const double *matrix);
 extern int i_ft2_sethinting(FT2_Fonthandle *handle, int hinting);
 extern int i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, 
-                      char const *text, int len, int *bbox, int utf8);
+                      char const *text, size_t len, int *bbox, int utf8);
 extern int i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, 
-                     char const *text, int len, int vlayout, int utf8, int *bbox);
+                     char const *text, size_t len, int vlayout, int utf8, int *bbox);
 extern int i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, 
                       const i_color *cl, double cheight, double cwidth, 
-                      char const *text, int len, int align, int aa, 
+                      char const *text, size_t len, int align, int aa, 
                       int vlayout, int utf8);
 extern int i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, 
                     int channel, double cheight, double cwidth, 
-                    char const *text, int len, int align, int aa, int vlayout
-                    int utf8);
-extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len,
+                    char const *text, size_t len, int align, int aa
+                   int vlayout, int utf8);
+extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, size_t len,
                            int utf8, char *work);
 extern int i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, 
                            size_t name_buf_size);
@@ -297,18 +297,6 @@ extern int
 i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, const long *coords);
 #endif
 
-#ifdef WIN32
-
-extern int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox, int utf8);
-extern int i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, 
-                    int size, const char *text, int len, int align, int aa, int utf8);
-extern int i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, 
-                  int size, const char *text, int len, int align, int aa, int utf8);
-extern int i_wf_addfont(char const *file);
-extern int i_wf_delfont(char const *file);
-
-#endif
-
 /* functions for reading and writing formats */
 
 /* general reader callback 
diff --git a/imext.c b/imext.c
index 30c7255..8a67b48 100644 (file)
--- a/imext.c
+++ b/imext.c
@@ -123,7 +123,8 @@ im_ext_funcs imager_function_table =
     i_gsamp_bg,
     i_gsampf_bg,
     i_get_file_background,
-    i_get_file_backgroundf
+    i_get_file_backgroundf,
+    i_utf8_advance
   };
 
 /* in general these functions aren't called by Imager internally, but
diff --git a/imext.h b/imext.h
index 1e2ed44..c9d6f46 100644 (file)
--- a/imext.h
+++ b/imext.h
@@ -219,4 +219,6 @@ extern im_ext_funcs *imager_function_ext_table;
 #define i_get_file_backgroundf(im, bg) \
   ((im_extt->f_i_get_file_backgroundf)((im), (bg)))
 
+#define i_utf8_advance(p, s) ((im_extt->f_i_utf8_advance)((p), (s)))
+
 #endif
index b35a68c..a0d0172 100644 (file)
@@ -164,6 +164,7 @@ typedef struct {
                      int out_channels, i_fcolor const * bg);
   void (*f_i_get_file_background)(i_img *im, i_color *bg);
   void (*f_i_get_file_backgroundf)(i_img *im, i_fcolor *bg);
+  unsigned long (*f_i_utf8_advance)(char const **p, size_t *len);
 
   /* IMAGER_API_LEVEL 6 functions will be added here */
 } im_ext_funcs;
diff --git a/imio.h b/imio.h
index d78e473..557097b 100644 (file)
--- a/imio.h
+++ b/imio.h
@@ -24,7 +24,7 @@ void  i_mempool_destroy(i_mempool *mp);
 #undef max
 #endif
 
-extern unsigned long i_utf8_advance(char const **p, int *len);
+extern unsigned long i_utf8_advance(char const **p, size_t *len);
 
 /* XXX Shouldn't these go away? */
 
diff --git a/io.c b/io.c
index 186eea4..4576bf9 100644 (file)
--- a/io.c
+++ b/io.c
@@ -361,7 +361,7 @@ possible representation.
 */
 
 unsigned long 
-i_utf8_advance(char const **p, int *len) {
+i_utf8_advance(char const **p, size_t *len) {
   unsigned char c;
   int i, ci, clen = 0;
   unsigned char codes[3];
index 253578a..4f2f571 100644 (file)
@@ -1527,6 +1527,10 @@ will change:
 
 B<i_gsamp_bg>
 
+=item *
+
+B<i_utf8_advance>
+
 
 
 =back
diff --git a/lib/Imager/Font/Win32.pm b/lib/Imager/Font/Win32.pm
deleted file mode 100644 (file)
index d6ef0cb..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package Imager::Font::Win32;
-use strict;
-use vars qw(@ISA $VERSION);
-@ISA = qw(Imager::Font);
-
-$VERSION = "1.006";
-
-# called by Imager::Font::new()
-# since Win32's HFONTs include the size information this
-# is just a stub
-sub new {
-  my ($class, %opts) = @_;
-
-  return bless \%opts, $class;
-}
-
-sub _bounding_box {
-  my ($self, %opts) = @_;
-  
-  my @bbox = Imager::i_wf_bbox($self->{face}, $opts{size}, $opts{string}, $opts{utf8});
-}
-
-sub _draw {
-  my $self = shift;
-
-  my %input = @_;
-  if (exists $input{channel}) {
-    Imager::i_wf_cp($self->{face}, $input{image}{IMG}, $input{x}, $input{'y'},
-                   $input{channel}, $input{size},
-                   $input{string}, $input{align}, $input{aa}, $input{utf8});
-  }
-  else {
-    Imager::i_wf_text($self->{face}, $input{image}{IMG}, $input{x}, 
-                     $input{'y'}, $input{color}, $input{size}, 
-                     $input{string}, $input{align}, $input{aa}, $input{utf8});
-  }
-}
-
-
-sub utf8 {
-  return 1;
-}
-
-1;
-
-__END__
-
-=head1 NAME
-
-=for stopwords GDI
-
-Imager::Font::Win32 - uses Win32 GDI services for text output
-
-=head1 SYNOPSIS
-
-  my $font = Imager::Font->new(face=>"Arial");
-
-=head1 DESCRIPTION
-
-Implements font support using Win32 GDI calls.  See Imager::Font for 
-usage information.
-
-=cut
diff --git a/t/t37w32font.t b/t/t37w32font.t
deleted file mode 100644 (file)
index 603390f..0000000
+++ /dev/null
@@ -1,207 +0,0 @@
-#!perl -w
-use strict;
-use Test::More tests => 54;
-BEGIN { use_ok(Imager => ':all') }
-use Imager::Test qw(diff_text_with_nul);
-++$|;
-
-init_log("testout/t37w32font.log",1);
-
-SKIP:
-{
-  i_has_format('w32') or skip("no MS Windows", 53);
-  print "# has w32\n";
-
-  my $fontname=$ENV{'TTFONTTEST'} || 'Times New Roman Bold';
-  
-  # i_init_fonts(); # unnecessary for Win32 font support
-
-  my $bgcolor=i_color_new(255,0,0,0);
-  my $overlay=Imager::ImgRaw::new(200,70,3);
-  
-  my @bbox=Imager::i_wf_bbox($fontname, 50.0,'XMCLH');
-  print "#bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n";
-  
-  ok(Imager::i_wf_cp($fontname,$overlay,5,50,1,50.0,'XMCLH',1,1),
-     "i_wf_cp smoke test");
-  i_line($overlay,0,50,100,50,$bgcolor, 1);
-  
-  open(FH,">testout/t37w32font.ppm") || die "cannot open testout/t37w32font.ppm\n";
-  binmode(FH);
-  my $io = Imager::io_new_fd(fileno(FH));
-  i_writeppm_wiol($overlay,$io);
-  close(FH);
-  
-  $bgcolor=i_color_set($bgcolor,200,200,200,0);
-  my $backgr=Imager::ImgRaw::new(500,300,3);
-  
-  ok(Imager::i_wf_text($fontname,$backgr,100,100,$bgcolor,100,'MAW.',1, 1),
-     "i_wf_text smoke test");
-  i_line($backgr,0, 100, 499, 100, NC(0, 0, 255), 1);
-  
-  open(FH,">testout/t37w32font2.ppm") || die "cannot open testout/t37w32font2.ppm\n";
-  binmode(FH);
-  $io = Imager::io_new_fd(fileno(FH));
-  i_writeppm_wiol($backgr,$io);
-  close(FH);
-  
-  my $img = Imager->new(xsize=>200, ysize=>200);
-  my $font = Imager::Font->new(face=>$fontname, size=>20);
-  ok($img->string('x'=>30, 'y'=>30, string=>"Imager", color=>NC(255, 0, 0), 
-              font=>$font),
-     "string with win32 smoke test")
-    or print "# ",$img->errstr,"\n";
-  $img->write(file=>'testout/t37_oo.ppm') or print "not ";
-  my @bbox2 = $font->bounding_box(string=>'Imager');
-  is(@bbox2, 8, "got 8 values from bounding_box");
-
-  # this only applies while the Win32 driver returns 6 values
-  # at this point we don't return the advance width from the low level
-  # bounding box function, so the Imager::Font::BBox advance method should
-  # return end_offset, check it does
-  my $bbox = $font->bounding_box(string=>"some text");
-  ok($bbox, "got the bounding box object");
-  is($bbox->advance_width, $bbox->end_offset, 
-     "check advance_width fallback correct");
-
- SKIP:
-  {
-    $^O eq 'cygwin' and skip("Too hard to get correct directory for test font on cygwin", 13);
-    ok(Imager::i_wf_addfont("fontfiles/ExistenceTest.ttf"), "add test font")
-      or print "# ",Imager::_error_as_msg(),"\n";
-    
-    my $namefont = Imager::Font->new(face=>"ExistenceTest");
-    ok($namefont, "create font based on added font");
-    
-    # the test font is known to have a shorter advance width for that char
-    @bbox = $namefont->bounding_box(string=>"/", size=>100);
-    print "# / box: @bbox\n";
-    is(@bbox, 8, "should be 8 entries");
-    isnt($bbox[6], $bbox[2], "different advance width");
-    $bbox = $namefont->bounding_box(string=>"/", size=>100);
-    ok($bbox->pos_width != $bbox->advance_width, "OO check");
-    
-    cmp_ok($bbox->right_bearing, '<', 0, "check right bearing");
-  
-    cmp_ok($bbox->display_width, '>', $bbox->advance_width,
-          "check display width (roughly)");
-    
-    my $im = Imager->new(xsize=>200, ysize=>200);
-    $im->box(filled => 1, color => '#202020');
-    $im->box(box => [ 20 + $bbox->neg_width, 100-$bbox->ascent,
-                     20+$bbox->advance_width-$bbox->right_bearing, 100-$bbox->descent ],
-            color => '#101010', filled => 1);
-    $im->line(color=>'blue', x1=>20, y1=>0, x2=>20, y2=>199);
-    my $right = 20 + $bbox->advance_width;
-    $im->line(color=>'blue', x1=>$right, y1=>0, x2=>$right, y2=>199);
-    $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100);
-    ok($im->string(font=>$namefont, text=>"/", x=>20, y=>100, color=>'white', size=>100),
-       "draw / from ExistenceText")
-       or print "# ", $im->errstr, "\n";
-    $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red');
-    $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red');
-    $im->write(file=>'testout/t37w32_slash.ppm');
-    
-    # check with a char that fits inside the box
-    $bbox = $namefont->bounding_box(string=>"!", size=>100);
-    print "# pos width ", $bbox->pos_width, "\n";
-    print "# ! box: @$bbox\n";
-    is($bbox->pos_width, $bbox->advance_width, 
-     "check backwards compatibility");
-    cmp_ok($bbox->left_bearing, '>', 0, "left bearing positive");
-    cmp_ok($bbox->right_bearing, '>', 0, "right bearing positive");
-    cmp_ok($bbox->display_width, '<', $bbox->advance_width,
-          "display smaller than advance");
-
-    $im = Imager->new(xsize=>200, ysize=>200);
-    $im->box(filled => 1, color => '#202020');
-    $im->box(box => [ 20 + $bbox->neg_width, 100-$bbox->ascent,
-                     20+$bbox->advance_width-$bbox->right_bearing, 100-$bbox->descent ],
-            color => '#101010', filled => 1);
-    $im->line(color=>'blue', x1=>20, y1=>0, x2=>20, y2=>199);
-    $right = 20 + $bbox->advance_width;
-    $im->line(color=>'blue', x1=>$right, y1=>0, x2=>$right, y2=>199);
-    $im->line(color=>'blue', x1=>0, y1 => 100, x2=>199, y2 => 100);
-    ok($im->string(font=>$namefont, text=>"!", x=>20, y=>100, color=>'white', size=>100),
-       "draw / from ExistenceText")
-       or print "# ", $im->errstr, "\n";
-    $im->setpixel(x => 20+$bbox->neg_width, y => 100-$bbox->ascent, color => 'red');
-    $im->setpixel(x => 20+$bbox->advance_width - $bbox->right_bearing, y => 100-$bbox->descent, color => 'red');
-    $im->write(file=>'testout/t37w32_bang.ppm');
-
-    Imager::i_wf_delfont("fontfiles/ExistenceTest.ttf");
-  }
-
- SKIP:
-  { print "# alignment tests\n";
-    my $font = Imager::Font->new(face=>"Arial");
-    ok($font, "loaded Arial OO")
-      or skip("could not load font:".Imager->errstr, 4);
-    my $im = Imager->new(xsize=>140, ysize=>150);
-    my %common = 
-      (
-       font=>$font, 
-       size=>40, 
-       aa=>1,
-      );
-    $im->line(x1=>0, y1=>40, x2=>139, y2=>40, color=>'blue');
-    $im->line(x1=>0, y1=>90, x2=>139, y2=>90, color=>'blue');
-    $im->line(x1=>0, y1=>110, x2=>139, y2=>110, color=>'blue');
-    for my $args ([ x=>5,   text=>"A", color=>"white" ],
-                  [ x=>40,  text=>"y", color=>"white" ],
-                  [ x=>75,  text=>"A", channel=>1 ],
-                  [ x=>110, text=>"y", channel=>1 ]) {
-      ok($im->string(%common, @$args, 'y'=>40), "A no alignment");
-      ok($im->string(%common, @$args, 'y'=>90, align=>1), "A align=1");
-      ok($im->string(%common, @$args, 'y'=>110, align=>0), "A align=0");
-    }
-    ok($im->write(file=>'testout/t37align.ppm'), "save align image");
-  }
-  { print "# utf 8 support\n";
-    my $font = Imager::Font->new(face => "Arial");
-    ok($font, "created font");
-    my $im = Imager->new(xsize => 100, ysize => 100);
-    ok($im->string(string => "\xE2\x98\xBA", size => 80, aa => 1, utf8 => 1, 
-                  color => "white", font => $font, x => 5, y => 80),
-       "draw in utf8 (hand encoded)")
-       or print "# ", $im->errstr, "\n";
-    ok($im->write(file=>'testout/t37utf8.ppm'), "save utf8 image");
-
-    # native perl utf8
-    # Win32 only supported on 5.6+
-    # since this gets compiled even on older perls we need to be careful 
-    # creating the string
-    my $text;
-    eval q{$text = "\x{263A}"}; # A, HYPHEN, A in our test font
-    my $im2 = Imager->new(xsize => 100, ysize => 100);
-    ok($im2->string(string => $text, size => 80, aa => 1,
-                   color => 'white', font => $font, x => 5, y => 80),
-       "draw in utf8 (perl utf8)")
-       or print "# ", $im->errstr, "\n";
-    ok($im2->write(file=>'testout/t37utf8b.ppm'), "save utf8 image");
-    is(Imager::i_img_diff($im->{IMG}, $im2->{IMG}), 0,
-       "check result is the same");
-
-    # bounding box
-    cmp_ok($font->bounding_box(string=>$text, size => 80)->advance_width, '<', 100,
-          "check we only get width of single char rather than 3");
-  }
-
-  { # string output cut off at NUL ('\0')
-    # https://rt.cpan.org/Ticket/Display.html?id=21770 cont'd
-    my $font = Imager::Font->new(face=>'Arial', type=>'w32');
-    ok($font, "loaded Arial");
-
-    diff_text_with_nul("a\\0b vs a", "a\0b - color", "a", 
-                      font => $font, color => '#FFFFFF');
-    diff_text_with_nul("a\\0b vs a", "a\0b - channel", "a", 
-                      font => $font, channel => 1);
-
-    # UTF8 encoded \x{2010}
-    my $dash = pack("C*", 0xE2, 0x80, 0x90);
-    diff_text_with_nul("utf8 dash\0dash vs dash - color", "$dash\0$dash", $dash,
-                      font => $font, color => '#FFFFFF', utf8 => 1);
-    diff_text_with_nul("utf8 dash\0dash vs dash - channel", "$dash\0$dash", $dash,
-                      font => $font, channel => 1, utf8 => 1);
-  }
-}
diff --git a/win32.c b/win32.c
deleted file mode 100644 (file)
index b076b2c..0000000
--- a/win32.c
+++ /dev/null
@@ -1,554 +0,0 @@
-#define _WIN32_WINNT 0x500
-#include "imager.h"
-#define STRICT
-#include <windows.h>
-
-/*
-=head1 NAME
-
-win32.c - implements some win32 specific code, specifically Win32 font support.
-
-=head1 SYNOPSIS
-
-   int bbox[6];
-   if (i_wf_bbox(facename, size, text, text_len, bbox)) {
-     // we have the bbox
-   }
-   i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa, utf8);
-   i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa, utf8)
-
-=head1 DESCRIPTION
-
-An Imager interface to font output using the Win32 GDI.
-
-=over
-
-=cut
-*/
-
-#define fixed(x) ((x).value + ((x).fract) / 65536.0)
-
-static void set_logfont(const char *face, int size, LOGFONT *lf);
-
-static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
-                          HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8);
-static LPWSTR utf8_to_wide_string(char const *text, int text_len, int *wide_chars);
-
-/*
-=item i_wf_bbox(face, size, text, length, bbox, utf8)
-
-Calculate a bounding box for the text.
-
-=cut
-*/
-
-int i_wf_bbox(const char *face, int size, const char *text, int length, int *bbox,
-             int utf8) {
-  LOGFONT lf;
-  HFONT font, oldFont;
-  HDC dc;
-  SIZE sz;
-  TEXTMETRIC tm;
-  ABC first, last;
-  GLYPHMETRICS gm;
-  MAT2 mat;
-  int ascent, descent, max_ascent = -size, min_descent = size;
-  const char *workp;
-  int work_len;
-  int got_first_ch = 0;
-  unsigned long first_ch, last_ch;
-
-  mm_log((1, "i_wf_bbox(face %s, size %d, text %p, length %d, bbox %p, utf8 %d)\n", face, size, text, length, bbox, utf8));
-
-  set_logfont(face, size, &lf);
-  font = CreateFontIndirect(&lf);
-  if (!font) 
-    return 0;
-  dc = GetDC(NULL);
-  oldFont = (HFONT)SelectObject(dc, font);
-
-  {
-    char facename[100];
-    if (GetTextFace(dc, sizeof(facename), facename)) {
-      mm_log((1, "  face: %s\n", facename));
-    }
-  }
-
-  workp = text;
-  work_len = length;
-  while (work_len > 0) {
-    unsigned long c;
-    unsigned char cp;
-
-    if (utf8) {
-      c = i_utf8_advance(&workp, &work_len);
-      if (c == ~0UL) {
-        i_push_error(0, "invalid UTF8 character");
-        return 0;
-      }
-    }
-    else {
-      c = (unsigned char)*workp++;
-      --work_len;
-    }
-    if (!got_first_ch) {
-      first_ch = c;
-      ++got_first_ch;
-    }
-    last_ch = c;
-
-    cp = c > '~' ? '.' : c < ' ' ? '.' : c;
-    
-    memset(&mat, 0, sizeof(mat));
-    mat.eM11.value = 1;
-    mat.eM22.value = 1;
-    if (GetGlyphOutline(dc, (UINT)c, GGO_METRICS, &gm, 0, NULL, &mat) != GDI_ERROR) {
-      mm_log((2, "  glyph '%c' (%02x): bbx (%u,%u) org (%d,%d) inc(%d,%d)\n",
-             cp, c, gm.gmBlackBoxX, gm.gmBlackBoxY, gm.gmptGlyphOrigin.x,
-               gm.gmptGlyphOrigin.y, gm.gmCellIncX, gm.gmCellIncY));
-
-      ascent = gm.gmptGlyphOrigin.y;
-      descent = ascent - gm.gmBlackBoxY;
-      if (ascent > max_ascent) max_ascent = ascent;
-      if (descent < min_descent) min_descent = descent;
-    }
-    else {
-       mm_log((1, "  glyph '%c' (%02x): error %d\n", cp, c, GetLastError()));
-    }
-  }
-
-  if (utf8) {
-    int wide_chars;
-    LPWSTR wide_text = utf8_to_wide_string(text, length, &wide_chars);
-
-    if (!wide_text)
-      return 0;
-
-    if (!GetTextExtentPoint32W(dc, wide_text, wide_chars, &sz)
-       || !GetTextMetrics(dc, &tm)) {
-      SelectObject(dc, oldFont);
-      ReleaseDC(NULL, dc);
-      DeleteObject(font);
-      return 0;
-    }
-
-    myfree(wide_text);
-  }
-  else {
-    if (!GetTextExtentPoint32(dc, text, length, &sz)
-       || !GetTextMetrics(dc, &tm)) {
-      SelectObject(dc, oldFont);
-      ReleaseDC(NULL, dc);
-      DeleteObject(font);
-      return 0;
-    }
-  }
-  bbox[BBOX_GLOBAL_DESCENT] = -tm.tmDescent;
-  bbox[BBOX_DESCENT] = min_descent == size ? -tm.tmDescent : min_descent;
-  bbox[BBOX_POS_WIDTH] = sz.cx;
-  bbox[BBOX_ADVANCE_WIDTH] = sz.cx;
-  bbox[BBOX_GLOBAL_ASCENT] = tm.tmAscent;
-  bbox[BBOX_ASCENT] = max_ascent == -size ? tm.tmAscent : max_ascent;
-  
-  if (length
-      && GetCharABCWidths(dc, first_ch, first_ch, &first)
-      && GetCharABCWidths(dc, last_ch, last_ch, &last)) {
-    mm_log((1, "first: %d A: %d  B: %d  C: %d\n", first_ch,
-           first.abcA, first.abcB, first.abcC));
-    mm_log((1, "last: %d A: %d  B: %d  C: %d\n", last_ch,
-           last.abcA, last.abcB, last.abcC));
-    bbox[BBOX_NEG_WIDTH] = first.abcA;
-    bbox[BBOX_RIGHT_BEARING] = last.abcC;
-    if (last.abcC < 0)
-      bbox[BBOX_POS_WIDTH] -= last.abcC;
-  }
-  else {
-    bbox[BBOX_NEG_WIDTH] = 0;
-    bbox[BBOX_RIGHT_BEARING] = 0;
-  }
-
-  SelectObject(dc, oldFont);
-  ReleaseDC(NULL, dc);
-  DeleteObject(font);
-
-  mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
-
-  return BBOX_RIGHT_BEARING + 1;
-}
-
-/*
-=item i_wf_text(face, im, tx, ty, cl, size, text, len, align, aa)
-
-Draws the text in the given color.
-
-=cut
-*/
-
-int
-i_wf_text(const char *face, i_img *im, int tx, int ty, const i_color *cl, int size, 
-         const char *text, int len, int align, int aa, int utf8) {
-  unsigned char *bits;
-  HBITMAP bm;
-  SIZE sz;
-  int line_width;
-  int x, y;
-  int ch;
-  TEXTMETRIC tm;
-  int top;
-  int bbox[BOUNDING_BOX_COUNT];
-
-  mm_log((1, "i_wf_text(face %s, im %p, tx %d, ty %d, cl %p, size %d, text %p, length %d, align %d, aa %d,  utf8 %d)\n", face, im, tx, ty, cl, size, text, len, align, aa, aa, utf8));
-
-  if (!i_wf_bbox(face, size, text, len, bbox, utf8))
-    return 0;
-
-  bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
-  if (!bits)
-    return 0;
-  
-  tx += bbox[BBOX_NEG_WIDTH];
-  line_width = sz.cx * 3;
-  line_width = (line_width + 3) / 4 * 4;
-  top = ty;
-  if (align) {
-    top -= tm.tmAscent;
-  }
-  else {
-    top -= tm.tmAscent - bbox[BBOX_ASCENT];
-  }
-
-  for (y = 0; y < sz.cy; ++y) {
-    for (x = 0; x < sz.cx; ++x) {
-      i_color pel;
-      int scale = bits[3 * x];
-      i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
-      for (ch = 0; ch < im->channels; ++ch) {
-       pel.channel[ch] = 
-         ((255-scale) * pel.channel[ch] + scale*cl->channel[ch]) / 255;
-      }
-      i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
-    }
-    bits += line_width;
-  }
-  DeleteObject(bm);
-
-  return 1;
-}
-
-/*
-=item i_wf_cp(face, im, tx, ty, channel, size, text, len, align, aa)
-
-Draws the text in the given channel.
-
-=cut
-*/
-
-int
-i_wf_cp(const char *face, i_img *im, int tx, int ty, int channel, int size, 
-         const char *text, int len, int align, int aa, int utf8) {
-  unsigned char *bits;
-  HBITMAP bm;
-  SIZE sz;
-  int line_width;
-  int x, y;
-  TEXTMETRIC tm;
-  int top;
-  int bbox[BOUNDING_BOX_COUNT];
-
-  mm_log((1, "i_wf_cp(face %s, im %p, tx %d, ty %d, channel %d, size %d, text %p, length %d, align %d, aa %d,  utf8 %d)\n", face, im, tx, ty, channel, size, text, len, align, aa, aa, utf8));
-
-  if (!i_wf_bbox(face, size, text, len, bbox, utf8))
-    return 0;
-
-  bits = render_text(face, size, text, len, aa, &bm, &sz, &tm, bbox, utf8);
-  if (!bits)
-    return 0;
-  
-  line_width = sz.cx * 3;
-  line_width = (line_width + 3) / 4 * 4;
-  top = ty;
-  if (align) {
-    top -= tm.tmAscent;
-  }
-  else {
-    top -= tm.tmAscent - bbox[BBOX_ASCENT];
-  }
-
-  for (y = 0; y < sz.cy; ++y) {
-    for (x = 0; x < sz.cx; ++x) {
-      i_color pel;
-      int scale = bits[3 * x];
-      i_gpix(im, tx+x, top+sz.cy-y-1, &pel);
-      pel.channel[channel] = scale;
-      i_ppix(im, tx+x, top+sz.cy-y-1, &pel);
-    }
-    bits += line_width;
-  }
-  DeleteObject(bm);
-
-  return 1;
-}
-
-static HMODULE gdi_dll;
-typedef BOOL (CALLBACK *AddFontResourceExA_t)(LPCSTR, DWORD, PVOID);
-static AddFontResourceExA_t AddFontResourceExAp;
-typedef BOOL (CALLBACK *RemoveFontResourceExA_t)(LPCSTR, DWORD, PVOID);
-static RemoveFontResourceExA_t RemoveFontResourceExAp;
-
-/*
-=item i_wf_addfont(char const *filename, char const *resource_file)
-
-Adds a TTF font file as usable by the application.
-
-Where possible the font is added as private to the application.
-
-Under Windows 95/98/ME the font is added globally, since we don't have
-any choice.  In either case call i_wf_delfont() to remove it.
-
-=cut
- */
-int
-i_wf_addfont(char const *filename) {
-  i_clear_error();
-
-  if (!gdi_dll) {
-    gdi_dll = GetModuleHandle("GDI32");
-    if (gdi_dll) {
-      AddFontResourceExAp = (AddFontResourceExA_t)GetProcAddress(gdi_dll, "AddFontResourceExA");
-      RemoveFontResourceExAp = (RemoveFontResourceExA_t)GetProcAddress(gdi_dll, "RemoveFontResourceExA");
-    }
-  }
-
-  if (AddFontResourceExAp && RemoveFontResourceExAp
-      && AddFontResourceExAp(filename, FR_PRIVATE, 0)) {
-    return 1;
-  }
-  else if (AddFontResource(filename)) {
-    return 1;
-  }
-  else {
-    i_push_errorf(0, "Could not add resource: %ld", GetLastError());
-    return 0;
-  }
-}
-
-/*
-=item i_wf_delfont(char const *filename, char const *resource_file)
-
-Deletes a TTF font file as usable by the application.
-
-=cut
- */
-int
-i_wf_delfont(char const *filename) {
-  i_clear_error();
-
-  if (AddFontResourceExAp && RemoveFontResourceExAp
-      && RemoveFontResourceExAp(filename, FR_PRIVATE, 0)) {
-    return 1;
-  }
-  else if (RemoveFontResource(filename)) {
-    return 1;
-  }
-  else {
-    i_push_errorf(0, "Could not remove resource: %ld", GetLastError());
-    return 0;
-  }
-}
-
-/*
-=back
-
-=head1 INTERNAL FUNCTIONS
-
-=over
-
-=item set_logfont(face, size, lf)
-
-Fills in a LOGFONT structure with reasonable defaults.
-
-=cut
-*/
-
-static void set_logfont(const char *face, int size, LOGFONT *lf) {
-  memset(lf, 0, sizeof(LOGFONT));
-
-  lf->lfHeight = -size; /* character height rather than cell height */
-  lf->lfCharSet = DEFAULT_CHARSET;
-  lf->lfOutPrecision = OUT_TT_PRECIS;
-  lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
-  lf->lfQuality = PROOF_QUALITY;
-  strncpy(lf->lfFaceName, face, sizeof(lf->lfFaceName)-1);
-  /* NUL terminated by the memset at the top */
-}
-
-/*
-=item render_text(face, size, text, length, aa, pbm, psz, tm)
-
-renders the text to an in-memory RGB bitmap 
-
-It would be nice to render to greyscale, but Windows doesn't have
-native greyscale bitmaps.
-
-=cut
-*/
-static LPVOID render_text(const char *face, int size, const char *text, int length, int aa,
-                  HBITMAP *pbm, SIZE *psz, TEXTMETRIC *tm, int *bbox, int utf8) {
-  BITMAPINFO bmi;
-  BITMAPINFOHEADER *bmih = &bmi.bmiHeader;
-  HDC dc, bmpDc;
-  LOGFONT lf;
-  HFONT font, oldFont;
-  SIZE sz;
-  HBITMAP bm, oldBm;
-  LPVOID bits;
-  int wide_count;
-  LPWSTR wide_text;
-
-  dc = GetDC(NULL);
-  set_logfont(face, size, &lf);
-
-#ifdef ANTIALIASED_QUALITY
-  /* See KB article Q197076
-     "INFO: Controlling Anti-aliased Text via the LOGFONT Structure"
-  */
-  lf.lfQuality = aa ? ANTIALIASED_QUALITY : NONANTIALIASED_QUALITY;
-#endif
-
-  if (utf8) {
-    wide_text = utf8_to_wide_string(text, length, &wide_count);
-  }
-  else {
-    wide_text = NULL;
-  }
-
-  bmpDc = CreateCompatibleDC(dc);
-  if (bmpDc) {
-    font = CreateFontIndirect(&lf);
-    if (font) {
-      oldFont = SelectObject(bmpDc, font);
-
-      GetTextMetrics(bmpDc, tm);
-      sz.cx = bbox[BBOX_ADVANCE_WIDTH] - bbox[BBOX_NEG_WIDTH] + bbox[BBOX_POS_WIDTH];
-      sz.cy = bbox[BBOX_GLOBAL_ASCENT] - bbox[BBOX_GLOBAL_DESCENT];
-      
-      memset(&bmi, 0, sizeof(bmi));
-      bmih->biSize = sizeof(*bmih);
-      bmih->biWidth = sz.cx;
-      bmih->biHeight = sz.cy;
-      bmih->biPlanes = 1;
-      bmih->biBitCount = 24;
-      bmih->biCompression = BI_RGB;
-      bmih->biSizeImage = 0;
-      bmih->biXPelsPerMeter = (LONG)(72 / 2.54 * 100);
-      bmih->biYPelsPerMeter = bmih->biXPelsPerMeter;
-      bmih->biClrUsed = 0;
-      bmih->biClrImportant = 0;
-      
-      bm = CreateDIBSection(dc, &bmi, DIB_RGB_COLORS, &bits, NULL, 0);
-
-      if (bm) {
-       oldBm = SelectObject(bmpDc, bm);
-       SetTextColor(bmpDc, RGB(255, 255, 255));
-       SetBkColor(bmpDc, RGB(0, 0, 0));
-       if (utf8) {
-         TextOutW(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, wide_text, wide_count);
-       }
-       else {
-         TextOut(bmpDc, -bbox[BBOX_NEG_WIDTH], 0, text, length);
-       }
-       SelectObject(bmpDc, oldBm);
-      }
-      else {
-       i_push_errorf(0, "Could not create DIB section for render: %ld",
-                     GetLastError());
-       SelectObject(bmpDc, oldFont);
-       DeleteObject(font);
-       DeleteDC(bmpDc);
-       ReleaseDC(NULL, dc);
-       if (wide_text)
-         myfree(wide_text);
-       return NULL;
-      }
-      SelectObject(bmpDc, oldFont);
-      DeleteObject(font);
-    }
-    else {
-      if (wide_text)
-       myfree(wide_text);
-      i_push_errorf(0, "Could not create logical font: %ld",
-                   GetLastError());
-      DeleteDC(bmpDc);
-      ReleaseDC(NULL, dc);
-      return NULL;
-    }
-    DeleteDC(bmpDc);
-  }
-  else {
-    if (wide_text)
-      myfree(wide_text);
-    i_push_errorf(0, "Could not create rendering DC: %ld", GetLastError());
-    ReleaseDC(NULL, dc);
-    return NULL;
-  }
-
-  if (wide_text)
-    myfree(wide_text);
-
-  ReleaseDC(NULL, dc);
-
-  *pbm = bm;
-  *psz = sz;
-
-  return bits;
-}
-
-/*
-=item utf8_to_wide_string(text, text_len, wide_chars)
-
-=cut
-*/
-
-static
-LPWSTR
-utf8_to_wide_string(char const *text, int text_len, int *wide_chars) {
-  int wide_count = MultiByteToWideChar(CP_UTF8, 0, text, text_len, NULL, 0);
-  LPWSTR result;
-
-  if (wide_count < 0) {
-    i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
-    return NULL;
-  }
-  ++wide_count;
-  result = mymalloc(sizeof(WCHAR) * wide_count);
-  if (MultiByteToWideChar(CP_UTF8, 0, text, text_len, result, wide_count) < 0) {
-    i_push_errorf(0, "Could not convert utf8: %ld", GetLastError());
-    return NULL;
-  }
-
-  result[wide_count-1] = (WCHAR)'\0';
-  *wide_chars = wide_count - 1;
-
-  return result;
-}
-
-
-/*
-=back
-
-=head1 BUGS
-
-Should really use a structure so we can set more attributes.
-
-Should support UTF8
-
-=head1 AUTHOR
-
-Tony Cook <tony@develop-help.com>
-
-=head1 SEE ALSO
-
-Imager(3)
-
-=cut
-*/