if (!i_ft2_getdpi(font, &xdpi, &ydpi)) { error }
double matrix[6];
if (!i_ft2_settransform(font, matrix)) { error }
- int bbox[6];
- if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox)) { error }
+ int bbox[BOUNDING_BOX_COUNT];
+ if (!i_ft2_bbox(font, cheight, cwidth, text, length, bbox, utf8)) { error }
i_img *im = ...;
i_color cl;
if (!i_ft2_text(font, im, tx, ty, cl, cheight, cwidth, text, length, align,
#include <stdio.h>
#include <ft2build.h>
#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;
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 },
};
/*
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));
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
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;
}
*/
int
i_ft2_bbox(FT2_Fonthandle *handle, double cheight, double cwidth,
- char *text, int len, int *bbox) {
+ char const *text, int len, int *bbox, int utf8) {
FT_Error error;
int width;
int index;
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));
i_push_error(0, "setting size");
}
+ if (!handle->hint)
+ loadFlags |= FT_LOAD_NO_HINTING;
+
first = 1;
width = 0;
- while (len--) {
- int c = (unsigned char)*text++;
-
+ 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;
+ }
+
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)",
if (glyph_ascent > ascent)
ascent = glyph_ascent;
- if (glyph_descent > descent)
+ if (glyph_descent < descent)
descent = glyph_descent;
width += gm->horiAdvance / 64;
/* 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;
-
- return 1;
+ bbox[BBOX_NEG_WIDTH] = start;
+ bbox[BBOX_GLOBAL_DESCENT] = handle->face->size->metrics.descender / 64;
+ bbox[BBOX_POS_WIDTH] = width;
+ if (rightb < 0)
+ bbox[BBOX_POS_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;
+ bbox[BBOX_RIGHT_BEARING] = rightb;
+ mm_log((1, " bbox=> negw=%d glob_desc=%d pos_wid=%d glob_asc=%d desc=%d asc=%d adv_width=%d rightb=%d\n", bbox[0], bbox[1], bbox[2], bbox[3], bbox[4], bbox[5], bbox[6], bbox[7]));
+
+ return BBOX_RIGHT_BEARING + 1;
}
/*
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];
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])));
}
/*
=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]);
}
/*
*/
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;
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);
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;
}
x += slot->advance.x / 64;
y += slot->advance.y / 64;
-
+
if (glyph_ascent > ascent)
ascent = glyph_ascent;
if (glyph_descent > descent)
return 1;
}
-
-
static int
make_bmp_map(FT_Bitmap *bitmap, unsigned char *map);
*/
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;
loadFlags |= FT_LOAD_NO_HINTING;
/* set the base-line based on the string ascent */
- if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox))
+ if (!i_ft2_bbox(handle, cheight, cwidth, text, len, bbox, utf8))
return 0;
if (!align) {
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;
=cut
*/
+int
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;
return 1;
}
+/*
+=item i_ft2_has_chars(handle, char *text, int len, int utf8, char *out)
+
+Check if the given characters are defined by the font.
+
+Returns the number of characters that were checked.
+
+=cut
+*/
+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_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 = i_utf8_advance(&text, &len);
+ if (c == ~0UL) {
+ i_push_error(0, "invalid UTF8 character");
+ return 0;
+ }
+ }
+ else {
+ c = (unsigned char)*text++;
+ --len;
+ }
+
+ index = FT_Get_Char_Index(handle->face, c);
+ *out++ = index != 0;
+ ++count;
+ }
+
+ return count;
+}
+
/* 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
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 0;
}
- 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
}
/*