X-Git-Url: http://git.imager.perl.org/imager.git/blobdiff_plain/f62b2d84592a72eed5a3cb4a92ba315a706e7dcd..da4e02834c5896d938832b3735a39eeaa289bea9:/freetyp2.c diff --git a/freetyp2.c b/freetyp2.c index e155d525..8cb6be9c 100644 --- a/freetyp2.c +++ b/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; @@ -38,9 +38,14 @@ Truetype, Type1 and Windows FNT. #include #include #include FT_FREETYPE_H +#ifdef FT_MULTIPLE_MASTERS_H +#ifndef T1_CONFIG_OPTION_NO_MM_SUPPORT +#define IM_FT2_MM +#include FT_MULTIPLE_MASTERS_H +#endif +#endif static void ft2_push_message(int code); -static unsigned long utf8_advance(char **p, int *len); static FT_Library library; @@ -71,9 +76,37 @@ struct FT2_Fonthandle { FT_Face face; int xdpi, ydpi; int hint; + FT_Encoding encoding; /* used to adjust so we can align the draw point to the top-left */ double matrix[6]; + +#ifdef IM_FT2_MM + /* Multiple master data if any */ + int has_mm; + FT_Multi_Master mm; +#endif +}; + +/* the following is used to select a "best" encoding */ +static struct enc_score { + FT_Encoding encoding; + int score; +} enc_scores[] = +{ + /* the selections here are fairly arbitrary + ideally we need to give the user a list of encodings available + and a mechanism to choose one */ + { ft_encoding_unicode, 10 }, + { ft_encoding_sjis, 8 }, + { ft_encoding_gb2312, 8 }, + { ft_encoding_big5, 8 }, + { ft_encoding_wansung, 8 }, + { ft_encoding_johab, 8 }, + { ft_encoding_latin_2, 6 }, + { ft_encoding_apple_roman, 6 }, + { ft_encoding_adobe_standard, 6 }, + { ft_encoding_adobe_expert, 6 }, }; /* @@ -93,8 +126,9 @@ i_ft2_new(char *name, int index) { FT_Error error; FT2_Fonthandle *result; FT_Face face; - double matrix[6] = { 1, 0, 0, - 0, 1, 0 }; + int i, j; + FT_Encoding encoding; + int score; mm_log((1, "i_ft2_new(name %p, index %d)\n", name, index)); @@ -103,12 +137,32 @@ i_ft2_new(char *name, int index) { if (error) { ft2_push_message(error); i_push_error(error, "Opening face"); + mm_log((2, "error opening face '%s': %d\n", name, error)); return NULL; } + encoding = face->num_charmaps ? face->charmaps[0]->encoding : ft_encoding_unicode; + score = 0; + for (i = 0; i < face->num_charmaps; ++i) { + FT_Encoding enc_entry = face->charmaps[i]->encoding; + mm_log((2, "i_ft2_new, encoding %lX platform %u encoding %u\n", + enc_entry, face->charmaps[i]->platform_id, + face->charmaps[i]->encoding_id)); + for (j = 0; j < sizeof(enc_scores) / sizeof(*enc_scores); ++j) { + if (enc_scores[j].encoding == enc_entry && enc_scores[j].score > score) { + encoding = enc_entry; + score = enc_scores[j].score; + break; + } + } + } + FT_Select_Charmap(face, encoding); + mm_log((2, "i_ft2_new, selected encoding %lX\n", encoding)); + result = mymalloc(sizeof(FT2_Fonthandle)); result->face = face; result->xdpi = result->ydpi = 72; + result->encoding = encoding; /* by default we disable hinting on a call to i_ft2_settransform() if we don't do this, then the hinting can the untransformed text @@ -122,6 +176,27 @@ i_ft2_new(char *name, int index) { result->matrix[0] = 1; result->matrix[1] = 0; result->matrix[2] = 0; result->matrix[3] = 0; result->matrix[4] = 1; result->matrix[5] = 0; +#ifdef IM_FT2_MM + { + FT_Multi_Master *mm = &result->mm; + int i; + + if ((face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) != 0 + && (error = FT_Get_Multi_Master(face, mm)) == 0) { + mm_log((2, "MM Font, %d axes, %d designs\n", mm->num_axis, mm->num_designs)); + for (i = 0; i < mm->num_axis; ++i) { + mm_log((2, " axis %d name %s range %ld - %ld\n", i, mm->axis[i].name, + (long)(mm->axis[i].minimum), (long)(mm->axis[i].maximum))); + } + result->has_mm = 1; + } + else { + mm_log((2, "No multiple masters\n")); + result->has_mm = 0; + } + } +#endif + return result; } @@ -242,7 +317,7 @@ Returns non-zero on success. */ int i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, - char *text, int len, int *bbox, int utf8) { + char const *text, int len, int *bbox, int utf8) { FT_Error error; int width; int index; @@ -251,6 +326,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)); @@ -262,12 +339,15 @@ 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) { unsigned long c; if (utf8) { - c = utf8_advance(&text, &len); + c = i_utf8_advance(&text, &len); if (c == ~0UL) { i_push_error(0, "invalid UTF8 character"); return 0; @@ -279,7 +359,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)", @@ -299,7 +379,7 @@ i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth, if (glyph_ascent > ascent) ascent = glyph_ascent; - if (glyph_descent > descent) + if (glyph_descent < descent) descent = glyph_descent; width += gm->horiAdvance / 64; @@ -308,20 +388,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)/64; + 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; } /* @@ -345,7 +426,6 @@ too much hard work. void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) { double work[8]; double *matrix = handle->matrix; - int i; work[0] = matrix[0] * bbox[0] + matrix[1] * bbox[1]; work[1] = matrix[3] * bbox[0] + matrix[4] * bbox[1]; @@ -356,10 +436,10 @@ void ft2_transform_box(FT2_Fonthandle *handle, int bbox[4]) { work[6] = matrix[0] * bbox[2] + matrix[1] * bbox[3]; work[7] = matrix[3] * bbox[2] + matrix[4] * bbox[3]; - bbox[0] = floor(min(min(work[0], work[2]),min(work[4], work[6]))); - bbox[1] = floor(min(min(work[1], work[3]),min(work[5], work[7]))); - bbox[2] = ceil(max(max(work[0], work[2]),max(work[4], work[6]))); - bbox[3] = ceil(max(max(work[1], work[3]),max(work[5], work[7]))); + bbox[0] = floor(i_min(i_min(work[0], work[2]),i_min(work[4], work[6]))); + bbox[1] = floor(i_min(i_min(work[1], work[3]),i_min(work[5], work[7]))); + bbox[2] = ceil(i_max(i_max(work[0], work[2]),i_max(work[4], work[6]))); + bbox[3] = ceil(i_max(i_max(work[1], work[3]),i_max(work[5], work[7]))); } /* @@ -371,10 +451,10 @@ bounding box in bbox[] that encloses both. =cut */ static void expand_bounds(int bbox[4], int bbox2[4]) { - bbox[0] = min(bbox[0], bbox2[0]); - bbox[1] = min(bbox[1], bbox2[1]); - bbox[2] = max(bbox[2], bbox2[2]); - bbox[3] = max(bbox[3], bbox2[3]); + bbox[0] = i_min(bbox[0], bbox2[0]); + bbox[1] = i_min(bbox[1], bbox2[1]); + bbox[2] = i_max(bbox[2], bbox2[2]); + bbox[3] = i_max(bbox[3], bbox2[3]); } /* @@ -399,7 +479,7 @@ Returns non-zero on success. */ int i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, - char *text, int len, int vlayout, int utf8, int *bbox) { + char const *text, int len, int vlayout, int utf8, int *bbox) { FT_Error error; int width; int index; @@ -407,17 +487,17 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, int ascent = 0, descent = 0; int glyph_ascent, glyph_descent; FT_Glyph_Metrics *gm; - int start = 0; int work[4]; int bounds[4]; double x = 0, y = 0; int i; FT_GlyphSlot slot; - int advx, advy; int loadFlags = FT_LOAD_DEFAULT; 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); @@ -431,7 +511,7 @@ i_ft2_bbox_r(FT2_Fonthandle *handle, double cheight, double cwidth, while (len) { unsigned long c; if (utf8) { - c = utf8_advance(&text, &len); + c = i_utf8_advance(&text, &len); if (c == ~0UL) { i_push_error(0, "invalid UTF8 character"); return 0; @@ -491,7 +571,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) @@ -524,8 +604,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); @@ -548,12 +626,12 @@ Returns non-zero on success. */ int i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, - double cheight, double cwidth, char *text, int len, int align, + double cheight, double cwidth, char const *text, int len, int align, int aa, int vlayout, int utf8) { 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; @@ -589,7 +667,7 @@ i_ft2_text(FT2_Fonthandle *handle, i_img *im, int tx, int ty, i_color *cl, while (len) { unsigned long c; if (utf8) { - c = utf8_advance(&text, &len); + c = i_utf8_advance(&text, &len); if (c == ~0UL) { i_push_error(0, "invalid UTF8 character"); return 0; @@ -689,7 +767,7 @@ Returns non-zero on success. */ i_ft2_cp(FT2_Fonthandle *handle, i_img *im, int tx, int ty, int channel, - double cheight, double cwidth, char *text, int len, int align, + double cheight, double cwidth, char const *text, int len, int align, int aa, int vlayout, int utf8) { int bbox[8]; i_img *work; @@ -741,17 +819,17 @@ Returns the number of characters that were checked. =cut */ -int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8, - char *out) { +int i_ft2_has_chars(FT2_Fonthandle *handle, char const *text, int len, + int utf8, char *out) { int count = 0; - mm_log((1, "i_ft2_check_chars(handle %p, text %p, len %d, utf8 %d)\n", + mm_log((1, "i_ft2_has_chars(handle %p, text %p, len %d, utf8 %d)\n", handle, text, len, utf8)); while (len) { unsigned long c; int index; if (utf8) { - c = utf8_advance(&text, &len); + c = i_utf8_advance(&text, &len); if (c == ~0UL) { i_push_error(0, "invalid UTF8 character"); return 0; @@ -773,10 +851,10 @@ int i_ft2_has_chars(FT2_Fonthandle *handle, char *text, int len, int utf8, /* uses a method described in fterrors.h to build an error translation function */ -#undef __FT_ERRORS_H__ +#undef __FTERRORS_H__ #define FT_ERRORDEF(e, v, s) case v: i_push_error(code, s); return; -#define FT_ERROR_START_LIST -#define FT_ERROR_END_LIST +#define FT_ERROR_START_LIST +#define FT_ERROR_END_LIST /* =back @@ -835,81 +913,201 @@ make_bmp_map(FT_Bitmap *bitmap, unsigned char *map) { return 1; } -struct utf8_size { - int mask, expect; - int size; -}; +/* FREETYPE_PATCH was introduced in 2.0.6, we don't want a false + positive on 2.0.0 to 2.0.4, so we accept a false negative in 2.0.5 */ +#ifndef FREETYPE_PATCH +#define FREETYPE_PATCH 4 +#endif -struct utf8_size utf8_sizes[] = -{ - { 0x80, 0x00, 1 }, - { 0xE0, 0xC0, 2 }, - { 0xF0, 0xE0, 3 }, - { 0xF8, 0xF0, 4 }, -}; +/* FT_Get_Postscript_Name() was introduced in FT2.0.5 */ +#define IM_HAS_FACE_NAME (FREETYPE_MINOR > 0 || FREETYPE_PATCH >= 5) +/* #define IM_HAS_FACE_NAME 0 */ /* -=item utf8_advance(char **p, int *len) - -Retreive a UTF8 character from the stream. +=item i_ft2_face_name(handle, name_buf, name_buf_size) -Modifies *p and *len to indicate the consumed characters. - -This doesn't support the extended UTF8 encoding used by later versions -of Perl. +Fills the given buffer with the Postscript Face name of the font, +if there is one. =cut */ -unsigned long utf8_advance(char **p, int *len) { - unsigned char c; - int i, ci, clen = 0; - unsigned char codes[3]; - if (*len == 0) - return ~0UL; - c = *(*p)++; --*len; - - for (i = 0; i < sizeof(utf8_sizes)/sizeof(*utf8_sizes); ++i) { - if ((c & utf8_sizes[i].mask) == utf8_sizes[i].expect) { - clen = utf8_sizes[i].size; - } +int +i_ft2_face_name(FT2_Fonthandle *handle, char *name_buf, size_t name_buf_size) { +#if IM_HAS_FACE_NAME + 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; } - if (clen == 0 || *len < clen-1) { - --*p; ++*len; - return ~0UL; + else { + i_push_error(0, "no face name available"); + *name_buf = '\0'; + + return 0; } +#else + i_clear_error(); + i_push_error(0, "Freetype 2.0.6 or later required"); + *name_buf = '\0'; - /* check that each character is well formed */ - i = 1; - ci = 0; - while (i < clen) { - if (((*p)[ci] & 0xC0) != 0x80) { - --*p; ++*len; - return ~0UL; - } - codes[ci] = (*p)[ci]; - ++ci; ++i; + return 0; +#endif +} + +int +i_ft2_can_face_name(void) { + return IM_HAS_FACE_NAME; +} + +/* FT_Has_PS_Glyph_Names() was introduced in FT2.1.1 */ +/* well, I assume FREETYPE_MAJOR is 2, since we're here */ +#if FREETYPE_MINOR < 1 || (FREETYPE_MINOR == 1 && FREETYPE_PATCH < 1) +#define FT_Has_PS_Glyph_Names(face) (FT_HAS_GLYPH_NAMES(face)) +#endif + +int +i_ft2_glyph_name(FT2_Fonthandle *handle, unsigned long ch, char *name_buf, + size_t name_buf_size, int reliable_only) { +#ifdef FT_CONFIG_OPTION_NO_GLYPH_NAMES + i_clear_error(); + *name_buf = '\0'; + i_push_error(0, "FT2 configured without glyph name support"); + + return 0; +#else + FT_UInt index; + + i_clear_error(); + + if (!FT_HAS_GLYPH_NAMES(handle->face)) { + i_push_error(0, "no glyph names in font"); + *name_buf = '\0'; + return 0; } - *p += clen-1; *len -= clen-1; - if (c & 0x80) { - if ((c & 0xE0) == 0xC0) { - return ((c & 0x1F) << 6) + (codes[0] & 0x3F); - } - else if ((c & 0xF0) == 0xE0) { - return ((c & 0x0F) << 12) | ((codes[0] & 0x3F) << 6) | (codes[1] & 0x3f); + if (reliable_only && !FT_Has_PS_Glyph_Names(handle->face)) { + i_push_error(0, "no reliable glyph names in font - set reliable_only to 0 to try anyway"); + *name_buf = '\0'; + return 0; + } + + 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; } - else if ((c & 0xF8) == 0xF0) { - return ((c & 0x07) << 18) | ((codes[0] & 0x3F) << 12) - | ((codes[1] & 0x3F) << 6) | (codes[2] & 0x3F); + if (*name_buf) { + return strlen(name_buf) + 1; } else { - *p -= clen; *len += clen; - return ~0UL; + return 0; } } else { - return c; + i_push_error(0, "no glyph for that character"); + *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 +} + +int +i_ft2_is_multiple_master(FT2_Fonthandle *handle) { + i_clear_error(); +#ifdef IM_FT2_MM + return handle->has_mm; +#else + return 0; +#endif +} + +int +i_ft2_get_multiple_masters(FT2_Fonthandle *handle, i_font_mm *mm) { +#ifdef IM_FT2_MM + int i; + FT_Multi_Master *mms = &handle->mm; + + i_clear_error(); + if (!handle->has_mm) { + i_push_error(0, "Font has no multiple masters"); + return 0; + } + mm->num_axis = mms->num_axis; + mm->num_designs = mms->num_designs; + for (i = 0; i < mms->num_axis; ++i) { + mm->axis[i].name = mms->axis[i].name; + mm->axis[i].minimum = mms->axis[i].minimum; + mm->axis[i].maximum = mms->axis[i].maximum; + } + + return 1; +#else + i_clear_error(); + i_push_error(0, "Multiple master functions unavailable"); + return 0; +#endif +} + +int +i_ft2_set_mm_coords(FT2_Fonthandle *handle, int coord_count, long *coords) { +#ifdef IM_FT2_MM + int i; + FT_Long ftcoords[T1_MAX_MM_AXIS]; + FT_Error error; + + i_clear_error(); + if (!handle->has_mm) { + i_push_error(0, "Font has no multiple masters"); + return 0; + } + if (coord_count != handle->mm.num_axis) { + i_push_error(0, "Number of MM coords doesn't match MM axis count"); + return 0; } + for (i = 0; i < coord_count; ++i) + ftcoords[i] = coords[i]; + + error = FT_Set_MM_Design_Coordinates(handle->face, coord_count, ftcoords); + if (error) { + ft2_push_message(error); + return 0; + } + + return 1; +#else + i_clear_error(); + i_push_error(0, "Multiple master functions unavailable"); + + return 0; +#endif } /*