From a556912d911731f0da70f94d373374a238471f77 Mon Sep 17 00:00:00 2001 From: Tony Cook Date: Thu, 3 Feb 2011 17:49:17 +1100 Subject: [PATCH] move t1lib font support to a separate module --- .gitignore | 2 + Imager.pm | 20 +- Imager.xs | 183 ----- MANIFEST | 21 +- MANIFEST.SKIP | 6 + Makefile.PL | 26 +- T1/.gitignore | 1 + T1/Makefile.PL | 120 +++ T1/T1.pm | 211 ++++++ T1/T1.xs | 199 +++++ {fontfiles => T1/fontfiles}/ExistenceTest.afm | 0 {fontfiles => T1/fontfiles}/ExistenceTest.pfb | Bin {fontfiles => T1/fontfiles}/SpaceTest.afm | 0 {fontfiles => T1/fontfiles}/SpaceTest.pfb | Bin {fontfiles => T1/fontfiles}/dcr10.afm | 0 {fontfiles => T1/fontfiles}/dcr10.pfb | Bin T1/imt1.c | 684 +++++++++++++++++ T1/imt1.h | 40 + t/t30t1font.t => T1/t/t10type1.t | 65 +- T1/t/t20oo.t | 65 ++ font.c | 708 ------------------ lib/Imager/Font.pm | 4 +- lib/Imager/Font/Type1.pm | 186 +---- t/t36oofont.t | 55 +- 24 files changed, 1400 insertions(+), 1196 deletions(-) create mode 100644 T1/.gitignore create mode 100644 T1/Makefile.PL create mode 100644 T1/T1.pm create mode 100644 T1/T1.xs rename {fontfiles => T1/fontfiles}/ExistenceTest.afm (100%) rename {fontfiles => T1/fontfiles}/ExistenceTest.pfb (100%) rename {fontfiles => T1/fontfiles}/SpaceTest.afm (100%) rename {fontfiles => T1/fontfiles}/SpaceTest.pfb (100%) rename {fontfiles => T1/fontfiles}/dcr10.afm (100%) rename {fontfiles => T1/fontfiles}/dcr10.pfb (100%) create mode 100644 T1/imt1.c create mode 100644 T1/imt1.h rename t/t30t1font.t => T1/t/t10type1.t (86%) create mode 100644 T1/t/t20oo.t diff --git a/.gitignore b/.gitignore index 8103c3c6..ead9e937 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ *.o Makefile Makefile.old +MANIFEST.bak pm_to_blib blib @@ -23,6 +24,7 @@ scale.c # build generated files *.bs */Makefile +Imager-[0-9]* # test generated files _Inline/ diff --git a/Imager.pm b/Imager.pm index 869dabc4..0802663f 100644 --- a/Imager.pm +++ b/Imager.pm @@ -61,14 +61,6 @@ use Imager::Font; i_img_diff - i_init_fonts - i_t1_new - i_t1_destroy - i_t1_set_aa - i_t1_cp - i_t1_text - i_t1_bbox - i_tt_set_aa i_tt_cp i_tt_text @@ -155,7 +147,7 @@ my %defaults; BEGIN { require Exporter; @ISA = qw(Exporter); - $VERSION = '0.80'; + $VERSION = '0.80_01'; eval { require XSLoader; XSLoader::load(Imager => $VERSION); @@ -176,6 +168,7 @@ my %format_classes = jpeg => "Imager::File::JPEG", w32 => "Imager::Font::W32", ft2 => "Imager::Font::FT2", + t1 => "Imager::Font::T1", ); tie %formats, "Imager::FORMATS", \%formats_low, \%format_classes; @@ -474,13 +467,10 @@ sub init { $warn_obsolete = $parms{'warn_obsolete'}; } -# if ($parms{T1LIB_CONFIG}) { $ENV{T1LIB_CONFIG}=$parms{T1LIB_CONFIG}; } -# if ( $ENV{T1LIB_CONFIG} and ( $fontstate eq 'missing conf' )) { -# i_init_fonts(); -# $fontstate='ok'; -# } if (exists $parms{'t1log'}) { - i_init_fonts($parms{'t1log'}); + if ($formats{t1}) { + Imager::Font::T1::i_init_t1($parms{'t1log'}); + } } } diff --git a/Imager.xs b/Imager.xs index d1512c0c..b0c674ea 100644 --- a/Imager.xs +++ b/Imager.xs @@ -2006,10 +2006,6 @@ i_img_diffd(im1,im2) Imager::ImgRaw im1 Imager::ImgRaw im2 -undef_int -i_init_fonts(t1log=0) - int t1log - bool _is_color_object(sv) SV* sv @@ -2022,185 +2018,6 @@ _is_color_object(sv) RETVAL #ifdef HAVE_LIBT1 - -void -i_t1_set_aa(st) - int st - -int -i_t1_new(pfb,afm) - char* pfb - char* afm - -int -i_t1_destroy(font_id) - int font_id - - -undef_int -i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") - Imager::ImgRaw im - int xb - int yb - int channel - int fontnum - float points - SV* str_sv - int align - int utf8 - char* flags - PREINIT: - char *str; - STRLEN len; - CODE: -#ifdef SvUTF8 - if (SvUTF8(str_sv)) - utf8 = 1; -#endif - str = SvPV(str_sv, len); - RETVAL = i_t1_cp(im, xb,yb,channel,fontnum,points,str,len,align, - utf8,flags); - OUTPUT: - RETVAL - - -void -i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="") - int fontnum - float point - SV* str_sv - int utf8 - char* flags - PREINIT: - char *str; - STRLEN len; - int cords[BOUNDING_BOX_COUNT]; - int i; - int rc; - PPCODE: -#ifdef SvUTF8 - if (SvUTF8(str_sv)) - utf8 = 1; -#endif - str = SvPV(str_sv, len); - rc = i_t1_bbox(fontnum,point,str,len,cords,utf8,flags); - if (rc > 0) { - EXTEND(SP, rc); - for (i = 0; i < rc; ++i) - PUSHs(sv_2mortal(newSViv(cords[i]))); - } - - - -undef_int -i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") - Imager::ImgRaw im - int xb - int yb - Imager::Color cl - int fontnum - float points - SV* str_sv - int align - int utf8 - char* flags - PREINIT: - char *str; - STRLEN len; - CODE: -#ifdef SvUTF8 - if (SvUTF8(str_sv)) - utf8 = 1; -#endif - str = SvPV(str_sv, len); - RETVAL = i_t1_text(im, xb,yb,cl,fontnum,points,str,len,align, - utf8,flags); - OUTPUT: - RETVAL - -void -i_t1_has_chars(handle, text_sv, utf8 = 0) - int handle - SV *text_sv - int utf8 - PREINIT: - char const *text; - STRLEN len; - char *work; - int count; - int i; - PPCODE: -#ifdef SvUTF8 - if (SvUTF8(text_sv)) - utf8 = 1; -#endif - text = SvPV(text_sv, len); - work = mymalloc(len); - count = i_t1_has_chars(handle, text, len, utf8, work); - if (GIMME_V == G_ARRAY) { - EXTEND(SP, count); - for (i = 0; i < count; ++i) { - PUSHs(sv_2mortal(newSViv(work[i]))); - } - } - else { - EXTEND(SP, 1); - PUSHs(sv_2mortal(newSVpv(work, count))); - } - myfree(work); - -void -i_t1_face_name(handle) - int handle - PREINIT: - char name[255]; - int len; - PPCODE: - len = i_t1_face_name(handle, name, sizeof(name)); - if (len) { - EXTEND(SP, 1); - PUSHs(sv_2mortal(newSVpv(name, strlen(name)))); - } - -void -i_t1_glyph_name(handle, text_sv, utf8 = 0) - int handle - SV *text_sv - int utf8 - PREINIT: - char const *text; - STRLEN work_len; - size_t len; - char name[255]; - PPCODE: -#ifdef SvUTF8 - if (SvUTF8(text_sv)) - utf8 = 1; -#endif - text = SvPV(text_sv, work_len); - len = work_len; - while (len) { - unsigned long ch; - if (utf8) { - ch = i_utf8_advance(&text, &len); - if (ch == ~0UL) { - i_push_error(0, "invalid UTF8 character"); - break; - } - } - else { - ch = *text++; - --len; - } - EXTEND(SP, 1); - if (i_t1_glyph_name(handle, ch, name, sizeof(name))) { - PUSHs(sv_2mortal(newSVpv(name, 0))); - } - else { - PUSHs(&PL_sv_undef); - } - } - #endif #ifdef HAVE_LIBTT diff --git a/MANIFEST b/MANIFEST index b8cf2d8e..9e932fdc 100644 --- a/MANIFEST +++ b/MANIFEST @@ -50,16 +50,10 @@ Flines/Makefile.PL Flines/t/t00flines.t flip.im font.c -fontfiles/dcr10.afm -fontfiles/dcr10.pfb fontfiles/dodge.ttf -fontfiles/ExistenceTest.afm please edit ExistenceTest.sfd in CVS -fontfiles/ExistenceTest.pfb to change these files, edited and fontfiles/ExistenceTest.ttf generated using pfaedit fontfiles/ImUgly.ttf fontfiles/NameTest.ttf test glyph_names() - see FT2/t/t10ft2.t -fontfiles/SpaceTest.afm -fontfiles/SpaceTest.pfb test bounding box with spaces in t30t1font.t FT2/fontfiles/dodge.ttf FT2/fontfiles/ExistenceTest.afm FT2/fontfiles/ExistenceTest.pfb @@ -181,7 +175,7 @@ lib/Imager/Font/BBox.pm lib/Imager/Font/FreeType2.pm lib/Imager/Font/Image.pm lib/Imager/Font/Truetype.pm -lib/Imager/Font/Type1.pm +lib/Imager/Font/Type1.pm Compatibilty wrapper for Imager::Font::T1 lib/Imager/Font/Wrap.pm lib/Imager/Fountain.pm lib/Imager/Handy.pod @@ -335,6 +329,19 @@ t/t94kwalitee.t Various "kwalitee" tests t/t99thread.t Test wrt to perl threads t/tr18561.t Regression tests t/tr18561b.t +T1/fontfiles/dcr10.afm +T1/fontfiles/dcr10.pfb +T1/fontfiles/ExistenceTest.afm please edit ExistenceTest.sfd in CVS +T1/fontfiles/ExistenceTest.pfb to change these files, edited and +T1/fontfiles/SpaceTest.afm test bounding box with spaces in t10type1.t +T1/fontfiles/SpaceTest.pfb +T1/imt1.c +T1/imt1.h +T1/Makefile.PL +T1/t/t10type1.t +T1/t/t20oo.t +T1/T1.pm +T1/T1.xs tags.c testimg/alpha16.tga 16-bit/pixel TGA with alpha "channel" RT 32926 testimg/bad1oflow.bmp 1-bit/pixel, overflow integer on 32-bit machines diff --git a/MANIFEST.SKIP b/MANIFEST.SKIP index a48eb3c3..cc4b7999 100644 --- a/MANIFEST.SKIP +++ b/MANIFEST.SKIP @@ -14,6 +14,10 @@ ^\.svn\b /\.svn\b +# git work files +^\.git/ +^(?:.*/)?\.gitignore$ + # editor trash ~$ (^|/)\#.*\#$ @@ -58,6 +62,7 @@ Makefile\.old$ ^PNG/testout/ ^SGI/testout/ ^TIFF/testout/ +^T1/testout/ # generated from .xs ^CountColor/CountColor\.c$ @@ -70,6 +75,7 @@ Makefile\.old$ ^PNG/PNG\.c$ ^SGI/SGI\.c$ ^TIFF/TIFF\.c$ +^T1/T1\.c$ ^.*/Changes$ ^blib/ diff --git a/Makefile.PL b/Makefile.PL index 40677cca..1914e7f5 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -597,19 +597,19 @@ sub init { # should use this option instead of the 'gif' option} # }; - $formats{'T1-fonts'}={ - order=>'30', - def=>'HAVE_LIBT1', - inccheck=>sub { -e catfile($_[0], 't1lib.h') }, - libcheck=>sub { $_[0] eq "libt1$aext" or $_[0] eq "libt1.$lext" }, - libfiles=>'-lt1', - objfiles=>'', - docs=>q{ - postscript t1 fonts are scalable fonts. They can include - ligatures and kerning information and generally yield good - visual quality. We depend on libt1 to rasterize the fonts - for use in images.} - }; +# $formats{'T1-fonts'}={ +# order=>'30', +# def=>'HAVE_LIBT1', +# inccheck=>sub { -e catfile($_[0], 't1lib.h') }, +# libcheck=>sub { $_[0] eq "libt1$aext" or $_[0] eq "libt1.$lext" }, +# libfiles=>'-lt1', +# objfiles=>'', +# docs=>q{ +# postscript t1 fonts are scalable fonts. They can include +# ligatures and kerning information and generally yield good +# visual quality. We depend on libt1 to rasterize the fonts +# for use in images.} +# }; $formats{'TT-fonts'}= { diff --git a/T1/.gitignore b/T1/.gitignore new file mode 100644 index 00000000..3dc3c585 --- /dev/null +++ b/T1/.gitignore @@ -0,0 +1 @@ +T1.c diff --git a/T1/Makefile.PL b/T1/Makefile.PL new file mode 100644 index 00000000..14577c7a --- /dev/null +++ b/T1/Makefile.PL @@ -0,0 +1,120 @@ +#!perl -w +use strict; +use ExtUtils::MakeMaker; +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; + +my $MM_ver = eval $ExtUtils::MakeMaker::VERSION; + +my %opts = + ( + NAME => 'Imager::Font::T1', + VERSION_FROM => 'T1.pm', + OBJECT => 'T1.o imt1.o', + clean => { FILES => 'testout' }, + ); + +my @inc; +if ($BUILDING_IMAGER) { + push @inc, "-I.."; + unshift @INC, "../lib"; +} +else { + unshift @INC, "inc"; + print "T1Lib: 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.80_01" ); + 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 => "git://git.imager.perl.org/imager-type1.git", + web => "http://git.imager.perl.org/imager-type1.git/", + type => "git", + }, + }, + }; + $opts{PREREQ_PM} = + { + @Imager_req, + }; + } +} + +require Imager::Probe; + +my %probe = + ( + name => "T1Lib", + inccheck => + sub { -e File::Spec->catfile($_[0], "t1lib.h") }, + libbase => "t1", + testcode => _t1_test_code(), + testcodeheaders => [ "stdio.h", "string.h", "t1lib.h" ], + incpath => \@incpaths, + libpath => \@libpaths, + ); + +my $probe_res = Imager::Probe->probe(\%probe); +if ($probe_res) { + push @inc, $probe_res->{INC}; + $opts{LIBS} = $probe_res->{LIBS}; + $opts{DEFINE} = $probe_res->{DEFINE}; + $opts{INC} = "@inc"; + + if ($MM_ver > 6.06) { + $opts{AUTHOR} = 'Tony Cook '; + $opts{ABSTRACT} = 'T1Lib font driver for Imager'; + } + + WriteMakefile(%opts); +} +else { + if ($BUILDING_IMAGER) { + ExtUtils::MakeMaker::WriteEmptyMakefile(%opts); + } + else { + # fail in good way + die "OS unsupported: T1Lib headers/libraries not found\n"; + } +} + +sub _t1_test_code { + return <<'CODE'; +int font_id; +if (T1_InitLib(IGNORE_CONFIGFILE|IGNORE_FONTDATABASE) == NULL) { + fprintf(stderr, "T1Lib: Cannot initialize\n"); + return 1; +} +T1_CloseLib(); +return 0; +CODE +} + diff --git a/T1/T1.pm b/T1/T1.pm new file mode 100644 index 00000000..0ae68be4 --- /dev/null +++ b/T1/T1.pm @@ -0,0 +1,211 @@ +package Imager::Font::T1; +use strict; +use Imager::Color; +use vars qw(@ISA $VERSION); +@ISA = qw(Imager::Font); + +BEGIN { + $VERSION = "1.011"; + + eval { + require XSLoader; + XSLoader::load('Imager::Font::T1', $VERSION); + 1; + } or do { + require DynaLoader; + push @ISA, 'DynaLoader'; + bootstrap Imager::Font::T1 $VERSION; + }; +} + + +*_first = \&Imager::Font::_first; + +my $t1aa; + +# $T1AA is in there because for some reason (probably cache related) antialiasing +# is a system wide setting in t1 lib. + +sub t1_set_aa_level { + if (!defined $t1aa or $_[0] != $t1aa) { + i_t1_set_aa($_[0]); + $t1aa=$_[0]; + } +} + +sub new { + my $class = shift; + my %hsh=(color=>Imager::Color->new(255,0,0,0), + size=>15, + @_); + + unless ($hsh{file}) { + $Imager::ERRSTR = "No font file specified"; + return; + } + unless (-e $hsh{file}) { + $Imager::ERRSTR = "Font file $hsh{file} not found"; + return; + } + unless ($Imager::formats{t1}) { + $Imager::ERRSTR = "Type 1 fonts not supported in this build"; + return; + } + # we want to avoid T1Lib's file search mechanism + unless ($hsh{file} =~ m!^/! + || $hsh{file} =~ m!^\.\/?/! + || $^O =~ /^(MSWin32|cygwin)$/ && $hsh{file} =~ /^[a-z]:/) { + $hsh{file} = './' . $hsh{file}; + } + + if($hsh{afm}) { + unless (-e $hsh{afm}) { + $Imager::ERRSTR = "Afm file $hsh{afm} not found"; + return; + } + unless ($hsh{afm} =~ m!^/! + || $hsh{afm} =~ m!^\./! + || $^O =~ /^(MSWin32|cygwin)$/ && $hsh{file} =~ /^[a-z]:/) { + $hsh{file} = './' . $hsh{file}; + } + } else { + $hsh{afm} = 0; + } + + my $id = i_t1_new($hsh{file},$hsh{afm}); + unless ($id >= 0) { # the low-level code may miss some error handling + $Imager::ERRSTR = "Could not load font ($id)"; + return; + } + return bless { + id => $id, + aa => $hsh{aa} || 0, + file => $hsh{file}, + type => 't1', + size => $hsh{size}, + color => $hsh{color}, + }, $class; +} + +sub _draw { + my $self = shift; + my %input = @_; + t1_set_aa_level($input{aa}); + my $flags = ''; + $flags .= 'u' if $input{underline}; + $flags .= 's' if $input{strikethrough}; + $flags .= 'o' if $input{overline}; + if (exists $input{channel}) { + i_t1_cp($input{image}{IMG}, $input{'x'}, $input{'y'}, + $input{channel}, $self->{id}, $input{size}, + $input{string}, length($input{string}), $input{align}, + $input{utf8}, $flags); + } else { + i_t1_text($input{image}{IMG}, $input{'x'}, $input{'y'}, + $input{color}, $self->{id}, $input{size}, + $input{string}, length($input{string}), + $input{align}, $input{utf8}, $flags); + } + + return $self; +} + +sub _bounding_box { + my $self = shift; + my %input = @_; + my $flags = ''; + $flags .= 'u' if $input{underline}; + $flags .= 's' if $input{strikethrough}; + $flags .= 'o' if $input{overline}; + return i_t1_bbox($self->{id}, $input{size}, $input{string}, + length($input{string}), $input{utf8}, $flags); +} + +# check if the font has the characters in the given string +sub has_chars { + my ($self, %hsh) = @_; + + unless (defined $hsh{string} && length $hsh{string}) { + $Imager::ERRSTR = "No string supplied to \$font->has_chars()"; + return; + } + return i_t1_has_chars($self->{id}, $hsh{string}, + _first($hsh{'utf8'}, $self->{utf8}, 0)); +} + +sub utf8 { + 1; +} + +sub face_name { + my ($self) = @_; + + i_t1_face_name($self->{id}); +} + +sub glyph_names { + my ($self, %input) = @_; + + my $string = $input{string}; + defined $string + or return Imager->_set_error("no string parameter passed to glyph_names"); + my $utf8 = _first($input{utf8} || 0); + + i_t1_glyph_name($self->{id}, $string, $utf8); +} + + +1; + +__END__ + +=head1 NAME + + Imager::Font::Type1 - low-level functions for Type1 fonts + +=head1 DESCRIPTION + +Imager::Font creates a Imager::Font::Type1 object when asked to create +a font object based on a C<.pfb> file. + +See Imager::Font to see how to use this type. + +This class provides low-level functions that require the caller to +perform data validation + +By default Imager no longer creates the F log file. You +can re-enable that by calling Imager::init() with the C option: + + Imager::init(t1log=>1); + +This must be called before creating any fonts. + +Currently specific to Imager::Font::Type1, you can use the following +flags when drawing text or calculating a bounding box: + +=for stopwords overline strikethrough + +=over + +=item * + +C - Draw the text with an underline. + +=item * + +C - Draw the text with an overline. + +=item * + +C - Draw the text with a strikethrough. + +=back + +Obviously, if you're calculating the bounding box the size of the line +is included in the box, and the line isn't drawn :) + +=head1 AUTHOR + +Addi, Tony + +=cut diff --git a/T1/T1.xs b/T1/T1.xs new file mode 100644 index 00000000..255eec64 --- /dev/null +++ b/T1/T1.xs @@ -0,0 +1,199 @@ +#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 "imt1.h" + +DEFINE_IMAGER_CALLBACKS; + +MODULE = Imager::Font::T1 PACKAGE = Imager::Font::T1 + +undef_int +i_init_t1(t1log) + int t1log + +void +i_t1_set_aa(st) + int st + +int +i_t1_new(pfb,afm) + char* pfb + char* afm + +int +i_t1_destroy(font_id) + int font_id + + +undef_int +i_t1_cp(im,xb,yb,channel,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") + Imager::ImgRaw im + int xb + int yb + int channel + int fontnum + float points + SV* str_sv + int align + int utf8 + char* flags + PREINIT: + char *str; + STRLEN len; + CODE: +#ifdef SvUTF8 + if (SvUTF8(str_sv)) + utf8 = 1; +#endif + str = SvPV(str_sv, len); + RETVAL = i_t1_cp(im, xb,yb,channel,fontnum,points,str,len,align, + utf8,flags); + OUTPUT: + RETVAL + + +void +i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="") + int fontnum + float point + SV* str_sv + int utf8 + char* flags + PREINIT: + char *str; + STRLEN len; + int cords[BOUNDING_BOX_COUNT]; + int i; + int rc; + PPCODE: +#ifdef SvUTF8 + if (SvUTF8(str_sv)) + utf8 = 1; +#endif + str = SvPV(str_sv, len); + rc = i_t1_bbox(fontnum,point,str,len,cords,utf8,flags); + if (rc > 0) { + EXTEND(SP, rc); + for (i = 0; i < rc; ++i) + PUSHs(sv_2mortal(newSViv(cords[i]))); + } + + + +undef_int +i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,flags="") + Imager::ImgRaw im + int xb + int yb + Imager::Color cl + int fontnum + float points + SV* str_sv + int align + int utf8 + char* flags + PREINIT: + char *str; + STRLEN len; + CODE: +#ifdef SvUTF8 + if (SvUTF8(str_sv)) + utf8 = 1; +#endif + str = SvPV(str_sv, len); + RETVAL = i_t1_text(im, xb,yb,cl,fontnum,points,str,len,align, + utf8,flags); + OUTPUT: + RETVAL + +void +i_t1_has_chars(handle, text_sv, utf8 = 0) + int handle + SV *text_sv + int utf8 + PREINIT: + char const *text; + STRLEN len; + char *work; + int count; + int i; + PPCODE: +#ifdef SvUTF8 + if (SvUTF8(text_sv)) + utf8 = 1; +#endif + text = SvPV(text_sv, len); + work = mymalloc(len); + count = i_t1_has_chars(handle, text, len, utf8, work); + if (GIMME_V == G_ARRAY) { + EXTEND(SP, count); + for (i = 0; i < count; ++i) { + PUSHs(sv_2mortal(newSViv(work[i]))); + } + } + else { + EXTEND(SP, 1); + PUSHs(sv_2mortal(newSVpv(work, count))); + } + myfree(work); + +void +i_t1_face_name(handle) + int handle + PREINIT: + char name[255]; + int len; + PPCODE: + len = i_t1_face_name(handle, name, sizeof(name)); + if (len) { + EXTEND(SP, 1); + PUSHs(sv_2mortal(newSVpv(name, strlen(name)))); + } + +void +i_t1_glyph_name(handle, text_sv, utf8 = 0) + int handle + SV *text_sv + int utf8 + PREINIT: + char const *text; + STRLEN work_len; + size_t len; + char name[255]; + PPCODE: +#ifdef SvUTF8 + if (SvUTF8(text_sv)) + utf8 = 1; +#endif + text = SvPV(text_sv, work_len); + len = work_len; + while (len) { + unsigned long ch; + if (utf8) { + ch = i_utf8_advance(&text, &len); + if (ch == ~0UL) { + i_push_error(0, "invalid UTF8 character"); + break; + } + } + else { + ch = *text++; + --len; + } + EXTEND(SP, 1); + if (i_t1_glyph_name(handle, ch, name, sizeof(name))) { + PUSHs(sv_2mortal(newSVpv(name, 0))); + } + else { + PUSHs(&PL_sv_undef); + } + } + +BOOT: + PERL_INITIALIZE_IMAGER_CALLBACKS; diff --git a/fontfiles/ExistenceTest.afm b/T1/fontfiles/ExistenceTest.afm similarity index 100% rename from fontfiles/ExistenceTest.afm rename to T1/fontfiles/ExistenceTest.afm diff --git a/fontfiles/ExistenceTest.pfb b/T1/fontfiles/ExistenceTest.pfb similarity index 100% rename from fontfiles/ExistenceTest.pfb rename to T1/fontfiles/ExistenceTest.pfb diff --git a/fontfiles/SpaceTest.afm b/T1/fontfiles/SpaceTest.afm similarity index 100% rename from fontfiles/SpaceTest.afm rename to T1/fontfiles/SpaceTest.afm diff --git a/fontfiles/SpaceTest.pfb b/T1/fontfiles/SpaceTest.pfb similarity index 100% rename from fontfiles/SpaceTest.pfb rename to T1/fontfiles/SpaceTest.pfb diff --git a/fontfiles/dcr10.afm b/T1/fontfiles/dcr10.afm similarity index 100% rename from fontfiles/dcr10.afm rename to T1/fontfiles/dcr10.afm diff --git a/fontfiles/dcr10.pfb b/T1/fontfiles/dcr10.pfb similarity index 100% rename from fontfiles/dcr10.pfb rename to T1/fontfiles/dcr10.pfb diff --git a/T1/imt1.c b/T1/imt1.c new file mode 100644 index 00000000..1629a147 --- /dev/null +++ b/T1/imt1.c @@ -0,0 +1,684 @@ +#include "imext.h" +#include "imt1.h" +#include +#include + +static int t1_get_flags(char const *flags); +static char *t1_from_utf8(char const *in, size_t len, int *outlen); + +static void t1_push_error(void); + +static int t1_active_fonts = 0; +static int t1_initialized = 0; + +/* +=item i_init_t1(t1log) + +Initializes the t1lib font rendering engine. + +=cut +*/ + +undef_int +i_init_t1(int t1log) { + int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; + mm_log((1,"init_t1()\n")); + + i_clear_error(); + + if (t1_active_fonts) { + mm_log((1, "Cannot re-initialize T1 - active fonts\n")); + i_push_error(0, "Cannot re-initialize T1 - active fonts"); + return 1; + } + + if (t1_initialized) { + T1_CloseLib(); + } + + if (t1log) + init_flags |= LOGFILE; + if ((T1_InitLib(init_flags) == NULL)){ + mm_log((1,"Initialization of t1lib failed\n")); + i_push_error(0, "T1_InitLib failed"); + return(1); + } + T1_SetLogLevel(T1LOG_DEBUG); + i_t1_set_aa(1); /* Default Antialias value */ + + ++t1_initialized; + + return(0); +} + +/* +=item i_close_t1() + +Shuts the t1lib font rendering engine down. + + This it seems that this function is never used. + +=cut +*/ + +void +i_close_t1(void) { + T1_CloseLib(); + t1_initialized = 0; +} + + +/* +=item i_t1_new(pfb, afm) + +Loads the fonts with the given filenames, returns its font id + + pfb - path to pfb file for font + afm - path to afm file for font + +=cut +*/ + +int +i_t1_new(char *pfb,char *afm) { + int font_id; + + i_clear_error(); + + if (!t1_initialized && i_init_t1(0)) + return -1; + + mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); + font_id = T1_AddFont(pfb); + if (font_id<0) { + mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); + return font_id; + } + + if (afm != NULL) { + mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm)); + if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm)); + } + + ++t1_active_fonts; + + return font_id; +} + +/* +=item i_t1_destroy(font_id) + +Frees resources for a t1 font with given font id. + + font_id - number of the font to free + +=cut +*/ + +int +i_t1_destroy(int font_id) { + mm_log((1,"i_t1_destroy(font_id %d)\n",font_id)); + + --t1_active_fonts; + + return T1_DeleteFont(font_id); +} + + +/* +=item i_t1_set_aa(st) + +Sets the antialiasing level of the t1 library. + + st - 0 = NONE, 1 = LOW, 2 = HIGH. + +=cut +*/ + +void +i_t1_set_aa(int st) { + int i; + unsigned long cst[17]; + switch(st) { + case 0: + T1_AASetBitsPerPixel( 8 ); + T1_AASetLevel( T1_AA_NONE ); + T1_AANSetGrayValues( 0, 255 ); + mm_log((1,"setting T1 antialias to none\n")); + break; + case 1: + T1_AASetBitsPerPixel( 8 ); + T1_AASetLevel( T1_AA_LOW ); + T1_AASetGrayValues( 0,65,127,191,255 ); + mm_log((1,"setting T1 antialias to low\n")); + break; + case 2: + T1_AASetBitsPerPixel(8); + T1_AASetLevel(T1_AA_HIGH); + for(i=0;i<17;i++) cst[i]=(i*255)/16; + T1_AAHSetGrayValues( cst ); + mm_log((1,"setting T1 antialias to high\n")); + } +} + + +/* +=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align) + +Interface to text rendering into a single channel in an image + + im pointer to image structure + xb x coordinate of start of string + yb y coordinate of start of string ( see align ) + channel - destination channel + fontnum - t1 library font id + points - number of points in fontheight + str - string to render + len - string length + align - (0 - top of font glyph | 1 - baseline ) + +=cut +*/ + +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) { + GLYPH *glyph; + int xsize,ysize,x,y; + i_color val; + int mod_flags = t1_get_flags(flags); + + unsigned int ch_mask_store; + + if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } + + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); + myfree(work); + } + else { + glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); + } + if (glyph == NULL) + return 0; + + mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); + mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); + mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); + mm_log((1,"bpp: %d\n",glyph->bpp)); + + xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; + ysize=glyph->metrics.ascent-glyph->metrics.descent; + + mm_log((1,"width: %d height: %d\n",xsize,ysize)); + + ch_mask_store=im->ch_mask; + im->ch_mask=1<metrics.leftSideBearing; yb-=glyph->metrics.ascent; } + + for(y=0;ybits[y*xsize+x]; + i_ppix(im,x+xb,y+yb,&val); + } + + im->ch_mask=ch_mask_store; + return 1; +} + +static void +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) + bbox->llx = 0; + if (str[len-1] == space_position && bbox->urx < advance) + bbox->urx = advance; + if (bbox->lly > bbox->ury) + bbox->lly = bbox->ury = 0; +} + +/* +=item i_t1_bbox(handle, fontnum, points, str, len, cords) + +function to get a strings bounding box given the font id and sizes + + handle - pointer to font handle + fontnum - t1 library font id + points - number of points in fontheight + str - string to measure + len - string length + cords - the bounding box (modified in place) + +=cut +*/ + +int +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); + int advance; + int space_position = T1_GetEncodingIndex(fontnum, "space"); + + mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len)); + T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */ + + if (len == 0) { + /* len == 0 has special meaning to T1lib, but it means there's + nothing to draw, so return that */ + bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0; + advance = 0; + } + else { + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); + bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); + t1_fix_bbox(&bbox, work, worklen, advance, space_position); + myfree(work); + } + else { + advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags); + bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags); + t1_fix_bbox(&bbox, str, len, advance, space_position); + } + } + gbbox = T1_GetFontBBox(fontnum); + + mm_log((1,"bbox: (%d,%d,%d,%d)\n", + (int)(bbox.llx*points/1000), + (int)(gbbox.lly*points/1000), + (int)(bbox.urx*points/1000), + (int)(gbbox.ury*points/1000), + (int)(bbox.lly*points/1000), + (int)(bbox.ury*points/1000) )); + + + cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000; + cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000; + + cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000; + cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000; + + cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000; + cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000; + + cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000; + cords[BBOX_RIGHT_BEARING] = + cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; + + return BBOX_RIGHT_BEARING+1; +} + + +/* +=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align) + +Interface to text rendering in a single color onto an image + + im - pointer to image structure + xb - x coordinate of start of string + yb - y coordinate of start of string ( see align ) + cl - color to draw the text in + fontnum - t1 library font id + points - number of points in fontheight + str - char pointer to string to render + len - string length + align - (0 - top of font glyph | 1 - baseline ) + +=cut +*/ + +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) { + GLYPH *glyph; + int xsize,ysize,y; + int mod_flags = t1_get_flags(flags); + i_render *r; + + if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } + + if (utf8) { + int worklen; + char *work = t1_from_utf8(str, len, &worklen); + glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); + myfree(work); + } + else { + /* T1_AASetString() accepts a char * not a const char */ + glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); + } + if (glyph == NULL) + return 0; + + mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); + mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); + mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); + mm_log((1,"bpp: %d\n",glyph->bpp)); + + xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; + ysize=glyph->metrics.ascent-glyph->metrics.descent; + + mm_log((1,"width: %d height: %d\n",xsize,ysize)); + + if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } + + r = i_render_new(im, xsize); + for(y=0;ybits+y*xsize, cl); + } + i_render_delete(r); + + return 1; +} + +/* +=item t1_get_flags(flags) + +Processes the characters in I to create a mod_flags value used +by some T1Lib functions. + +=cut + */ +static int +t1_get_flags(char const *flags) { + int mod_flags = T1_KERNING; + + while (*flags) { + switch (*flags++) { + case 'u': case 'U': mod_flags |= T1_UNDERLINE; break; + case 'o': case 'O': mod_flags |= T1_OVERLINE; break; + case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break; + /* ignore anything we don't recognize */ + } + } + + return mod_flags; +} + +/* +=item t1_from_utf8(char const *in, size_t len, int *outlen) + +Produces an unencoded version of I by dropping any Unicode +character over 255. + +Returns a newly allocated buffer which should be freed with myfree(). +Sets *outlen to the number of bytes used in the output string. + +=cut +*/ + +static char * +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; + unsigned long c; + + while (len) { + c = i_utf8_advance(&in, &len); + if (c == ~0UL) { + myfree(out); + i_push_error(0, "invalid UTF8 character"); + return 0; + } + /* yeah, just drop them */ + if (c < 0x100) { + *p++ = (char)c; + } + } + *p = '\0'; + *outlen = p - out; + + return out; +} + +/* +=item i_t1_has_chars(font_num, text, len, utf8, out) + +Check if the given characters are defined by the font. Note that len +is the number of bytes, not the number of characters (when utf8 is +non-zero). + +out[char index] will be true if the character exists. + +Accepts UTF-8, but since T1 can only have 256 characters, any chars +with values over 255 will simply be returned as false. + +Returns the number of characters that were checked. + +=cut +*/ + +int +i_t1_has_chars(int font_num, const char *text, size_t len, int utf8, + char *out) { + int count = 0; + + mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", + font_num, text, len, utf8)); + + i_clear_error(); + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + + while (len) { + unsigned long c; + if (utf8) { + c = i_utf8_advance(&text, &len); + if (c == ~0UL) { + i_push_error(0, "invalid UTF8 character"); + return 0; + } + } + else { + c = (unsigned char)*text++; + --len; + } + + if (c >= 0x100) { + /* limit of 256 characters for T1 */ + *out++ = 0; + } + else { + char const * name = T1_GetCharName(font_num, (unsigned char)c); + + if (name) { + *out++ = strcmp(name, ".notdef") != 0; + } + else { + mm_log((2, " No name found for character %lx\n", c)); + *out++ = 0; + } + } + ++count; + } + + return count; +} + +/* +=item i_t1_face_name(font_num, name_buf, name_buf_size) + +Copies the face name of the given C to C. Returns +the number of characters required to store the name (which can be +larger than C, including the space required to store +the terminating NUL). + +If name_buf is too small (as specified by name_buf_size) then the name +will be truncated. name_buf will always be NUL termintaed. + +=cut +*/ + +int +i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) { + char *name; + + T1_errno = 0; + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + name = T1_GetFontName(font_num); + + if (name) { + strncpy(name_buf, name, name_buf_size); + name_buf[name_buf_size-1] = '\0'; + return strlen(name) + 1; + } + else { + t1_push_error(); + return 0; + } +} + +int +i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, + size_t name_buf_size) { + char *name; + + i_clear_error(); + if (ch > 0xFF) { + return 0; + } + if (T1_LoadFont(font_num)) { + t1_push_error(); + return 0; + } + name = T1_GetCharName(font_num, (unsigned char)ch); + if (name) { + if (strcmp(name, ".notdef")) { + strncpy(name_buf, name, name_buf_size); + name_buf[name_buf_size-1] = '\0'; + return strlen(name) + 1; + } + else { + return 0; + } + } + else { + t1_push_error(); + return 0; + } +} + +static void +t1_push_error(void) { + switch (T1_errno) { + case 0: + i_push_error(0, "No error"); + break; + +#ifdef T1ERR_SCAN_FONT_FORMAT + case T1ERR_SCAN_FONT_FORMAT: + i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); + break; +#endif + +#ifdef T1ERR_SCAN_FILE_OPEN_ERR + case T1ERR_SCAN_FILE_OPEN_ERR: + i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); + break; +#endif + +#ifdef T1ERR_SCAN_OUT_OF_MEMORY + case T1ERR_SCAN_OUT_OF_MEMORY: + i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); + break; +#endif + +#ifdef T1ERR_SCAN_ERROR + case T1ERR_SCAN_ERROR: + i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); + break; +#endif + +#ifdef T1ERR_SCAN_FILE_EOF + case T1ERR_SCAN_FILE_EOF: + i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); + break; +#endif + +#ifdef T1ERR_PATH_ERROR + case T1ERR_PATH_ERROR: + i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); + break; +#endif + +#ifdef T1ERR_PARSE_ERROR + case T1ERR_PARSE_ERROR: + i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); + break; +#endif + +#ifdef T1ERR_TYPE1_ABORT + case T1ERR_TYPE1_ABORT: + i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); + break; +#endif + +#ifdef T1ERR_INVALID_FONTID + case T1ERR_INVALID_FONTID: + i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); + break; +#endif + +#ifdef T1ERR_INVALID_PARAMETER + case T1ERR_INVALID_PARAMETER: + i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); + break; +#endif + +#ifdef T1ERR_OP_NOT_PERMITTED + case T1ERR_OP_NOT_PERMITTED: + i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); + break; +#endif + +#ifdef T1ERR_ALLOC_MEM + case T1ERR_ALLOC_MEM: + i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); + break; +#endif + +#ifdef T1ERR_FILE_OPEN_ERR + case T1ERR_FILE_OPEN_ERR: + i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); + break; +#endif + +#ifdef T1ERR_UNSPECIFIED + case T1ERR_UNSPECIFIED: + i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); + break; +#endif + +#ifdef T1ERR_NO_AFM_DATA + case T1ERR_NO_AFM_DATA: + i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); + break; +#endif + +#ifdef T1ERR_X11 + case T1ERR_X11: + i_push_error(T1ERR_X11, "X11"); + break; +#endif + +#ifdef T1ERR_COMPOSITE_CHAR + case T1ERR_COMPOSITE_CHAR: + i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); + break; +#endif + + default: + i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); + } +} + diff --git a/T1/imt1.h b/T1/imt1.h new file mode 100644 index 00000000..9b3bbb75 --- /dev/null +++ b/T1/imt1.h @@ -0,0 +1,40 @@ +#ifndef IMAGER_IMT1_H +#define IMAGER_IMT1_H + +#include "imdatatypes.h" + +extern undef_int +i_init_t1(int t1log); + +extern void +i_close_t1(void); + +extern int +i_t1_new(char *pfb,char *afm); + +extern int +i_t1_destroy(int font_id); + +extern void +i_t1_set_aa(int st); + +extern 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); + +extern int +i_t1_bbox(int fontnum,float points,const char *str,size_t len,int cords[6], int utf8,char const *flags); + +extern 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); + +extern int +i_t1_has_chars(int font_num, const char *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); +#endif diff --git a/t/t30t1font.t b/T1/t/t10type1.t similarity index 86% rename from t/t30t1font.t rename to T1/t/t10type1.t index c24c0937..ebe73f6e 100644 --- a/t/t30t1font.t +++ b/T1/t/t10type1.t @@ -1,27 +1,22 @@ #!perl -w -# Before `make install' is performed this script should be runnable with -# `make test'. After `make install' it should work as `perl test.pl' - -######################### We start with some black magic to print on failure. - -# Change 1..1 below to 1..last_test_to_print . -# (It may become useful if the test is moved to ./t subdirectory.) use strict; use Test::More; use Imager ':all'; use Imager::Test qw(diff_text_with_nul is_color3); +use Imager::Font::T1; use Cwd qw(getcwd abs_path); #$Imager::DEBUG=1; -i_has_format("t1") - or plan skip_all => "t1lib unavailble or disabled"; +plan tests => 97; + +ok($Imager::formats{t1}, "must have t1"); -plan tests => 95; +ok((-d "testout" or mkdir "testout"), "make output directory"); -d "testout" or mkdir "testout"; -init_log("testout/t30t1font.log",1); +init_log("testout/t10type1.log",1); my $deffont = 'fontfiles/dcr10.pfb'; @@ -47,7 +42,7 @@ SKIP: init(t1log=>0); unlink "t1lib.log"; - my $fnum=Imager::i_t1_new($fontname_pfb,$fontname_afm); # this will load the pfb font + my $fnum=Imager::Font::T1::i_t1_new($fontname_pfb,$fontname_afm); # this will load the pfb font unless (ok($fnum >= 0, "load font $fontname_pfb")) { skip("without the font I can't do a thing", 90); } @@ -55,11 +50,11 @@ SKIP: my $bgcolor=Imager::Color->new(255,0,0,0); my $overlay=Imager::ImgRaw::new(200,70,3); - ok(i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1), "i_t1_cp"); + ok(Imager::Font::T1::i_t1_cp($overlay,5,50,1,$fnum,50.0,'XMCLH',5,1), "i_t1_cp"); i_line($overlay,0,50,100,50,$bgcolor,1); - my @bbox=i_t1_bbox(0,50.0,'XMCLH',5); + my @bbox=Imager::Font::T1::i_t1_bbox(0,50.0,'XMCLH',5); is(@bbox, 8, "i_t1_bbox"); print "# bbox: ($bbox[0], $bbox[1]) - ($bbox[2], $bbox[3])\n"; @@ -72,8 +67,8 @@ SKIP: $bgcolor=Imager::Color::set($bgcolor,200,200,200,0); my $backgr=Imager::ImgRaw::new(280,300,3); - i_t1_set_aa(2); - ok(i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1), "i_t1_text"); + Imager::Font::T1::i_t1_set_aa(2); + ok(Imager::Font::T1::i_t1_text($backgr,10,100,$bgcolor,$fnum,150.0,'test',4,1), "i_t1_text"); # "UTF8" tests # for perl < 5.6 we can hand-encode text @@ -84,9 +79,9 @@ SKIP: my $text = pack("C*", 0x41, 0xC2, 0xA1, 0xE2, 0x80, 0x90, 0x41); my $alttext = "A\xA1A"; - my @utf8box = i_t1_bbox($fnum, 50.0, $text, length($text), 1); + my @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 1); is(@utf8box, 8, "utf8 bbox element count"); - my @base = i_t1_bbox($fnum, 50.0, $alttext, length($alttext), 0); + my @base = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $alttext, length($alttext), 0); is(@base, 8, "alt bbox element count"); my $maxdiff = $fontname_pfb eq $deffont ? 0 : $base[2] / 3; print "# (@utf8box vs @base)\n"; @@ -94,9 +89,9 @@ SKIP: "compare box sizes $utf8box[2] vs $base[2] (maxerror $maxdiff)"); # hand-encoded UTF8 drawing - ok(i_t1_text($backgr, 10, 140, $bgcolor, $fnum, 32, $text, length($text), 1,1), "draw hand-encoded UTF8"); + ok(Imager::Font::T1::i_t1_text($backgr, 10, 140, $bgcolor, $fnum, 32, $text, length($text), 1,1), "draw hand-encoded UTF8"); - ok(i_t1_cp($backgr, 80, 140, 1, $fnum, 32, $text, length($text), 1, 1), + ok(Imager::Font::T1::i_t1_cp($backgr, 80, 140, 1, $fnum, 32, $text, length($text), 1, 1), "cp hand-encoded UTF8"); # ok, try native perl UTF8 if available @@ -108,16 +103,16 @@ SKIP: # versions eval q{$text = "A\xA1\x{2010}A"}; # A, a with ogonek, HYPHEN, A in our test font #$text = "A".chr(0xA1).chr(0x2010)."A"; # this one works too - ok(i_t1_text($backgr, 10, 180, $bgcolor, $fnum, 32, $text, length($text), 1), + ok(Imager::Font::T1::i_t1_text($backgr, 10, 180, $bgcolor, $fnum, 32, $text, length($text), 1), "draw UTF8"); - ok(i_t1_cp($backgr, 80, 180, 1, $fnum, 32, $text, length($text), 1), + ok(Imager::Font::T1::i_t1_cp($backgr, 80, 180, 1, $fnum, 32, $text, length($text), 1), "cp UTF8"); - @utf8box = i_t1_bbox($fnum, 50.0, $text, length($text), 0); + @utf8box = Imager::Font::T1::i_t1_bbox($fnum, 50.0, $text, length($text), 0); is(@utf8box, 8, "native utf8 bbox element count"); ok(abs($utf8box[2] - $base[2]) <= $maxdiff, "compare box sizes native $utf8box[2] vs $base[2] (maxerror $maxdiff)"); eval q{$text = "A\xA1\xA2\x01\x1F\x{0100}A"}; - ok(i_t1_text($backgr, 10, 220, $bgcolor, $fnum, 32, $text, 0, 1, 0, "uso"), + ok(Imager::Font::T1::i_t1_text($backgr, 10, 220, $bgcolor, $fnum, 32, $text, 0, 1, 0, "uso"), "more complex output"); } @@ -127,36 +122,36 @@ SKIP: i_writeppm_wiol($backgr, $IO); close(FH); - my $rc=i_t1_destroy($fnum); + my $rc=Imager::Font::T1::i_t1_destroy($fnum); unless (ok($rc >= 0, "i_t1_destroy")) { print "# i_t1_destroy failed: rc=$rc\n"; } - print "# debug: ",join(" x ",i_t1_bbox(0,50,"eses",4) ),"\n"; - print "# debug: ",join(" x ",i_t1_bbox(0,50,"llll",4) ),"\n"; + print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"eses",4) ),"\n"; + print "# debug: ",join(" x ",Imager::Font::T1::i_t1_bbox(0,50,"llll",4) ),"\n"; # character existance tests - uses the special ExistenceTest font my $exists_font = 'fontfiles/ExistenceTest.pfb'; my $exists_afm = 'fontfiles/ExistenceText.afm'; - -e $exists_font or die; + -e $exists_font or die "$exists_font not found"; - my $font_num = Imager::i_t1_new($exists_font, $exists_afm); + my $font_num = Imager::Font::T1::i_t1_new($exists_font, $exists_afm); SKIP: { ok($font_num >= 0, 'loading test font') or skip('Could not load test font', 6); # first the list interface - my @exists = Imager::i_t1_has_chars($font_num, "!A"); + my @exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A"); is(@exists, 2, "return count from has_chars"); ok($exists[0], "we have an exclamation mark"); ok(!$exists[1], "we have no uppercase A"); # then the scalar interface - my $exists = Imager::i_t1_has_chars($font_num, "!A"); + my $exists = Imager::Font::T1::i_t1_has_chars($font_num, "!A"); is(length($exists), 2, "return scalar length"); ok(ord(substr($exists, 0, 1)), "we have an exclamation mark"); ok(!ord(substr($exists, 1, 1)), "we have no upper-case A"); - i_t1_destroy($font_num); + Imager::Font::T1::i_t1_destroy($font_num); } my $font = Imager::Font->new(file=>$exists_font, type=>'t1'); @@ -181,7 +176,7 @@ SKIP: isnt($bbox[2], $bbox[5], "different advance to pos_width"); # names - my $face_name = Imager::i_t1_face_name($font->{id}); + my $face_name = Imager::Font::T1::i_t1_face_name($font->{id}); print "# face $face_name\n"; is($face_name, 'ExistenceTest', "face name"); $face_name = $font->face_name; @@ -266,8 +261,8 @@ SKIP: # the problem we had with spaces my $space_fontfile = "fontfiles/SpaceTest.pfb"; my $font = Imager::Font->new(file => $space_fontfile, type => 't1'); - ok($font, "loaded $deffont") - or skip("failed to load $deffont" . Imager->errstr, 13); + ok($font, "loaded $space_fontfile") + or skip("failed to load $space_fontfile" . Imager->errstr, 13); my $bbox = $font->bounding_box(string => "", size => 36); print "# empty string bbox: @$bbox\n"; is($bbox->start_offset, 0, "empty string start_offset"); diff --git a/T1/t/t20oo.t b/T1/t/t20oo.t new file mode 100644 index 00000000..7334f995 --- /dev/null +++ b/T1/t/t20oo.t @@ -0,0 +1,65 @@ +#!/usr/bin/perl -w +use strict; +use Imager; +use Test::More tests => 9; + +# extracted from t/t36oofont.t + +my $fontname_pfb = "fontfiles/dcr10.pfb"; + +my $green=Imager::Color->new(92,205,92,128); +die $Imager::ERRSTR unless $green; +my $red=Imager::Color->new(205, 92, 92, 255); +die $Imager::ERRSTR unless $red; + +ok((-d "testout" or mkdir "testout"), "make output directory"); + +init_log("testout/t20oo.log", 1); + +my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n"; + +my $font=Imager::Font->new(file=>$fontname_pfb,size=>25, type => "t1") + or die $img->{ERRSTR}; + +ok(1, "created font"); + +ok($img->string(font=>$font, text=>"XMCLH", 'x'=>100, 'y'=>100), + "draw text"); +$img->line(x1=>0, x2=>300, y1=>50, y2=>50, color=>$green); + +my $text="LLySja"; +my @bbox=$font->bounding_box(string=>$text, 'x'=>0, 'y'=>50); + +is(@bbox, 8, "bounding box list length"); + +$img->box(box=>\@bbox, color=>$green); + +# "utf8" support +$text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41); +ok($img->string(font=>$font, text=>$text, 'x'=>100, 'y'=>50, utf8=>1, + overline=>1), + "draw 'utf8' hand-encoded text"); + +ok($img->string(font=>$font, text=>$text, 'x'=>140, 'y'=>50, utf8=>1, + underline=>1, channel=>2), + "channel 'utf8' hand-encoded text"); + +SKIP: +{ + $] >= 5.006 + or skip("perl too old for native utf8", 2); + eval q{$text = "A\x{2010}A"}; + ok($img->string(font=>$font, text=>$text, 'x'=>180, 'y'=>50, + strikethrough=>1), + "draw native UTF8 text"); + ok($img->string(font=>$font, text=>$text, 'x'=>220, 'y'=>50, channel=>1), + "channel native UTF8 text"); +} + +ok($img->write(file=>"testout/t36oofont1.ppm", type=>'pnm'), + "write t36oofont1.ppm") + or print "# ",$img->errstr,"\n"; + +unless ($ENV{IMAGER_KEEP_FILES}) { + unlink "testout/t36oofont1.ppm"; +} diff --git a/font.c b/font.c index 19df9938..efb83550 100644 --- a/font.c +++ b/font.c @@ -9,7 +9,6 @@ #include #ifdef HAVE_LIBT1 -#include #endif @@ -50,713 +49,6 @@ Some of these functions are internal. */ -/* -=item i_init_fonts() - -Initialize font rendering libraries if they are avaliable. - -=cut -*/ - -undef_int -i_init_fonts(int t1log) { - mm_log((1,"Initializing fonts\n")); - -#ifdef HAVE_LIBT1 - if (i_init_t1(t1log)) - return 0; -#endif - - return(1); /* FIXME: Always true - check the return values of the init_t1 and init_tt functions */ -} - - - - -#ifdef HAVE_LIBT1 - -static int t1_get_flags(char const *flags); -static char *t1_from_utf8(char const *in, size_t len, int *outlen); - -static void t1_push_error(void); - -static int t1_active_fonts = 0; -static int t1_initialized = 0; - -/* -=item i_init_t1(t1log) - -Initializes the t1lib font rendering engine. - -=cut -*/ - -undef_int -i_init_t1(int t1log) { - int init_flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE; - mm_log((1,"init_t1()\n")); - - i_clear_error(); - - if (t1_active_fonts) { - mm_log((1, "Cannot re-initialize T1 - active fonts\n")); - i_push_error(0, "Cannot re-initialize T1 - active fonts"); - return 1; - } - - if (t1_initialized) { - T1_CloseLib(); - } - - if (t1log) - init_flags |= LOGFILE; - if ((T1_InitLib(init_flags) == NULL)){ - mm_log((1,"Initialization of t1lib failed\n")); - i_push_error(0, "T1_InitLib failed"); - return(1); - } - T1_SetLogLevel(T1LOG_DEBUG); - i_t1_set_aa(1); /* Default Antialias value */ - - ++t1_initialized; - - return(0); -} - - -/* -=item i_close_t1() - -Shuts the t1lib font rendering engine down. - - This it seems that this function is never used. - -=cut -*/ - -void -i_close_t1(void) { - T1_CloseLib(); - t1_initialized = 0; -} - - -/* -=item i_t1_new(pfb, afm) - -Loads the fonts with the given filenames, returns its font id - - pfb - path to pfb file for font - afm - path to afm file for font - -=cut -*/ - -int -i_t1_new(char *pfb,char *afm) { - int font_id; - - i_clear_error(); - - if (!t1_initialized && i_init_t1(0)) - return -1; - - mm_log((1,"i_t1_new(pfb %s,afm %s)\n",pfb,(afm?afm:"NULL"))); - font_id = T1_AddFont(pfb); - if (font_id<0) { - mm_log((1,"i_t1_new: Failed to load pfb file '%s' - return code %d.\n",pfb,font_id)); - return font_id; - } - - if (afm != NULL) { - mm_log((1,"i_t1_new: requesting afm file '%s'.\n",afm)); - if (T1_SetAfmFileName(font_id,afm)<0) mm_log((1,"i_t1_new: afm loading of '%s' failed.\n",afm)); - } - - ++t1_active_fonts; - - return font_id; -} - -/* -=item i_t1_destroy(font_id) - -Frees resources for a t1 font with given font id. - - font_id - number of the font to free - -=cut -*/ - -int -i_t1_destroy(int font_id) { - mm_log((1,"i_t1_destroy(font_id %d)\n",font_id)); - - --t1_active_fonts; - - return T1_DeleteFont(font_id); -} - - -/* -=item i_t1_set_aa(st) - -Sets the antialiasing level of the t1 library. - - st - 0 = NONE, 1 = LOW, 2 = HIGH. - -=cut -*/ - -void -i_t1_set_aa(int st) { - int i; - unsigned long cst[17]; - switch(st) { - case 0: - T1_AASetBitsPerPixel( 8 ); - T1_AASetLevel( T1_AA_NONE ); - T1_AANSetGrayValues( 0, 255 ); - mm_log((1,"setting T1 antialias to none\n")); - break; - case 1: - T1_AASetBitsPerPixel( 8 ); - T1_AASetLevel( T1_AA_LOW ); - T1_AASetGrayValues( 0,65,127,191,255 ); - mm_log((1,"setting T1 antialias to low\n")); - break; - case 2: - T1_AASetBitsPerPixel(8); - T1_AASetLevel(T1_AA_HIGH); - for(i=0;i<17;i++) cst[i]=(i*255)/16; - T1_AAHSetGrayValues( cst ); - mm_log((1,"setting T1 antialias to high\n")); - } -} - - -/* -=item i_t1_cp(im, xb, yb, channel, fontnum, points, str, len, align) - -Interface to text rendering into a single channel in an image - - im pointer to image structure - xb x coordinate of start of string - yb y coordinate of start of string ( see align ) - channel - destination channel - fontnum - t1 library font id - points - number of points in fontheight - str - string to render - len - string length - align - (0 - top of font glyph | 1 - baseline ) - -=cut -*/ - -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) { - GLYPH *glyph; - int xsize,ysize,x,y; - i_color val; - int mod_flags = t1_get_flags(flags); - - unsigned int ch_mask_store; - - if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } - - if (utf8) { - int worklen; - char *work = t1_from_utf8(str, len, &worklen); - glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); - myfree(work); - } - else { - glyph=T1_AASetString( fontnum, str, len, 0, mod_flags, points, NULL); - } - if (glyph == NULL) - return 0; - - mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); - mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); - mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); - mm_log((1,"bpp: %d\n",glyph->bpp)); - - xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; - ysize=glyph->metrics.ascent-glyph->metrics.descent; - - mm_log((1,"width: %d height: %d\n",xsize,ysize)); - - ch_mask_store=im->ch_mask; - im->ch_mask=1<metrics.leftSideBearing; yb-=glyph->metrics.ascent; } - - for(y=0;ybits[y*xsize+x]; - i_ppix(im,x+xb,y+yb,&val); - } - - im->ch_mask=ch_mask_store; - return 1; -} - -static void -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) - bbox->llx = 0; - if (str[len-1] == space_position && bbox->urx < advance) - bbox->urx = advance; - if (bbox->lly > bbox->ury) - bbox->lly = bbox->ury = 0; -} - -/* -=item i_t1_bbox(handle, fontnum, points, str, len, cords) - -function to get a strings bounding box given the font id and sizes - - handle - pointer to font handle - fontnum - t1 library font id - points - number of points in fontheight - str - string to measure - len - string length - cords - the bounding box (modified in place) - -=cut -*/ - -int -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); - int advance; - int space_position = T1_GetEncodingIndex(fontnum, "space"); - - mm_log((1,"i_t1_bbox(fontnum %d,points %.2f,str '%.*s', len %d)\n",fontnum,points,len,str,len)); - T1_LoadFont(fontnum); /* FIXME: Here a return code is ignored - haw haw haw */ - - if (len == 0) { - /* len == 0 has special meaning to T1lib, but it means there's - nothing to draw, so return that */ - bbox.llx = bbox.lly = bbox.urx = bbox.ury = 0; - advance = 0; - } - else { - if (utf8) { - int worklen; - char *work = t1_from_utf8(str, len, &worklen); - advance = T1_GetStringWidth(fontnum, work, worklen, 0, mod_flags); - bbox = T1_GetStringBBox(fontnum,work,worklen,0,mod_flags); - t1_fix_bbox(&bbox, work, worklen, advance, space_position); - myfree(work); - } - else { - advance = T1_GetStringWidth(fontnum, (char *)str, len, 0, mod_flags); - bbox = T1_GetStringBBox(fontnum,(char *)str,len,0,mod_flags); - t1_fix_bbox(&bbox, str, len, advance, space_position); - } - } - gbbox = T1_GetFontBBox(fontnum); - - mm_log((1,"bbox: (%d,%d,%d,%d)\n", - (int)(bbox.llx*points/1000), - (int)(gbbox.lly*points/1000), - (int)(bbox.urx*points/1000), - (int)(gbbox.ury*points/1000), - (int)(bbox.lly*points/1000), - (int)(bbox.ury*points/1000) )); - - - cords[BBOX_NEG_WIDTH]=((float)bbox.llx*points)/1000; - cords[BBOX_POS_WIDTH]=((float)bbox.urx*points)/1000; - - cords[BBOX_GLOBAL_DESCENT]=((float)gbbox.lly*points)/1000; - cords[BBOX_GLOBAL_ASCENT]=((float)gbbox.ury*points)/1000; - - cords[BBOX_DESCENT]=((float)bbox.lly*points)/1000; - cords[BBOX_ASCENT]=((float)bbox.ury*points)/1000; - - cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000; - cords[BBOX_RIGHT_BEARING] = - cords[BBOX_ADVANCE_WIDTH] - cords[BBOX_POS_WIDTH]; - - return BBOX_RIGHT_BEARING+1; -} - - -/* -=item i_t1_text(im, xb, yb, cl, fontnum, points, str, len, align) - -Interface to text rendering in a single color onto an image - - im - pointer to image structure - xb - x coordinate of start of string - yb - y coordinate of start of string ( see align ) - cl - color to draw the text in - fontnum - t1 library font id - points - number of points in fontheight - str - char pointer to string to render - len - string length - align - (0 - top of font glyph | 1 - baseline ) - -=cut -*/ - -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) { - GLYPH *glyph; - int xsize,ysize,y; - int mod_flags = t1_get_flags(flags); - i_render r; - - if (im == NULL) { mm_log((1,"i_t1_cp: Null image in input\n")); return(0); } - - if (utf8) { - int worklen; - char *work = t1_from_utf8(str, len, &worklen); - glyph=T1_AASetString( fontnum, work, worklen, 0, mod_flags, points, NULL); - myfree(work); - } - else { - /* T1_AASetString() accepts a char * not a const char */ - glyph=T1_AASetString( fontnum, (char *)str, len, 0, mod_flags, points, NULL); - } - if (glyph == NULL) - return 0; - - mm_log((1,"metrics: ascent: %d descent: %d\n",glyph->metrics.ascent,glyph->metrics.descent)); - mm_log((1," leftSideBearing: %d rightSideBearing: %d\n",glyph->metrics.leftSideBearing,glyph->metrics.rightSideBearing)); - mm_log((1," advanceX: %d advanceY: %d\n",glyph->metrics.advanceX,glyph->metrics.advanceY)); - mm_log((1,"bpp: %d\n",glyph->bpp)); - - xsize=glyph->metrics.rightSideBearing-glyph->metrics.leftSideBearing; - ysize=glyph->metrics.ascent-glyph->metrics.descent; - - mm_log((1,"width: %d height: %d\n",xsize,ysize)); - - if (align==1) { xb+=glyph->metrics.leftSideBearing; yb-=glyph->metrics.ascent; } - - i_render_init(&r, im, xsize); - for(y=0;ybits+y*xsize, cl); - } - i_render_done(&r); - - return 1; -} - -/* -=item t1_get_flags(flags) - -Processes the characters in I to create a mod_flags value used -by some T1Lib functions. - -=cut - */ -static int -t1_get_flags(char const *flags) { - int mod_flags = T1_KERNING; - - while (*flags) { - switch (*flags++) { - case 'u': case 'U': mod_flags |= T1_UNDERLINE; break; - case 'o': case 'O': mod_flags |= T1_OVERLINE; break; - case 's': case 'S': mod_flags |= T1_OVERSTRIKE; break; - /* ignore anything we don't recognize */ - } - } - - return mod_flags; -} - -/* -=item t1_from_utf8(char const *in, size_t len, int *outlen) - -Produces an unencoded version of I by dropping any Unicode -character over 255. - -Returns a newly allocated buffer which should be freed with myfree(). -Sets *outlen to the number of bytes used in the output string. - -=cut -*/ - -static char * -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; - unsigned long c; - - while (len) { - c = i_utf8_advance(&in, &len); - if (c == ~0UL) { - myfree(out); - i_push_error(0, "invalid UTF8 character"); - return 0; - } - /* yeah, just drop them */ - if (c < 0x100) { - *p++ = (char)c; - } - } - *p = '\0'; - *outlen = p - out; - - return out; -} - -/* -=item i_t1_has_chars(font_num, text, len, utf8, out) - -Check if the given characters are defined by the font. Note that len -is the number of bytes, not the number of characters (when utf8 is -non-zero). - -out[char index] will be true if the character exists. - -Accepts UTF-8, but since T1 can only have 256 characters, any chars -with values over 255 will simply be returned as false. - -Returns the number of characters that were checked. - -=cut -*/ - -int -i_t1_has_chars(int font_num, const char *text, size_t len, int utf8, - char *out) { - int count = 0; - - mm_log((1, "i_t1_has_chars(font_num %d, text %p, len %d, utf8 %d)\n", - font_num, text, len, utf8)); - - i_clear_error(); - if (T1_LoadFont(font_num)) { - t1_push_error(); - return 0; - } - - while (len) { - unsigned long c; - if (utf8) { - c = i_utf8_advance(&text, &len); - if (c == ~0UL) { - i_push_error(0, "invalid UTF8 character"); - return 0; - } - } - else { - c = (unsigned char)*text++; - --len; - } - - if (c >= 0x100) { - /* limit of 256 characters for T1 */ - *out++ = 0; - } - else { - char const * name = T1_GetCharName(font_num, (unsigned char)c); - - if (name) { - *out++ = strcmp(name, ".notdef") != 0; - } - else { - mm_log((2, " No name found for character %lx\n", c)); - *out++ = 0; - } - } - ++count; - } - - return count; -} - -/* -=item i_t1_face_name(font_num, name_buf, name_buf_size) - -Copies the face name of the given C to C. Returns -the number of characters required to store the name (which can be -larger than C, including the space required to store -the terminating NUL). - -If name_buf is too small (as specified by name_buf_size) then the name -will be truncated. name_buf will always be NUL termintaed. - -=cut -*/ - -int -i_t1_face_name(int font_num, char *name_buf, size_t name_buf_size) { - char *name; - - T1_errno = 0; - if (T1_LoadFont(font_num)) { - t1_push_error(); - return 0; - } - name = T1_GetFontName(font_num); - - if (name) { - strncpy(name_buf, name, name_buf_size); - name_buf[name_buf_size-1] = '\0'; - return strlen(name) + 1; - } - else { - t1_push_error(); - return 0; - } -} - -int -i_t1_glyph_name(int font_num, unsigned long ch, char *name_buf, - size_t name_buf_size) { - char *name; - - i_clear_error(); - if (ch > 0xFF) { - return 0; - } - if (T1_LoadFont(font_num)) { - t1_push_error(); - return 0; - } - name = T1_GetCharName(font_num, (unsigned char)ch); - if (name) { - if (strcmp(name, ".notdef")) { - strncpy(name_buf, name, name_buf_size); - name_buf[name_buf_size-1] = '\0'; - return strlen(name) + 1; - } - else { - return 0; - } - } - else { - t1_push_error(); - return 0; - } -} - -static void -t1_push_error(void) { - switch (T1_errno) { - case 0: - i_push_error(0, "No error"); - break; - -#ifdef T1ERR_SCAN_FONT_FORMAT - case T1ERR_SCAN_FONT_FORMAT: - i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT"); - break; -#endif - -#ifdef T1ERR_SCAN_FILE_OPEN_ERR - case T1ERR_SCAN_FILE_OPEN_ERR: - i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR"); - break; -#endif - -#ifdef T1ERR_SCAN_OUT_OF_MEMORY - case T1ERR_SCAN_OUT_OF_MEMORY: - i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY"); - break; -#endif - -#ifdef T1ERR_SCAN_ERROR - case T1ERR_SCAN_ERROR: - i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR"); - break; -#endif - -#ifdef T1ERR_SCAN_FILE_EOF - case T1ERR_SCAN_FILE_EOF: - i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF"); - break; -#endif - -#ifdef T1ERR_PATH_ERROR - case T1ERR_PATH_ERROR: - i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR"); - break; -#endif - -#ifdef T1ERR_PARSE_ERROR - case T1ERR_PARSE_ERROR: - i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR"); - break; -#endif - -#ifdef T1ERR_TYPE1_ABORT - case T1ERR_TYPE1_ABORT: - i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT"); - break; -#endif - -#ifdef T1ERR_INVALID_FONTID - case T1ERR_INVALID_FONTID: - i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID"); - break; -#endif - -#ifdef T1ERR_INVALID_PARAMETER - case T1ERR_INVALID_PARAMETER: - i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER"); - break; -#endif - -#ifdef T1ERR_OP_NOT_PERMITTED - case T1ERR_OP_NOT_PERMITTED: - i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED"); - break; -#endif - -#ifdef T1ERR_ALLOC_MEM - case T1ERR_ALLOC_MEM: - i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM"); - break; -#endif - -#ifdef T1ERR_FILE_OPEN_ERR - case T1ERR_FILE_OPEN_ERR: - i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR"); - break; -#endif - -#ifdef T1ERR_UNSPECIFIED - case T1ERR_UNSPECIFIED: - i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED"); - break; -#endif - -#ifdef T1ERR_NO_AFM_DATA - case T1ERR_NO_AFM_DATA: - i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA"); - break; -#endif - -#ifdef T1ERR_X11 - case T1ERR_X11: - i_push_error(T1ERR_X11, "X11"); - break; -#endif - -#ifdef T1ERR_COMPOSITE_CHAR - case T1ERR_COMPOSITE_CHAR: - i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR"); - break; -#endif - - default: - i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno); - } -} - -#endif /* HAVE_LIBT1 */ - /* Truetype font support */ #ifdef HAVE_LIBTT diff --git a/lib/Imager/Font.pm b/lib/Imager/Font.pm index 964bc528..2144219a 100644 --- a/lib/Imager/Font.pm +++ b/lib/Imager/Font.pm @@ -21,8 +21,8 @@ my %drivers = checktype => 1, }, t1=>{ - class=>'Imager::Font::Type1', - module=>'Imager/Font/Type1.pm', + class=>'Imager::Font::T1', + module=>'Imager/Font/T1.pm', files=>'.*\.pfb$', description => 'T1Lib', checktype => 1, diff --git a/lib/Imager/Font/Type1.pm b/lib/Imager/Font/Type1.pm index 0df82b89..d40dbfff 100644 --- a/lib/Imager/Font/Type1.pm +++ b/lib/Imager/Font/Type1.pm @@ -1,146 +1,10 @@ package Imager::Font::Type1; use strict; -use Imager::Color; +use Imager::Font::T1; use vars qw(@ISA $VERSION); -@ISA = qw(Imager::Font); - -$VERSION = "1.011"; - -*_first = \&Imager::Font::_first; - -my $t1aa; - -# $T1AA is in there because for some reason (probably cache related) antialiasing -# is a system wide setting in t1 lib. - -sub t1_set_aa_level { - if (!defined $t1aa or $_[0] != $t1aa) { - Imager::i_t1_set_aa($_[0]); - $t1aa=$_[0]; - } -} - -sub new { - my $class = shift; - my %hsh=(color=>Imager::Color->new(255,0,0,0), - size=>15, - @_); - - unless ($hsh{file}) { - $Imager::ERRSTR = "No font file specified"; - return; - } - unless (-e $hsh{file}) { - $Imager::ERRSTR = "Font file $hsh{file} not found"; - return; - } - unless ($Imager::formats{t1}) { - $Imager::ERRSTR = "Type 1 fonts not supported in this build"; - return; - } - # we want to avoid T1Lib's file search mechanism - unless ($hsh{file} =~ m!^/! - || $hsh{file} =~ m!^\.\/?/! - || $^O =~ /^(MSWin32|cygwin)$/ && $hsh{file} =~ /^[a-z]:/) { - $hsh{file} = './' . $hsh{file}; - } - - if($hsh{afm}) { - unless (-e $hsh{afm}) { - $Imager::ERRSTR = "Afm file $hsh{afm} not found"; - return; - } - unless ($hsh{afm} =~ m!^/! - || $hsh{afm} =~ m!^\./! - || $^O =~ /^(MSWin32|cygwin)$/ && $hsh{file} =~ /^[a-z]:/) { - $hsh{file} = './' . $hsh{file}; - } - } else { - $hsh{afm} = 0; - } - - my $id = Imager::i_t1_new($hsh{file},$hsh{afm}); - unless ($id >= 0) { # the low-level code may miss some error handling - $Imager::ERRSTR = "Could not load font ($id)"; - return; - } - return bless { - id => $id, - aa => $hsh{aa} || 0, - file => $hsh{file}, - type => 't1', - size => $hsh{size}, - color => $hsh{color}, - }, $class; -} - -sub _draw { - my $self = shift; - my %input = @_; - t1_set_aa_level($input{aa}); - my $flags = ''; - $flags .= 'u' if $input{underline}; - $flags .= 's' if $input{strikethrough}; - $flags .= 'o' if $input{overline}; - if (exists $input{channel}) { - Imager::i_t1_cp($input{image}{IMG}, $input{'x'}, $input{'y'}, - $input{channel}, $self->{id}, $input{size}, - $input{string}, length($input{string}), $input{align}, - $input{utf8}, $flags); - } else { - Imager::i_t1_text($input{image}{IMG}, $input{'x'}, $input{'y'}, - $input{color}, $self->{id}, $input{size}, - $input{string}, length($input{string}), - $input{align}, $input{utf8}, $flags); - } - - return $self; -} - -sub _bounding_box { - my $self = shift; - my %input = @_; - my $flags = ''; - $flags .= 'u' if $input{underline}; - $flags .= 's' if $input{strikethrough}; - $flags .= 'o' if $input{overline}; - return Imager::i_t1_bbox($self->{id}, $input{size}, $input{string}, - length($input{string}), $input{utf8}, $flags); -} - -# check if the font has the characters in the given string -sub has_chars { - my ($self, %hsh) = @_; - - unless (defined $hsh{string} && length $hsh{string}) { - $Imager::ERRSTR = "No string supplied to \$font->has_chars()"; - return; - } - return Imager::i_t1_has_chars($self->{id}, $hsh{string}, - _first($hsh{'utf8'}, $self->{utf8}, 0)); -} - -sub utf8 { - 1; -} - -sub face_name { - my ($self) = @_; - - Imager::i_t1_face_name($self->{id}); -} - -sub glyph_names { - my ($self, %input) = @_; - - my $string = $input{string}; - defined $string - or return Imager->_set_error("no string parameter passed to glyph_names"); - my $utf8 = _first($input{utf8} || 0); - - Imager::i_t1_glyph_name($self->{id}, $string, $utf8); -} +@ISA = qw(Imager::Font::FT2); +$VERSION = "1.000"; 1; @@ -148,51 +12,15 @@ __END__ =head1 NAME - Imager::Font::Type1 - low-level functions for Type1 fonts + Imager::Font::Type1 - low-level functions for T1Lib text output =head1 DESCRIPTION -Imager::Font creates a Imager::Font::Type1 object when asked to create -a font object based on a C<.pfb> file. - -See Imager::Font to see how to use this type. - -This class provides low-level functions that require the caller to -perform data validation - -By default Imager no longer creates the F log file. You -can re-enable that by calling Imager::init() with the C option: - - Imager::init(t1log=>1); - -This must be called before creating any fonts. - -Currently specific to Imager::Font::Type1, you can use the following -flags when drawing text or calculating a bounding box: - -=for stopwords overline strikethrough - -=over - -=item * - -C - Draw the text with an underline. - -=item * - -C - Draw the text with an overline. - -=item * - -C - Draw the text with a strikethrough. - -=back - -Obviously, if you're calculating the bounding box the size of the line -is included in the box, and the line isn't drawn :) +This is a simple wrapper around Imager::Font::T1 for backwards +compatibility. =head1 AUTHOR -Addi, Tony +Tony Cook =cut diff --git a/t/t36oofont.t b/t/t36oofont.t index 3038b743..5781e230 100644 --- a/t/t36oofont.t +++ b/t/t36oofont.t @@ -10,7 +10,7 @@ use strict; # Change 1..1 below to 1..last_test_to_print . # (It may become useful if the test is moved to ./t subdirectory.) -use Test::More tests => 20; +use Test::More tests => 12; BEGIN { use_ok('Imager') }; @@ -19,65 +19,12 @@ BEGIN { use_ok('Imager') }; init_log("testout/t36oofont.log", 1); my $fontname_tt=$ENV{'TTFONTTEST'}||'./fontfiles/dodge.ttf'; -my $fontname_pfb=$ENV{'T1FONTTESTPFB'}||'./fontfiles/dcr10.pfb'; - my $green=Imager::Color->new(92,205,92,128); die $Imager::ERRSTR unless $green; my $red=Imager::Color->new(205, 92, 92, 255); die $Imager::ERRSTR unless $red; -SKIP: -{ - i_has_format("t1") && -f $fontname_pfb - or skip("T1lib missing or disabled", 8); - - my $img=Imager->new(xsize=>300, ysize=>100) or die "$Imager::ERRSTR\n"; - - my $font=Imager::Font->new(file=>$fontname_pfb,size=>25) - or die $img->{ERRSTR}; - - ok(1, "created font"); - - ok($img->string(font=>$font, text=>"XMCLH", 'x'=>100, 'y'=>100), - "draw text"); - $img->line(x1=>0, x2=>300, y1=>50, y2=>50, color=>$green); - - my $text="LLySja"; - my @bbox=$font->bounding_box(string=>$text, 'x'=>0, 'y'=>50); - - is(@bbox, 8, "bounding box list length"); - - $img->box(box=>\@bbox, color=>$green); - - # "utf8" support - $text = pack("C*", 0x41, 0xE2, 0x80, 0x90, 0x41); - ok($img->string(font=>$font, text=>$text, 'x'=>100, 'y'=>50, utf8=>1, - overline=>1), - "draw 'utf8' hand-encoded text"); - - ok($img->string(font=>$font, text=>$text, 'x'=>140, 'y'=>50, utf8=>1, - underline=>1, channel=>2), - "channel 'utf8' hand-encoded text"); - - SKIP: - { - $] >= 5.006 - or skip("perl too old for native utf8", 2); - eval q{$text = "A\x{2010}A"}; - ok($img->string(font=>$font, text=>$text, 'x'=>180, 'y'=>50, - strikethrough=>1), - "draw native UTF8 text"); - ok($img->string(font=>$font, text=>$text, 'x'=>220, 'y'=>50, channel=>1), - "channel native UTF8 text"); - } - - ok($img->write(file=>"testout/t36oofont1.ppm", type=>'pnm'), - "write t36oofont1.ppm") - or print "# ",$img->errstr,"\n"; - -} - SKIP: { i_has_format("tt") && -f $fontname_tt -- 2.39.5