Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

- implemented i_t1_has_chars(), tests for same

        - added ExistenceTest.{pfb,afm,ttf} for testing $font->has_chars
        - tests for Imager::Font::Type1::has_chars();
        - tests for Imager::Font::Truetype::has_chars();
        - internal and external bounding box calculations now use
          the same hint flags as text output for Freetype 2.x
        - made the i_foo_bbox() interface more expandable by using
          symbolic constants for the sizes and array offsets
        - added a / character to the ExistenceTest.foo fonts that
          overlaps the right side of the character cell, to test the
          advance width reporting.
        - added advance width to the i_foo_bbox() interface, and
          implemented it for FT2, FT1 and Type 1
        - Imager::Font::bounding_box() now returns an Imager::Font::BBox
          object in scalar context.
        - implemented $font->align() text output method, for simple output
          of aligned text
        - created Imager::Font::Wrap::wrap_text to perform simple text
          wrapping
        - FT1, FT2 and T1 fonts now support the face_name method
        - FT1, FT2 and T1 now support the glyph_names() method
  • Loading branch information...
commit 3799c4d10a16068029e8d7abcad0ccddcaa47e94 1 parent 0cb68c1
Tony Cook authored
View
21 Changes
@@ -690,6 +690,27 @@ Revision history for Perl extension Imager.
- the code to read multiple tiffs didn't handle files with more
than five images correctly, causing a memory overrun.
- fix some minor test code hiccups
+ - implemented i_t1_has_chars(), tests for same
+ - added ExistenceTest.{pfb,afm,ttf} for testing $font->has_chars
+ - tests for Imager::Font::Type1::has_chars();
+ - tests for Imager::Font::Truetype::has_chars();
+ - internal and external bounding box calculations now use
+ the same hint flags as text output for Freetype 2.x
+ - made the i_foo_bbox() interface more expandable by using
+ symbolic constants for the sizes and array offsets
+ - added a / character to the ExistenceTest.foo fonts that
+ overlaps the right side of the character cell, to test the
+ advance width reporting.
+ - added advance width to the i_foo_bbox() interface, and
+ implemented it for FT2, FT1 and Type 1
+ - Imager::Font::bounding_box() now returns an Imager::Font::BBox
+ object in scalar context.
+ - implemented $font->align() text output method, for simple output
+ of aligned text
+ - created Imager::Font::Wrap::wrap_text to perform simple text
+ wrapping
+ - FT1, FT2 and T1 fonts now support the face_name method
+ - FT1, FT2 and T1 now support the glyph_names() method
=================================================================
View
2  Imager.pm
@@ -2708,7 +2708,7 @@ In cases where no image object is associated with an operation
C<$Imager::ERRSTR> is used to report errors not directly associated
with an image object.
-The C<Imager-><gt>new> method is described in detail in the
+The C<Imager-E<gt>new> method is described in detail in the
Imager::ImageTypes manpage.
=head1 SUPPORT
View
259 Imager.xs
@@ -1715,18 +1715,21 @@ i_t1_bbox(fontnum,point,str_sv,len_ignored,utf8=0,flags="")
PREINIT:
char *str;
STRLEN len;
- int cords[6];
+ int cords[BOUNDING_BOX_COUNT];
int i;
+ int rc;
PPCODE:
#ifdef SvUTF8
if (SvUTF8(str_sv))
utf8 = 1;
#endif
str = SvPV(str_sv, len);
- i_t1_bbox(fontnum,point,str,len,cords,utf8,flags);
- EXTEND(SP, 6);
- for (i = 0; i < 6; ++i)
- PUSHs(sv_2mortal(newSViv(cords[i])));
+ 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])));
+ }
@@ -1756,6 +1759,89 @@ i_t1_text(im,xb,yb,cl,fontnum,points,str_sv,len_ignored,align,utf8=0,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;
+ int len;
+ int outsize;
+ 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 char 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 (outsize = i_t1_glyph_name(handle, ch, name, sizeof(name))) {
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+ else {
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
#endif
#ifdef HAVE_LIBTT
@@ -1840,9 +1926,10 @@ i_tt_bbox(handle,point,str_sv,len_ignored, utf8)
int len_ignored
int utf8
PREINIT:
- int cords[6],rc;
+ int cords[BOUNDING_BOX_COUNT],rc;
char * str;
STRLEN len;
+ int i;
PPCODE:
#ifdef SvUTF8
if (SvUTF8(ST(2)))
@@ -1850,13 +1937,10 @@ i_tt_bbox(handle,point,str_sv,len_ignored, utf8)
#endif
str = SvPV(str_sv, len);
if ((rc=i_tt_bbox(handle,point,str,len,cords, utf8))) {
- EXTEND(SP, 4);
- PUSHs(sv_2mortal(newSViv(cords[0])));
- PUSHs(sv_2mortal(newSViv(cords[1])));
- PUSHs(sv_2mortal(newSViv(cords[2])));
- PUSHs(sv_2mortal(newSViv(cords[3])));
- PUSHs(sv_2mortal(newSViv(cords[4])));
- PUSHs(sv_2mortal(newSViv(cords[5])));
+ EXTEND(SP, rc);
+ for (i = 0; i < rc; ++i) {
+ PUSHs(sv_2mortal(newSViv(cords[i])));
+ }
}
void
@@ -1890,9 +1974,63 @@ i_tt_has_chars(handle, text_sv, utf8)
}
myfree(work);
-#endif
+void
+i_tt_dump_names(handle)
+ Imager::Font::TT handle
+void
+i_tt_face_name(handle)
+ Imager::Font::TT handle
+ PREINIT:
+ char name[255];
+ int len;
+ PPCODE:
+ len = i_tt_face_name(handle, name, sizeof(name));
+ if (len) {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv(name, strlen(name))));
+ }
+void i_tt_glyph_name(handle, text_sv, utf8 = 0)
+ Imager::Font::TT handle
+ SV *text_sv
+ int utf8
+ PREINIT:
+ char const *text;
+ STRLEN work_len;
+ int len;
+ int outsize;
+ 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 char 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 (outsize = i_tt_glyph_name(handle, ch, name, sizeof(name))) {
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+ else {
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+#endif
#ifdef HAVE_LIBJPEG
@@ -3813,17 +3951,14 @@ i_wf_bbox(face, size, text)
char *face
int size
char *text
+ int rc, i;
PREINIT:
- int cords[6];
+ int cords[BOUNDING_BOX_COUNT];
PPCODE:
- if (i_wf_bbox(face, size, text, strlen(text), cords)) {
- EXTEND(SP, 6);
- PUSHs(sv_2mortal(newSViv(cords[0])));
- PUSHs(sv_2mortal(newSViv(cords[1])));
- PUSHs(sv_2mortal(newSViv(cords[2])));
- PUSHs(sv_2mortal(newSViv(cords[3])));
- PUSHs(sv_2mortal(newSViv(cords[4])));
- PUSHs(sv_2mortal(newSViv(cords[5])));
+ if (rc = i_wf_bbox(face, size, text, strlen(text), cords)) {
+ EXTEND(SP, rc);
+ for (i = 0; i < rc; ++i)
+ PUSHs(sv_2mortal(newSViv(cords[i])));
}
undef_int
@@ -3930,23 +4065,28 @@ i_ft2_settransform(font, matrix)
RETVAL
void
-i_ft2_bbox(font, cheight, cwidth, text, utf8)
+i_ft2_bbox(font, cheight, cwidth, text_sv, utf8)
Imager::Font::FT2 font
double cheight
double cwidth
- char *text
+ SV *text_sv
int utf8
PREINIT:
- int bbox[6];
+ int bbox[BOUNDING_BOX_COUNT];
int i;
+ char *text;
+ STRLEN text_len;
+ int rc;
PPCODE:
+ text = SvPV(text_sv, text_len);
#ifdef SvUTF8
- if (SvUTF8(ST(3)))
+ if (SvUTF8(text_sv))
utf8 = 1;
#endif
- if (i_ft2_bbox(font, cheight, cwidth, text, strlen(text), bbox, utf8)) {
- EXTEND(SP, 6);
- for (i = 0; i < 6; ++i)
+ rc = i_ft2_bbox(font, cheight, cwidth, text, text_len, bbox, utf8);
+ if (rc) {
+ EXTEND(SP, rc);
+ for (i = 0; i < rc; ++i)
PUSHs(sv_2mortal(newSViv(bbox[i])));
}
@@ -4074,6 +4214,65 @@ i_ft2_has_chars(handle, text_sv, utf8)
}
myfree(work);
+void
+i_ft2_face_name(handle)
+ Imager::Font::FT2 handle
+ PREINIT:
+ char name[255];
+ int len;
+ PPCODE:
+ len = i_ft2_face_name(handle, name, sizeof(name));
+ if (len) {
+ EXTEND(SP, 1);
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+
+void i_ft2_glyph_name(handle, text_sv, utf8 = 0)
+ Imager::Font::FT2 handle
+ SV *text_sv
+ int utf8
+ PREINIT:
+ char const *text;
+ STRLEN work_len;
+ int len;
+ int outsize;
+ 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 char 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 (outsize = i_ft2_glyph_name(handle, ch, name, sizeof(name))) {
+ PUSHs(sv_2mortal(newSVpv(name, 0)));
+ }
+ else {
+ PUSHs(&PL_sv_undef);
+ }
+ }
+
+int
+i_ft2_can_do_glyph_names()
+
+int
+i_ft2_face_has_glyph_names(handle)
+ Imager::Font::FT2 handle
+
#endif
MODULE = Imager PACKAGE = Imager::FillHandle PREFIX=IFILL_
View
6 MANIFEST
@@ -52,6 +52,9 @@ tags.c
trans2.c
iolayer.h
iolayer.c
+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/dcr10.afm
fontfiles/dcr10.pfb
@@ -68,10 +71,12 @@ lib/Imager/Filters.pod
lib/Imager/Engines.pod
lib/Imager/Fill.pm
lib/Imager/Font.pm
+lib/Imager/Font/BBox.pm
lib/Imager/Font/Type1.pm
lib/Imager/Font/Truetype.pm
lib/Imager/Font/FreeType2.pm
lib/Imager/Font/Win32.pm
+lib/Imager/Font/Wrap.pm
lib/Imager/Fountain.pm
lib/Imager/interface.pod
lib/Imager/Matrix2d.pm
@@ -120,6 +125,7 @@ t/t68map.t
t/t69rubthru.t
t/t70newgif.t
t/t75polyaa.t
+t/t80texttools.t Test text wrapping
t/t90cc.t
testimg/bandw.gif
testimg/comp4.bmp Compressed 4-bit/pixel BMP
View
4 TODO
@@ -136,9 +136,9 @@ Clean up:
- try to clean up the inconsistencies between font types:
- utf8 (even if we just treat characters over 0xFF as missing for T1)
- (done for FT2, FT1)
+ (done for FT2, FT1, T1)
- transformations (done for FT2)
- - has_char() method (done for FT2, FT1)
+ - has_char() method (done for FT2, FT1, T1)
Format specific issues:
- provide patches for libgif and libungif that fix their bugs
View
2  bigtest.perl
@@ -25,7 +25,7 @@
for my $set (0..$top) {
++$total;
$ENV{IM_ENABLE} = join(' ', grep($set & $bits{$_}, @opts));
- print STDERR $opts{v} ? "Enable: $ENV{IM_ENABLE}\n" : '.';
+ print STDERR $opts{v} ? "$set/$top Enable: $ENV{IM_ENABLE}\n" : '.';
system("echo '****' \$IM_ENABLE >>testout/bigtest.txt");
if ($opts{d}) {
if (system("$make $makeopts disttest >>testout/bigtest.txt 2>&1")) {
View
12 datatypes.h
@@ -226,5 +226,17 @@ void octt_dump(struct octt *ct);
void octt_count(struct octt *ct,int *tot,int max,int *overflow);
void octt_delete(struct octt *ct);
+/* font bounding box results */
+enum bounding_box_index_t {
+ BBOX_NEG_WIDTH,
+ BBOX_GLOBAL_DESCENT,
+ BBOX_POS_WIDTH,
+ BBOX_GLOBAL_ASCENT,
+ BBOX_DESCENT,
+ BBOX_ASCENT,
+ BBOX_ADVANCE_WIDTH,
+ BOUNDING_BOX_COUNT
+};
+
#endif
View
451 font.c
@@ -44,14 +44,6 @@ Some of these functions are internal.
*/
-
-
-
-
-
-
-
-
/*
=item i_init_fonts()
@@ -88,6 +80,8 @@ i_init_fonts(int t1log) {
static int t1_get_flags(char const *flags);
static char *t1_from_utf8(char const *in, int len, int *outlen);
+static void t1_push_error(void);
+
/*
=item i_init_t1(t1log)
@@ -294,11 +288,12 @@ function to get a strings bounding box given the font id and sizes
=cut
*/
-void
+int
i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char const *flags) {
BBox bbox;
BBox gbbox;
int mod_flags = t1_get_flags(flags);
+ int advance;
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 */
@@ -312,6 +307,7 @@ i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char
bbox = T1_GetStringBBox(fontnum,str,len,0,mod_flags);
}
gbbox = T1_GetFontBBox(fontnum);
+ advance = T1_GetStringWidth(fontnum, str, len, 0, mod_flags);
mm_log((1,"bbox: (%d,%d,%d,%d)\n",
(int)(bbox.llx*points/1000),
@@ -322,14 +318,18 @@ i_t1_bbox(int fontnum,float points,char *str,int len,int cords[6], int utf8,char
(int)(bbox.ury*points/1000) ));
- cords[0]=((float)bbox.llx*points)/1000;
- cords[2]=((float)bbox.urx*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[1]=((float)gbbox.lly*points)/1000;
- cords[3]=((float)gbbox.ury*points)/1000;
+ cords[BBOX_ADVANCE_WIDTH] = ((float)advance * points)/1000;
- cords[4]=((float)bbox.lly*points)/1000;
- cords[5]=((float)bbox.ury*points)/1000;
+ return BBOX_ADVANCE_WIDTH+1;
}
@@ -455,16 +455,239 @@ t1_from_utf8(char const *in, int len, int *outlen) {
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, int 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;
+ int index;
+ 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<font_num> to C<name_buf>. Returns
+the number of characters required to store the name (which can be
+larger than C<name_buf_size>, 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;
+
+ case T1ERR_SCAN_FONT_FORMAT:
+ i_push_error(T1ERR_SCAN_FONT_FORMAT, "SCAN_FONT_FORMAT");
+ break;
+
+ case T1ERR_SCAN_FILE_OPEN_ERR:
+ i_push_error(T1ERR_SCAN_FILE_OPEN_ERR, "SCAN_FILE_OPEN_ERR");
+ break;
+
+ case T1ERR_SCAN_OUT_OF_MEMORY:
+ i_push_error(T1ERR_SCAN_OUT_OF_MEMORY, "SCAN_OUT_OF_MEMORY");
+ break;
+
+ case T1ERR_SCAN_ERROR:
+ i_push_error(T1ERR_SCAN_ERROR, "SCAN_ERROR");
+ break;
+
+ case T1ERR_SCAN_FILE_EOF:
+ i_push_error(T1ERR_SCAN_FILE_EOF, "SCAN_FILE_EOF");
+ break;
+
+ case T1ERR_PATH_ERROR:
+ i_push_error(T1ERR_PATH_ERROR, "PATH_ERROR");
+ break;
+
+ case T1ERR_PARSE_ERROR:
+ i_push_error(T1ERR_PARSE_ERROR, "PARSE_ERROR");
+ break;
+
+ case T1ERR_TYPE1_ABORT:
+ i_push_error(T1ERR_TYPE1_ABORT, "TYPE1_ABORT");
+ break;
+
+ case T1ERR_INVALID_FONTID:
+ i_push_error(T1ERR_INVALID_FONTID, "INVALID_FONTID");
+ break;
+
+ case T1ERR_INVALID_PARAMETER:
+ i_push_error(T1ERR_INVALID_PARAMETER, "INVALID_PARAMETER");
+ break;
+
+ case T1ERR_OP_NOT_PERMITTED:
+ i_push_error(T1ERR_OP_NOT_PERMITTED, "OP_NOT_PERMITTED");
+ break;
+
+ case T1ERR_ALLOC_MEM:
+ i_push_error(T1ERR_ALLOC_MEM, "ALLOC_MEM");
+ break;
+
+ case T1ERR_FILE_OPEN_ERR:
+ i_push_error(T1ERR_FILE_OPEN_ERR, "FILE_OPEN_ERR");
+ break;
+
+ case T1ERR_UNSPECIFIED:
+ i_push_error(T1ERR_UNSPECIFIED, "UNSPECIFIED");
+ break;
+
+ case T1ERR_NO_AFM_DATA:
+ i_push_error(T1ERR_NO_AFM_DATA, "NO_AFM_DATA");
+ break;
+
+ case T1ERR_X11:
+ i_push_error(T1ERR_X11, "X11");
+ break;
+
+ case T1ERR_COMPOSITE_CHAR:
+ i_push_error(T1ERR_COMPOSITE_CHAR, "COMPOSITE_CHAR");
+ break;
+
+ default:
+ i_push_errorf(T1_errno, "unknown error %d", (int)T1_errno);
+ }
+}
+
#endif /* HAVE_LIBT1 */
/* Truetype font support */
-
#ifdef HAVE_LIBTT
+/* This is enabled by default when configuring Freetype 1.x
+ I haven't a clue how to reliably detect it at compile time.
+
+ We need a compilation probe in Makefile.PL
+*/
+#define FTXPOST 1
+
#include <freetype.h>
#define TT_CHC 5
+#ifdef FTXPOST
+#include <ftxpost.h>
+#endif
+
/* convert a code point into an index in the glyph cache */
#define TT_HASH(x) ((x) & 0xFF)
@@ -492,6 +715,10 @@ struct TT_Fonthandle_ {
TT_Face_Properties properties;
TT_Instancehandle instanceh[TT_CHC];
TT_CharMap char_map;
+#ifdef FTXPOST
+ int loaded_names;
+ TT_Error load_cond;
+#endif
};
/* Defines */
@@ -553,6 +780,15 @@ i_init_tt() {
mm_log((1,"Initialization of freetype failed, code = 0x%x\n",error));
return(1);
}
+
+#ifdef FTXPOST
+ error = TT_Init_Post_Extension( engine );
+ if (error) {
+ mm_log((1, "Initialization of Post extension failed = 0x%x\n", error));
+ return 1;
+ }
+#endif
+
return(0);
}
@@ -726,6 +962,10 @@ i_tt_new(char *fontname) {
handle->instanceh[i].smooth=-1;
}
+#ifdef FTXPOST
+ handle->loaded_names = 0;
+#endif
+
mm_log((1,"i_tt_new <- 0x%X\n",handle));
return handle;
}
@@ -968,7 +1208,7 @@ i_tt_has_chars(TT_Fonthandle *handle, char const *text, int len, int utf8,
char *out) {
int count = 0;
int inst;
- mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
+ mm_log((1, "i_tt_has_chars(handle %p, text %p, len %d, utf8 %d)\n",
handle, text, len, utf8));
while (len) {
@@ -1316,7 +1556,7 @@ Interface to text rendering into a single channel in an image
undef_int
i_tt_cp( TT_Fonthandle *handle, i_img *im, int xb, int yb, int channel, float points, char const* txt, int len, int smooth, int utf8 ) {
- int cords[6];
+ int cords[BOUNDING_BOX_COUNT];
int ascent, st_offset;
TT_Raster_Map bit;
@@ -1352,7 +1592,7 @@ Interface to text rendering in a single color onto an image
undef_int
i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8) {
- int cords[6];
+ int cords[BOUNDING_BOX_COUNT];
int ascent, st_offset;
TT_Raster_Map bit;
@@ -1386,7 +1626,7 @@ Function to get texts bounding boxes given the instance of the font (internal)
static
undef_int
-i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[6], int utf8 ) {
+i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int cords[BOUNDING_BOX_COUNT], int utf8 ) {
int i, upm, casc, cdesc, first;
int start = 0;
@@ -1395,7 +1635,7 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
int gascent = 0;
int descent = 0;
int ascent = 0;
-
+ int rightb = 0;
unsigned long j;
unsigned char *ustr;
@@ -1445,12 +1685,12 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
character goes past the right of the advance width,
as is common for italic fonts
*/
- int rightb = gm->advance - gm->bearingX
+ rightb = gm->advance - gm->bearingX
- (gm->bbox.xMax - gm->bbox.xMin);
/* fprintf(stderr, "font info last: %d %d %d %d\n",
gm->bbox.xMax, gm->bbox.xMin, gm->advance, rightb); */
- if (rightb < 0)
- width -= rightb/64;
+ if (rightb > 0)
+ rightb = 0;
}
ascent = (ascent > casc ? ascent : casc );
@@ -1458,13 +1698,15 @@ i_tt_bbox_inst( TT_Fonthandle *handle, int inst ,const char *txt, int len, int c
}
}
- cords[0]=start;
- cords[1]=gdescent;
- cords[2]=width;
- cords[3]=gascent;
- cords[4]=descent;
- cords[5]=ascent;
- return 1;
+ cords[BBOX_NEG_WIDTH]=start;
+ cords[BBOX_GLOBAL_DESCENT]=gdescent;
+ cords[BBOX_POS_WIDTH]=width - rightb / 64;
+ cords[BBOX_GLOBAL_ASCENT]=gascent;
+ cords[BBOX_DESCENT]=descent;
+ cords[BBOX_ASCENT]=ascent;
+ cords[BBOX_ADVANCE_WIDTH] = width;
+
+ return BBOX_ADVANCE_WIDTH + 1;
}
@@ -1498,7 +1740,154 @@ i_tt_bbox( TT_Fonthandle *handle, float points,char *txt,int len,int cords[6], i
return i_tt_bbox_inst(handle, inst, txt, len, cords, utf8);
}
+/*
+=item i_tt_face_name(handle, name_buf, name_buf_size)
+
+Retrieve's the font's postscript name.
+This is complicated by the need to handle encodings and so on.
+
+=cut
+ */
+int
+i_tt_face_name(TT_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+ TT_Face_Properties props;
+ int name_count;
+ int i;
+ TT_UShort platform_id, encoding_id, lang_id, name_id;
+ TT_UShort name_len;
+ TT_String *name;
+ int want_index = -1; /* an acceptable but not perfect name */
+ int score = 0;
+
+ i_clear_error();
+
+ TT_Get_Face_Properties(handle->face, &props);
+ name_count = props.num_Names;
+ for (i = 0; i < name_count; ++i) {
+ TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
+ &name_id);
+
+ TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+ if (platform_id != TT_PLATFORM_APPLE_UNICODE && name_len
+ && name_id == TT_NAME_ID_PS_NAME) {
+ int might_want_index = -1;
+ int might_score = 0;
+ if ((platform_id == TT_PLATFORM_MACINTOSH && encoding_id == TT_MAC_ID_ROMAN)
+ ||
+ (platform_id == TT_PLATFORM_MICROSOFT && encoding_id == TT_MS_LANGID_ENGLISH_UNITED_STATES)) {
+ /* exactly what we want */
+ want_index = i;
+ break;
+ }
+
+ if (platform_id == TT_PLATFORM_MICROSOFT
+ && (encoding_id & 0xFF) == TT_MS_LANGID_ENGLISH_GENERAL) {
+ /* any english is good */
+ might_want_index = i;
+ might_score = 9;
+ }
+ /* there might be something in between */
+ else {
+ /* anything non-unicode is better than nothing */
+ might_want_index = i;
+ might_score = 1;
+ }
+ if (might_score > score) {
+ score = might_score;
+ want_index = might_want_index;
+ }
+ }
+ }
+
+ if (want_index != -1) {
+ TT_Get_Name_String(handle->face, want_index, &name, &name_len);
+
+ strncpy(name_buf, name, name_buf_size);
+ name_buf[name_buf_size-1] = '\0';
+
+ return strlen(name) + 1;
+ }
+ else {
+ i_push_error(0, "no face name present");
+ return 0;
+ }
+}
+
+void i_tt_dump_names(TT_Fonthandle *handle) {
+ TT_Face_Properties props;
+ int name_count;
+ int i;
+ TT_UShort platform_id, encoding_id, lang_id, name_id;
+ TT_UShort name_len;
+ TT_String *name;
+
+ TT_Get_Face_Properties(handle->face, &props);
+ name_count = props.num_Names;
+ for (i = 0; i < name_count; ++i) {
+ TT_Get_Name_ID(handle->face, i, &platform_id, &encoding_id, &lang_id,
+ &name_id);
+ TT_Get_Name_String(handle->face, i, &name, &name_len);
+
+ printf("# %d: plat %d enc %d lang %d name %d value ", i, platform_id,
+ encoding_id, lang_id, name_id);
+ if (platform_id == TT_PLATFORM_APPLE_UNICODE) {
+ printf("(unicode)\n");
+ }
+ else {
+ printf("'%s'\n", name);
+ }
+ }
+}
+
+int
+i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
+ size_t name_buf_size) {
+#ifdef FTXPOST
+ TT_Error rc;
+ TT_String *psname;
+ TT_UShort index;
+
+ i_clear_error();
+
+ if (!handle->loaded_names) {
+ TT_Post post;
+ mm_log((1, "Loading PS Names"));
+ handle->load_cond = TT_Load_PS_Names(handle->face, &post);
+ ++handle->loaded_names;
+ }
+
+ if (handle->load_cond) {
+ i_push_errorf(rc, "error loading names (%d)", handle->load_cond);
+ return 0;
+ }
+
+ index = TT_Char_Index(handle->char_map, ch);
+ if (!index) {
+ i_push_error(0, "no such character");
+ return 0;
+ }
+
+ rc = TT_Get_PS_Name(handle->face, index, &psname);
+
+ if (rc) {
+ i_push_error(rc, "error getting name");
+ return 0;
+ }
+
+ strncpy(name_buf, psname, name_buf_size);
+ name_buf[name_buf_size-1] = '\0';
+
+ return strlen(psname) + 1;
+#else
+ mm_log((1, "FTXPOST extension not enabled\n"));
+ i_clear_error();
+ i_push_error(0, "Use of FTXPOST extension disabled");
+
+ return 0;
+#endif
+}
#endif /* HAVE_LIBTT */
View
20 fontfiles/ExistenceTest.afm
@@ -0,0 +1,20 @@
+StartFontMetrics 2.0
+Comment Generated by pfaedit
+Comment Creation Date: Sat Dec 28 17:06:43 2002
+FontName ExistenceTest
+FullName ExistenceTest
+FamilyName ExistenceTest
+Weight Medium
+Notice (Created by Tony Cook,,, with PfaEdit 1.0 (http://pfaedit.sf.net))
+ItalicAngle 0
+IsFixedPitch false
+UnderlinePosition -100
+UnderlineThickness 50
+Version 001.000
+EncodingScheme AdobeStandardEncoding
+FontBBox -60 -55 819 775
+StartCharMetrics 2
+C 33 ; WX 310 ; N exclam ; B 51 0 207 738 ;
+C 47 ; WX 761 ; N slash ; B -60 -55 819 775 ;
+EndCharMetrics
+EndFontMetrics
View
BIN  fontfiles/ExistenceTest.pfb
Binary file not shown
View
440 fontfiles/ExistenceTest.sfd
@@ -0,0 +1,440 @@
+SplineFontDB: 1.0
+FontName: ExistenceTest
+FullName: ExistenceTest
+FamilyName: ExistenceTest
+Weight: Medium
+Copyright: Created by Tony Cook,,, with PfaEdit 1.0 (http://pfaedit.sf.net)
+Comments: 2002-12-23: Created.
+Version: 001.000
+ItalicAngle: 0
+UnderlinePosition: -100
+UnderlineWidth: 50
+Ascent: 800
+Descent: 200
+FSType: 12
+PfmFamily: 17
+TTFWeight: 500
+TTFWidth: 5
+Panose: 2 0 6 3 0 0 0 0 0 0
+LineGap: 90
+VLineGap: 0
+
+Encoding: adobestandard
+DisplaySize: -24
+AntiAlias: 1
+BeginChars: 256 95
+StartChar: space
+Encoding: 32 32
+Width: 1000
+EndChar
+StartChar: exclam
+Encoding: 33 33
+Width: 310
+Flags: W
+HStem: 0 126<99 163> 180 558<99 147>
+VStem: 51 144<224 690> 57 150<48 80>
+Fore
+107 126 m 2
+ 157 126 l 2
+ 184.6 126 207 103.6 207 76 c 2
+ 207 50 l 2
+ 207 22.4004 184.6 0 157 0 c 2
+ 107 0 l 2
+ 79.4004 0 57 22.4004 57 50 c 2
+ 57 76 l 2
+ 57 103.6 79.4004 126 107 126 c 2
+101 738 m 2
+ 145 738 l 2
+ 172.6 738 195 715.6 195 688 c 2
+ 195 230 l 2
+ 195 202.4 172.6 180 145 180 c 2
+ 101 180 l 2
+ 73.4004 180 51 202.4 51 230 c 2
+ 51 688 l 2
+ 51 715.6 73.4004 738 101 738 c 2
+EndSplineSet
+MinimumDistance: x2,-1
+EndChar
+StartChar: quotedbl
+Encoding: 34 34
+Width: 1000
+EndChar
+StartChar: numbersign
+Encoding: 35 35
+Width: 1000
+EndChar
+StartChar: dollar
+Encoding: 36 36
+Width: 1000
+EndChar
+StartChar: percent
+Encoding: 37 37
+Width: 1000
+EndChar
+StartChar: ampersand
+Encoding: 38 38
+Width: 1000
+EndChar
+StartChar: parenleft
+Encoding: 40 40
+Width: 1000
+EndChar
+StartChar: parenright
+Encoding: 41 41
+Width: 1000
+EndChar
+StartChar: asterisk
+Encoding: 42 42
+Width: 1000
+EndChar
+StartChar: plus
+Encoding: 43 43
+Width: 1000
+EndChar
+StartChar: comma
+Encoding: 44 44
+Width: 1000
+EndChar
+StartChar: hyphen
+Encoding: 45 45
+Width: 1000
+EndChar
+StartChar: period
+Encoding: 46 46
+Width: 1000
+EndChar
+StartChar: slash
+Encoding: 47 47
+Width: 761
+Flags: W
+DStem: 783.882 774.221 818.118 737.779 -59.1182 -17.7793 -24.8818 -54.2207
+Fore
+-59.1182 -17.7793 m 1
+ 783.882 774.221 l 1
+ 818.118 737.779 l 1
+ -24.8818 -54.2207 l 1
+ -59.1182 -17.7793 l 1
+EndSplineSet
+EndChar
+StartChar: zero
+Encoding: 48 48
+Width: 1000
+EndChar
+StartChar: one
+Encoding: 49 49
+Width: 1000
+EndChar
+StartChar: two
+Encoding: 50 50
+Width: 1000
+EndChar
+StartChar: three
+Encoding: 51 51
+Width: 1000
+EndChar
+StartChar: four
+Encoding: 52 52
+Width: 1000
+EndChar
+StartChar: five
+Encoding: 53 53
+Width: 1000
+EndChar
+StartChar: six
+Encoding: 54 54
+Width: 1000
+EndChar
+StartChar: seven
+Encoding: 55 55
+Width: 1000
+EndChar
+StartChar: eight
+Encoding: 56 56
+Width: 1000
+EndChar
+StartChar: nine
+Encoding: 57 57
+Width: 1000
+EndChar
+StartChar: colon
+Encoding: 58 58
+Width: 1000
+EndChar
+StartChar: semicolon
+Encoding: 59 59
+Width: 1000
+EndChar
+StartChar: less
+Encoding: 60 60
+Width: 1000
+EndChar
+StartChar: equal
+Encoding: 61 61
+Width: 1000
+EndChar
+StartChar: greater
+Encoding: 62 62
+Width: 1000
+EndChar
+StartChar: question
+Encoding: 63 63
+Width: 1000
+EndChar
+StartChar: at
+Encoding: 64 64
+Width: 1000
+EndChar
+StartChar: A
+Encoding: 65 65
+Width: 1000
+EndChar
+StartChar: B
+Encoding: 66 66
+Width: 1000
+EndChar
+StartChar: C
+Encoding: 67 67
+Width: 1000
+EndChar
+StartChar: D
+Encoding: 68 68
+Width: 1000
+EndChar
+StartChar: E
+Encoding: 69 69
+Width: 1000
+EndChar
+StartChar: F
+Encoding: 70 70
+Width: 1000
+EndChar
+StartChar: G
+Encoding: 71 71
+Width: 1000
+EndChar
+StartChar: H
+Encoding: 72 72
+Width: 1000
+EndChar
+StartChar: I
+Encoding: 73 73
+Width: 1000
+EndChar
+StartChar: J
+Encoding: 74 74
+Width: 1000
+EndChar
+StartChar: K
+Encoding: 75 75
+Width: 1000
+EndChar
+StartChar: L
+Encoding: 76 76
+Width: 1000
+EndChar
+StartChar: M
+Encoding: 77 77
+Width: 1000
+EndChar
+StartChar: N
+Encoding: 78 78
+Width: 1000
+EndChar
+StartChar: O
+Encoding: 79 79
+Width: 1000
+EndChar
+StartChar: P
+Encoding: 80 80
+Width: 1000
+EndChar
+StartChar: Q
+Encoding: 81 81
+Width: 1000
+EndChar
+StartChar: R
+Encoding: 82 82
+Width: 1000
+EndChar
+StartChar: S
+Encoding: 83 83
+Width: 1000
+EndChar
+StartChar: T
+Encoding: 84 84
+Width: 1000
+EndChar
+StartChar: U
+Encoding: 85 85
+Width: 1000
+EndChar
+StartChar: V
+Encoding: 86 86
+Width: 1000
+EndChar
+StartChar: W
+Encoding: 87 87
+Width: 1000
+EndChar
+StartChar: X
+Encoding: 88 88
+Width: 1000
+EndChar
+StartChar: Y
+Encoding: 89 89
+Width: 1000
+EndChar
+StartChar: Z
+Encoding: 90 90
+Width: 1000
+EndChar
+StartChar: bracketleft
+Encoding: 91 91
+Width: 1000
+EndChar
+StartChar: backslash
+Encoding: 92 92
+Width: 1000
+EndChar
+StartChar: bracketright
+Encoding: 93 93
+Width: 1000
+EndChar
+StartChar: asciicircum
+Encoding: 94 94
+Width: 1000
+EndChar
+StartChar: underscore
+Encoding: 95 95
+Width: 1000
+EndChar
+StartChar: a
+Encoding: 97 97
+Width: 1000
+EndChar
+StartChar: b
+Encoding: 98 98
+Width: 1000
+EndChar
+StartChar: c
+Encoding: 99 99
+Width: 1000
+EndChar
+StartChar: d
+Encoding: 100 100
+Width: 1000
+EndChar
+StartChar: e
+Encoding: 101 101
+Width: 1000
+EndChar
+StartChar: f
+Encoding: 102 102
+Width: 1000
+EndChar
+StartChar: g
+Encoding: 103 103
+Width: 1000
+EndChar
+StartChar: h
+Encoding: 104 104
+Width: 1000
+EndChar
+StartChar: i
+Encoding: 105 105
+Width: 1000
+EndChar
+StartChar: j
+Encoding: 106 106
+Width: 1000
+EndChar
+StartChar: k
+Encoding: 107 107
+Width: 1000
+EndChar
+StartChar: l
+Encoding: 108 108
+Width: 1000
+EndChar
+StartChar: m
+Encoding: 109 109
+Width: 1000
+EndChar
+StartChar: n
+Encoding: 110 110
+Width: 1000
+EndChar
+StartChar: o
+Encoding: 111 111
+Width: 1000
+EndChar
+StartChar: p
+Encoding: 112 112
+Width: 1000
+EndChar
+StartChar: q
+Encoding: 113 113
+Width: 1000
+EndChar
+StartChar: r
+Encoding: 114 114
+Width: 1000
+EndChar
+StartChar: s
+Encoding: 115 115
+Width: 1000
+EndChar
+StartChar: t
+Encoding: 116 116
+Width: 1000
+EndChar
+StartChar: u
+Encoding: 117 117
+Width: 1000
+EndChar
+StartChar: v
+Encoding: 118 118
+Width: 1000
+EndChar
+StartChar: w
+Encoding: 119 119
+Width: 1000
+EndChar
+StartChar: x
+Encoding: 120 120
+Width: 1000
+EndChar
+StartChar: y
+Encoding: 121 121
+Width: 1000
+EndChar
+StartChar: z
+Encoding: 122 122
+Width: 1000
+EndChar
+StartChar: braceleft
+Encoding: 123 123
+Width: 1000
+EndChar
+StartChar: bar
+Encoding: 124 124
+Width: 1000
+EndChar
+StartChar: braceright
+Encoding: 125 125
+Width: 1000
+EndChar
+StartChar: asciitilde
+Encoding: 126 126
+Width: 1000
+EndChar
+StartChar: quotesingle
+Encoding: 169 39
+Width: 1000
+EndChar
+StartChar: grave
+Encoding: 193 96
+Width: 1000
+EndChar
+EndChars
+EndSplineFont
View
BIN  fontfiles/ExistenceTest.ttf
Binary file not shown
View
120 freetyp2.c
@@ -12,7 +12,7 @@ freetyp2.c - font support via the FreeType library version 2.
if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
double matrix[6];
if (!i_ft2_settransform(font, matrix)) { error }
- int bbox[6];
+ int bbox[BOUNDING_BOX_COUNT];
if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
i_img *im = ...;
i_color cl;
@@ -294,6 +294,8 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
int glyph_ascent, glyph_descent;
FT_Glyph_Metrics *gm;
int start = 0;
+ int loadFlags = FT_LOAD_DEFAULT;
+ int rightb;
mm_log((1, "i_ft2_bbox(handle %p, cheight %f, cwidth %f, text %p, len %d, bbox %p)\n",
handle, cheight, cwidth, text, len, bbox));
@@ -305,6 +307,9 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
i_push_error(0, "setting size");
}
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
+
first = 1;
width = 0;
while (len) {
@@ -322,7 +327,7 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
}
index = FT_Get_Char_Index(handle->face, c);
- error = FT_Load_Glyph(handle->face, index, FT_LOAD_DEFAULT);
+ error = FT_Load_Glyph(handle->face, index, loadFlags);
if (error) {
ft2_push_message(error);
i_push_errorf(0, "loading glyph for character \\x%02x (glyph 0x%04X)",
@@ -351,20 +356,21 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
/* last character
handle the case where the right the of the character overlaps the
right*/
- int rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
- if (rightb < 0)
- width -= rightb / 64;
+ rightb = gm->horiAdvance - gm->horiBearingX - gm->width;
+ if (rightb > 0)
+ rightb = 0;
}
}
- bbox[0] = start;
- bbox[1] = handle->face->size->metrics.descender / 64;
- bbox[2] = width;
- bbox[3] = handle->face->size->metrics.ascender / 64;
- bbox[4] = descent;
- bbox[5] = ascent;
+ bbox[BBOX_NEG_WIDTH] = start;
+ bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
+ bbox[BBOX_POS_WIDTH] = width - rightb;
+ bbox[BBOX_GLOBAL_ASCENT] = handle->face->size->metrics.ascender / 64;
+ bbox[BBOX_DESCENT] = descent;
+ bbox[BBOX_ASCENT] = ascent;
+ bbox[BBOX_ADVANCE_WIDTH] = width;
- return 1;
+ return BBOX_ADVANCE_WIDTH + 1;
}
/*
@@ -461,6 +467,8 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
if (vlayout)
loadFlags |= FT_LOAD_VERTICAL_LAYOUT;
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
error = FT_Set_Char_Size(handle->face, cwidth*64, cheight*64,
handle->xdpi, handle->ydpi);
@@ -534,7 +542,7 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
}
x += slot->advance.x / 64;
y += slot->advance.y / 64;
-
+
if (glyph_ascent > ascent)
ascent = glyph_ascent;
if (glyph_descent > descent)
@@ -567,8 +575,6 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth,
return 1;
}
-
-
static int
make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
@@ -596,7 +602,7 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl,
FT_Error error;
int index;
FT_Glyph_Metrics *gm;
- int bbox[6];
+ int bbox[BOUNDING_BOX_COUNT];
FT_GlyphSlot slot;
int x, y;
unsigned char *bmp;
@@ -878,6 +884,88 @@ make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) {
return 1;
}
+int
+i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) {
+ char const *name = FT_Get_Postscript_Name(handle->face);
+
+ i_clear_error();
+
+ if (name) {
+ strncpy(name_buf, name, name_buf_size);
+ name_buf[name_buf_size-1] = '\0';
+
+ return strlen(name) + 1;
+ }
+ else {
+ i_push_error(0, "no face name available");
+ *name_buf = '\0';
+
+ return 0;
+ }
+}
+
+int
+i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch, char *name_buf,
+ size_t name_buf_size) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ i_clear_error();
+ *name_buf = '\0';
+ i_push_error(0, "FT2 configured without glyph name support");
+
+ return 0;
+#else
+ i_clear_error();
+
+ if (FT_Has_PS_Glyph_Names(handle->face)) {
+ FT_UInt index = FT_Get_Char_Index(handle->face, ch);
+
+ if (index) {
+ FT_Error error = FT_Get_Glyph_Name(handle->face, index, name_buf,
+ name_buf_size);
+ if (error) {
+ ft2_push_message(error);
+ *name_buf = '\0';
+ return;
+ }
+ if (*name_buf) {
+ return strlen(name_buf) + 1;
+ }
+ else {
+ return 0;
+ }
+ }
+ else {
+ i_push_error(0, "no glyph for that character");
+ *name_buf = 0;
+ return 0;
+ }
+ }
+ else {
+ i_push_error(0, "no glyph names in font");
+ *name_buf = '\0';
+ return 0;
+ }
+#endif
+}
+
+int
+i_ft2_can_do_glyph_names(void) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ return 0;
+#else
+ return 1;
+#endif
+}
+
+int
+i_ft2_face_has_glyph_names(FT2_Fonthandle *handle) {
+#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES
+ return 0;
+#else
+ return FT_Has_PS_Glyph_Names(handle->face);
+#endif
+}
+
/*
=back
View
18 image.h
@@ -250,10 +250,13 @@ 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, i_color *cl, int fontnum, float points, char* str, int len, int align, int utf8, char const *flags );
-void i_t1_bbox( int fontnum, float point, char *str, int len, int cords[6], int utf8, char const *flags );
+int i_t1_bbox( int fontnum, float point, char *str, int 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);
+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
#ifdef HAVE_LIBTT
@@ -269,6 +272,11 @@ undef_int i_tt_cp( TT_Fonthandle *handle,i_img *im,int xb,int yb,int channel,flo
undef_int i_tt_text( TT_Fonthandle *handle, i_img *im, int xb, int yb, i_color *cl, float points, char const* txt, int len, int smooth, int utf8);
undef_int i_tt_bbox( TT_Fonthandle *handle, float points,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);
+void i_tt_dump_names(TT_Fonthandle *handle);
+int i_tt_face_name(TT_Fonthandle *handle, char *name_buf,
+ size_t name_buf_size);
+int i_tt_glyph_name(TT_Fonthandle *handle, unsigned long ch, char *name_buf,
+ size_t name_buf_size);
#endif /* End of freetype headers */
@@ -294,6 +302,12 @@ extern int i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty,
int utf8);
extern int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len,
int utf8, char *work);
+extern int i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf,
+ size_t name_buf_size);
+extern int i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned char ch,
+ char *name_buf, size_t name_buf_size);
+extern int i_ft2_can_do_glyph_names(void);
+extern int i_ft2_face_has_glyph_names(FT2_Fonthandle *handle);
#endif
View
15 lib/Imager/Engines.pod
@@ -150,7 +150,9 @@ on colour values too - though you need to be careful - adding 2 white
values together and multiplying by 0.5 will give you grey, not white.
Division by zero (or a small number) just results in a large number.
-Modulo zero (or a small number) results in zero.
+Modulo zero (or a small number) results in zero. % is implemented
+using fmod() so you can use this to take a value mod a floating point
+value.
=item sin(N), cos(N), atan2(y,x)
@@ -248,6 +250,17 @@ For details on expression parsing see L<Imager::Expr>. For details on
the virtual machine used to transform the images, see
L<Imager::regmach.pod>.
+ # generate a colorful spiral
+ # requires that Parse::RecDescent be installed
+ my $newimg = Imager::transform2({
+ width => 160, height=>160,
+ expr => <<EOS
+ dist = distance(x, y, w/2, h/2);
+ angle = atan2(y-h/2, x-w/2);
+ angle2 = (dist / 10 + angle) % ( 2 * pi );
+ return hsv(angle*180/pi, 1, (sin(angle2)+1)/2);
+ EOS
+ });
=head2 Matrix Transformations
View
267 lib/Imager/Font.pm
@@ -117,13 +117,15 @@ sub draw {
}
$input{string} = _first($input{string}, $input{text});
unless (defined $input{string}) {
- $Imager::ERRSTR = "Missing require parameter 'string'";
+ $Imager::ERRSTR = "Missing required parameter 'string'";
return;
}
$input{aa} = _first($input{aa}, $input{antialias}, $self->{aa}, 1);
# the original draw code worked this out but didn't use it
$input{align} = _first($input{align}, $self->{align});
$input{color} = _first($input{color}, $self->{color});
+ $input{color} = Imager::_color($input{'color'});
+
$input{size} = _first($input{size}, $self->{size});
unless (defined $input{size}) {
$input{image}{ERRSTR} = "No font size provided";
@@ -136,6 +138,81 @@ sub draw {
$self->_draw(%input);
}
+sub align {
+ my $self = shift;
+ my %input = ( halign => 'left', valign => 'baseline',
+ 'x' => 0, 'y' => 0, @_ );
+
+ my $text = _first($input{string}, $input{text});
+ unless (defined $text) {
+ Imager->_set_error("Missing required parameter 'string'");
+ return;
+ }
+
+ # image needs to be supplied, but can be supplied as undef
+ unless (exists $input{image}) {
+ Imager->_set_error("Missing required parameter 'image'");
+ return;
+ }
+ my $size = _first($input{size}, $self->{size});
+ my $utf8 = _first($input{utf8}, 0);
+
+ my $bbox = $self->bounding_box(string=>$text, size=>$size, utf8=>$utf8);
+ my $valign = $input{valign};
+ $valign = 'baseline'
+ unless $valign && $valign =~ /^(?:top|center|bottom|baseline)$/;
+
+ my $halign = $input{halign};
+ $halign = 'start'
+ unless $halign && $halign =~ /^(?:left|start|center|end|right)$/;
+
+ my $x = $input{'x'};
+ my $y = $input{'y'};
+
+ if ($valign eq 'top') {
+ $y += $bbox->ascent;
+ }
+ elsif ($valign eq 'center') {
+ $y += $bbox->ascent - $bbox->text_height / 2;
+ }
+ elsif ($valign eq 'bottom') {
+ $y += $bbox->descent;
+ }
+ # else baseline is the default
+
+ if ($halign eq 'left') {
+ $x -= $bbox->start_offset;
+ }
+ elsif ($halign eq 'start') {
+ # nothing to do
+ }
+ elsif ($halign eq 'center') {
+ $x -= $bbox->start_offset + $bbox->total_width / 2;
+ }
+ elsif ($halign eq 'end' || $halign eq 'right') {
+ $x -= $bbox->start_offset + $bbox->total_width - 1;
+ }
+ $x = int($x);
+ $y = int($y);
+
+ if ($input{image}) {
+ delete @input{qw/x y/};
+ $self->draw(%input, 'x' => $x, 'y' => $y, align=>1)
+ or return;
+# for my $i (1 .. length $text) {
+# my $work = substr($text, 0, $i);
+# my $bbox = $self->bounding_box(string=>$work, size=>$size, utf8=>$utf8);
+# my $nx = $x + $bbox->end_offset;
+# $input{image}->setpixel(x=>[ ($nx) x 5 ],
+# 'y'=>[ $y-2, $y-1, $y, $y+1, $y+2 ],
+# color=>'FF0000');
+# }
+ }
+
+ return ($x+$bbox->start_offset, $y-$bbox->ascent,
+ $x+$bbox->end_offset, $y-$bbox->descent+1);
+}
+
sub bounding_box {
my $self=shift;
my %input=@_;
@@ -150,17 +227,24 @@ sub bounding_box {
my @box = $self->_bounding_box(%input);
- if(@box && exists $input{'x'} and exists $input{'y'}) {
- my($gdescent, $gascent)=@box[1,3];
- $box[1]=$input{'y'}-$gascent; # top = base - ascent (Y is down)
- $box[3]=$input{'y'}-$gdescent; # bottom = base - descent (Y is down, descent is negative)
- $box[0]+=$input{'x'};
- $box[2]+=$input{'x'};
- } elsif (@box && $input{'canon'}) {
- $box[3]-=$box[1]; # make it cannoical (ie (0,0) - (width, height))
- $box[2]-=$box[0];
+ if (wantarray) {
+ if(@box && exists $input{'x'} and exists $input{'y'}) {
+ my($gdescent, $gascent)=@box[1,3];
+ $box[1]=$input{'y'}-$gascent; # top = base - ascent (Y is down)
+ $box[3]=$input{'y'}-$gdescent; # bottom = base - descent (Y is down, descent is negative)
+ $box[0]+=$input{'x'};
+ $box[2]+=$input{'x'};
+ } elsif (@box && $input{'canon'}) {
+ $box[3]-=$box[1]; # make it cannoical (ie (0,0) - (width, height))
+ $box[2]-=$box[0];
+ }
+ return @box;
+ }
+ else {
+ require Imager::Font::BBox;
+
+ return Imager::Font::BBox->new(@box);
}
- return @box;
}
sub dpi {
@@ -314,24 +398,53 @@ Other logical font attributes may be added if there is sufficient demand.
Returns the bounding box for the specified string. Example:
- ($neg_width,
- $global_descent,
- $pos_width,
- $global_ascent,
- $descent,
- $ascent) = $font->bounding_box(string => "A Fool");
+ my ($neg_width,
+ $global_descent,
+ $pos_width,
+ $global_ascent,
+ $descent,
+ $ascent,
+ $advance_width) = $font->bounding_box(string => "A Fool");
+
+ my $bbox_object = $font->bounding_box(string => "A Fool");
+
+=over
+
+=item C<$neg_width>
-The C<$neg_width> is the relative start of a the string. In some
+the relative start of a the string. In some
cases this can be a negative number, in that case the first letter
stretches to the left of the starting position that is specified in
-the string method of the Imager class. <$global_descent> is the how
-far down the lowest letter of the entire font reaches below the
-baseline (this is often j). C<$pos_width> is how wide the string
-from the starting position is. The total width of the string is
-C<$pos_width-$neg_width>. C<$descent> and C<$ascent> are the same as
-<$global_descent> and <$global_ascent> except that they are only for
-the characters that appear in the string. Obviously we can stuff all
-the results into an array just as well:
+the string method of the Imager class
+
+=item C<$global_descent>
+
+how far down the lowest letter of the entire font reaches below the
+baseline (this is often j).
+
+=item C<$pos_width>
+
+how wide the string from
+the starting position is. The total width of the string is
+C<$pos_width-$neg_width>.
+
+=item C<$descent>
+
+=item C<$ascent>
+
+the same as <$global_descent> and <$global_ascent> except that they
+are only for the characters that appear in the string.
+
+=item C<$advance_width>
+
+the distance from the start point that the next string output should
+start at, this is often the same as C<$pos_width>, but can be
+different if the final character overlaps the right side of its
+character cell.
+
+=back
+
+Obviously we can stuff all the results into an array just as well:
@metrics = $font->bounding_box(string => "testing 123");
@@ -353,7 +466,9 @@ but:
$bbox[2] - horizontal space taken by glyphs
$bbox[3] - vertical space taken by glyphs
-
+Returns an L<Imager::Font::BBox> object in scalar context, so you can
+avoid all those confusing indices. This has methods as named above,
+with some extra convenience methods.
=item string
@@ -444,6 +559,84 @@ Sometimes it is necessary to know how much space a string takes before
rendering it. The bounding_box() method described earlier can be used
for that.
+=item align(string=>$text, size=>$size, x=>..., y=>..., valign => ..., halign=>...)
+
+Higher level text output - outputs the text aligned as specified
+around the given point (x,y).
+
+ # "Hello" centered at 100, 100 in the image.
+ my ($left, $top, $bottom, $right) =
+ $font->align(string=>"Hello",
+ x=>100, y=>100,
+ halign=>'center', valign=>'center',
+ image=>$image);
+
+Takes the same parameters as $font->draw(), and the following extra
+parameters:
+
+=over
+
+=item valign
+
+Possible values are:
+
+=over
+
+=item top
+
+Point is at the top of the text.
+
+=item bottom
+
+Point is at the bottom of the text.
+
+=item baseline
+
+Point is on the baseline of the text (default.)
+
+=item center
+
+Point is vertically centered within the text.
+
+=back
+
+=item halign
+
+=over
+
+=item left
+
+The point is at the left of the text.
+
+=item start
+
+The point is at the start point of the text.
+
+=item center
+
+The point is horizontally centered within the text.
+
+=item right
+
+The point is at the right end of the text.
+
+=item end
+
+The point is at the right end of the text. This will change to the
+end point of the text (once the proper bounding box interfaces are
+available).
+
+=back
+
+=item image
+
+The image to draw to. Set to C<undef> to avoid drawing but still
+calculate the bounding box.
+
+=back
+
+Returns a list specifying the bounds of the drawn text.
+
=item dpi()
=item dpi(xdpi=>$xdpi, ydpi=>$ydpi)
@@ -494,6 +687,24 @@ Imager::Font->new(file=>"arial.ttf", color=>$blue, aa=>1)
->string(text=>"Plan XYZ", border=>5)
->write(file=>"xyz.png");
+=item face_name()
+
+Returns the internal name of the face. Not all font types support
+this method yet.
+
+=item glyph_names(string=>$string [, utf8=>$utf8 ] );
+
+Returns a list of glyph names for each of the characters in the
+string. If the character has no name then C<undef> is returned for
+the character.
+
+Some font files do not include glyph names, in this case Freetype 2
+will not return any names. Freetype 1 can return standard names even
+if there are no glyph names in the font.
+
+Both Freetype 1.x and 2.x allow support for glyph names to not be
+included.
+
=back
=head1 UTF8
@@ -588,7 +799,7 @@ You need to modify this class to add new font types.
=head1 SEE ALSO
Imager(3), Imager::Font::FreeType2(3), Imager::Font::Type1(3),
-Imager::Font::Win32(3), Imager::Font::Truetype(3)
+Imager::Font::Win32(3), Imager::Font::Truetype(3), Imager::Font::BBox(3)
http://www.eecs.umich.edu/~addi/perl/Imager/
View
208 lib/Imager/Font/BBox.pm
@@ -0,0 +1,208 @@
+package Imager::Font::BBox;
+use strict;
+
+=head1 NAME
+
+Imager::Font::BBox - objects representing the bounding box of a string.
+
+=head1 SYNOPSIS
+
+ use Imager::Font;
+
+ # get the object
+ my $font = Imager::Font->new(...);
+ my $bbox = $font->bounding_box(string=>$text, size=>$size);
+
+ # methods
+ my $start = $bbox->start_offset;
+ my $end = $bbox->end_offset;
+ my $gdescent = $box->global_descent;
+ my $gascent = $bbox->global_ascent;
+ my $ascent = $bbox->ascent;
+ my $decent = $bbox->descent;
+ my $total_width = $bbox->total_width;
+ my $fheight = $bbox->font_height;
+ my $theight = $bbox->text_height;
+
+=head1 DESCRIPTION
+
+Objects of this class are returned by the Imager::Font bounding_box()
+method when it is called in scalar context.
+
+This will hopefully make the information from this method more
+accessible.
+
+=head1 METHODS
+
+=over
+
+=item start_offset()
+
+Returns the horizonatal offset from the selected drawing location to
+the left edge of the first character drawn. If this is positive, the
+first glyph is to the right of the drawing location.
+
+The alias neg_width() is present to match the bounding_box()
+documentation for list context.
+
+=cut
+
+sub start_offset {
+ return $_[0][0];
+}
+
+sub neg_width {
+ return $_[0][0];
+}
+
+=item end_offset()
+
+The offset from the selected drawing location to the right edge of the
+last character drawn. Should always be positive.
+
+You can use the alias pos_width() if you are used to the
+bounding_box() documentation for list context.
+
+=cut
+
+sub end_offset {
+ return $_[0][2];
+}
+
+sub pos_width {
+ return $_[0][2];
+}
+
+=item global_descent()
+
+The lowest position relative to the font baseline that any character
+in the font reaches in the character cell. Normally negative.
+
+At least one font we've seen has reported a positive number for this.
+
+=cut
+
+sub global_descent {
+ return $_[0][1];
+}
+
+=item global_ascent()
+
+The highest position relative to the font baseline that any character
+in the font reaches in the character cell. Normally positive.
+
+=cut
+
+sub global_ascent {
+ return $_[0][3];
+}
+
+=item descent()
+
+The lowest position relative to the font baseline that any character
+in the supplied string reaches. Negative when any character's glyph
+reaches below the baseline.
+
+=cut
+
+sub descent {
+ return $_[0][4];
+}
+
+=item ascent()
+
+The highest position relative to the font baseline that any character
+in the supplied string reaches. Positive if any character's glyph
+reaches above the baseline.
+
+=cut
+
+sub ascent {
+ return $_[0][5];
+}
+
+=item advance_width()
+
+=cut
+
+sub advance_width {
+ my $self = shift;
+
+ @$self > 6 ? $self->[6] : $self->[5];
+}
+
+=item total_width()
+
+The total displayed width of the string.
+
+=cut
+
+sub total_width {
+ my $self = shift;
+
+ $self->end_offset - $self->start_offset;
+}
+
+=item font_height()
+
+The maximum displayed height of any string using this font.
+
+=cut
+
+sub font_height {
+ my $self = shift;
+ $self->global_ascent - $self->global_descent;
+}
+
+=item text_height()
+
+The displayed height of the supplied string.
+
+=cut
+
+sub text_height {
+ my $self = shift;
+
+ $self->ascent - $self->descent;
+}
+
+=back
+
+=head1 INTERNAL FUNCTIONS
+
+=over
+
+=item new(...)
+
+Called by Imager::Font->bounding_box() to create the object.
+
+=cut
+
+sub new {
+ my $class = shift;
+ return bless [ @_ ], $class;
+}
+
+=back
+
+=head1 BUGS
+
+Doesn't reproduce the functionality that you get using the x and y
+parameters to Imager::Font->bounding_box(). I considered:
+
+ my ($left, $top, $right, $bottom) = $box->offset(x=>$x, y=>$y)
+
+but this is about as clumsy as the original.
+
+=head1 AUTHOR
+
+Tony Cook <tony@develop-help.com>
+
+=head1 SEE ALSO
+
+Imager(3), Imager::Font(3)
+
+=cut
+
+1;
+
View
24 lib/Imager/Font/FreeType2.pm
@@ -3,6 +3,9 @@ use strict;
use Imager::Color;
use vars qw(@ISA);
@ISA = qw(Imager::Font);
+
+*_first = \&Imager::Font::_first;
+
sub new {
my $class = shift;
my %hsh=(color=>Imager::Color->new(255,0,0,0),
@@ -114,6 +117,27 @@ sub has_chars {
return i_ft2_has_chars($self->{id}, $hsh{string}, $hsh{'utf8'} || 0);
}
+sub face_name {
+ my ($self) = @_;
+
+ i_ft2_face_name($self->{id});
+}
+
+sub can_glyph_names {
+ i_ft2_can_do_glyph_names();
+}
+
+sub glyph_names {
+ my ($self, %input) = @_;
+
+ my $string = $input{string};
+ defined $string
+ or return Imager->_seterror("no string parameter passed to glyph_names");
+ my $utf8 = _first($input{utf8} || 0);
+
+ i_ft2_glyph_name($self->{id}, $string, $utf8);
+}
+
1;
__END__
View
19 lib/Imager/Font/Truetype.pm
@@ -3,6 +3,8 @@ use strict;
use vars qw(@ISA);
@ISA = qw(Imager::Font);
+*_first = \&Imager::Font::_first;
+
sub new {
my $class = shift;
my %hsh=(color=>Imager::Color->new(255,0,0,0),
@@ -79,6 +81,23 @@ sub has_chars {
return Imager::i_tt_has_chars($self->{id}, $hsh{string}, $hsh{'utf8'} || 0);
}
+sub face_name {
+ my ($self) = @_;
+
+ Imager::i_tt_face_name($self->{id});
+}
+
+sub glyph_names {
+ my ($self, %input) = @_;
+
+ my $string = $input{string};
+ defined $string