gif => "Imager::File::GIF",
tiff => "Imager::File::TIFF",
jpeg => "Imager::File::JPEG",
+ w32 => "Imager::Font::W32",
);
tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes;
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;
PREINIT:
char const *text;
STRLEN work_len;
- int len;
+ size_t len;
char name[255];
PPCODE:
#ifdef SvUTF8
PREINIT:
char const *text;
STRLEN work_len;
- int len;
+ size_t len;
int outsize;
char name[255];
PPCODE:
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
PREINIT:
char const *text;
STRLEN work_len;
- int len;
+ size_t len;
char name[255];
PPCODE:
#ifdef SvUTF8
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
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
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
trans2.c
transform.perl Shell interface to Imager::Transform
typemap
-win32.c Implements font support through Win32 GDI
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',
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;
--- /dev/null
+#!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
+}
--- /dev/null
+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
--- /dev/null
+#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;
--- /dev/null
+#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
--- /dev/null
+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
--- /dev/null
+#!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);
+ }
+}
--- /dev/null
+#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
+*/
#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);
*/
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;
}
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)
*/
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);
*/
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);
}
/*
-=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.
*/
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;
*/
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;
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 */
*/
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",
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;
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;
*/
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;
*/
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;
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;
*/
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();
}
/*
-=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.
*/
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;
}
/*
-=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.
*/
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;
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>.
*/
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;
}
/*
-=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>.
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;
}
/*
-=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.
=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",
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);
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);
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);
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
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
#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
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;
#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? */
*/
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];
B<i_gsamp_bg>
+=item *
+
+B<i_utf8_advance>
+
=back
+++ /dev/null
-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
+++ /dev/null
-#!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);
- }
-}
+++ /dev/null
-#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
-*/