1. pygame
  2. Untitled project
  3. pygame

Commits

tanoku  committed 22f14a8

Added get_size and get_metrics methods to FreeType font objects.
Metrics handling backend.

  • Participants
  • Parent commits 60d15a4
  • Branches pgreloaded

Comments (0)

Files changed (4)

File doc/src/freetypebase.xml

View file
       <desc>Gets the name of the font face.</desc>
     </attr>
     <method name="get_size">
-      <call>get_size(text) -> int, int</call>
+      <call>get_size(pt, text) -> int, int</call>
       <desc>
           Gets the size which 'text' will occupy when rendered using 
           this Font.
       </desc>
     </method>
+    <method name="get_metrics">
+      <call>get_metrics(pt, text) -> [(int, int, int ...), ...]</call>
+      <desc>
+          Returns the glyph metrics for each character in 'text'.
+      </desc>
+    </method>
     <attr name="height">
       <desc>Gets the height of the Font.</desc>
     </attr>

File src/freetype/ft_font.c

View file
  */
 static PyObject* _ftfont_getsize(PyObject *self, PyObject* args, PyObject *kwds);
 static PyObject* _ftfont_render(PyObject *self, PyObject* args, PyObject *kwds);
+static PyObject* _ftfont_getmetrics(PyObject *self, PyObject* args, PyObject *kwds);
 /* static PyObject* _ftfont_copy(PyObject *self); */
 
 /*
         METH_VARARGS | METH_KEYWORDS,
         DOC_BASE_FONT_GET_SIZE 
     },
+    {
+        "get_metrics", 
+        (PyCFunction) _ftfont_getmetrics,
+        METH_VARARGS | METH_KEYWORDS,
+        DOC_BASE_FONT_GET_METRICS 
+    },
     { 
         "render", 
         (PyCFunction)_ftfont_render, 
 static PyObject*
 _ftfont_getsize(PyObject *self, PyObject* args, PyObject *kwds)
 {
-    /* TODO */
+    PyObject *text, *rtuple = NULL;
+    FT_Error error;
+    int text_size, free_buffer;
+    int width, height;
+    FT_UInt16 *text_buffer = NULL;
+
+    FreeTypeInstance *ft;
+    ASSERT_GRAB_FREETYPE(ft, NULL);
+
+    if (!PyArg_ParseTuple(args, "iO", &text_size, &text))
+        return NULL;
+
+    text_buffer = PGFT_BuildUnicodeString(text, &free_buffer);
+
+    if (!text_buffer)
+    {
+        PyErr_SetString(PyExc_ValueError, "Expecting unicode/bytes string");
+        return NULL;
+    }
+
+    error = PGFT_GetTextSize(ft, (PyFreeTypeFont *)self, 
+            text_buffer, text_size, &width, &height);
+
+    if (error)
+        PyErr_SetString(PyExc_RuntimeError, PGFT_GetError(ft));
+    else
+        rtuple = Py_BuildValue ("(ii)", width, height);
+
+    if (free_buffer)
+        free(text_buffer);
+
+    return rtuple;
+}
+
+static PyObject *
+_ftfont_getmetrics(PyObject *self, PyObject* args, PyObject *kwds)
+{
+    PyObject *text, *list;
+    void *buf;
+    int isunicode = 0;
+    int text_size, char_id, length, i;
+    int minx, miny, maxx, maxy, advance;
+
+    FreeTypeInstance *ft;
+    ASSERT_GRAB_FREETYPE(ft, NULL);
+
+    if (!PyArg_ParseTuple(args, "iO", &text_size, &text))
+        return NULL;
+
+    if (PyUnicode_Check(text))
+    {
+        buf = PyUnicode_AsUnicode(text);
+        isunicode = 1;
+    }
+    else if (Bytes_Check(text))
+    {
+        buf = Bytes_AS_STRING(text);
+    }
+    else
+    {
+        PyErr_SetString(PyExc_TypeError,
+            "argument must be a string or unicode");
+    }
+
+    if (!buf)
+        return NULL;
+
+    if (isunicode)
+        length = PyUnicode_GetSize(text);
+    else
+        length = Bytes_Size(text);
+
+    if (length == 0)
+        Py_RETURN_NONE;
+
+    list = PyList_New(length);
+    if (!list)
+        return NULL;
+
+    for (i = 0; i < length; i++)
+    {
+        if (isunicode)
+            char_id = ((Py_UNICODE *)buf)[i];
+        else
+            char_id = ((char *)buf)[i];
+
+        printf("Character %d \n", char_id);
+
+        if (PGFT_GetMetrics(ft, (PyFreeTypeFont *)self, char_id, 
+                    text_size, &minx, &maxx, &miny, &maxy, &advance) == 0)
+        {
+            PyList_SetItem (list, i, Py_BuildValue(
+                        "(iiiii)", minx, maxx, miny, maxy, advance));
+        }
+        else
+        {
+            Py_INCREF (Py_None);
+            PyList_SetItem (list, i, Py_None); 
+        }
+    }
+
+    return list;
 }
 
 static PyObject*

File src/freetype/ft_wrap.c

View file
 #include "ft_mod.h"
 #include "ft_wrap.h"
 #include "pgfreetype.h"
+#include "pgtypes.h"
 #include "freetypebase_doc.h"
 
-void    _PGTF_SetError(FreeTypeInstance *, const char *, FT_Error);
+void    _PGFT_SetError(FreeTypeInstance *, const char *, FT_Error);
+FT_Face _PGFT_GetFace(FreeTypeInstance *, PyFreeTypeFont *);
+FT_Face _PGFT_GetFaceSized(FreeTypeInstance *, PyFreeTypeFont *, int);
+void    _PGFT_BuildScaler(PyFreeTypeFont *, FTC_Scaler, int);
+int     _PGFT_LoadGlyph(FreeTypeInstance *, PyFreeTypeFont *, FTC_Scaler, int);
+
+
+void _PGFT_GetMetrics_FIXED(FT_Glyph_Metrics *, 
+        int *, int *, int *, int *, int *);
+
+void _PGFT_GetMetrics_SCALABLE(FT_Glyph_Metrics *, 
+        int *, int *, int *, int *, int *);
 
 static FT_Error
-_PGTF_face_request(FTC_FaceID face_id, 
+_PGFT_face_request(FTC_FaceID face_id, 
     FT_Library library, 
     FT_Pointer request_data, 
     FT_Face *aface)
     FT_Error error = 0;
     
     Py_BEGIN_ALLOW_THREADS;
-    error = FT_Open_Face(library, &id->open_args, id->face_index, aface);
+        error = FT_Open_Face(library, &id->open_args, id->face_index, aface);
     Py_END_ALLOW_THREADS;
 
     return error;
 }
 
 void
-_PGTF_SetError(FreeTypeInstance *ft, const char *error_msg, FT_Error error_id)
+_PGFT_SetError(FreeTypeInstance *ft, const char *error_msg, FT_Error error_id)
 {
 #undef __FTERRORS_H__
 #define FT_ERRORDEF( e, v, s )  { e, s },
         strcpy(ft->_error_msg, error_msg);
 }
 
+
+FT_UInt16 *
+PGFT_BuildUnicodeString(PyObject *obj, int *must_free)
+{
+    FT_UInt16 *utf16_buffer = NULL;
+    *must_free = 0;
+
+#ifdef IS_PYTHON_3
+    /* 
+     * If this is Python 3 and we pass an unicode string,
+     * we can access directly its internal contents, as
+     * they are in UCS-2
+     */
+    if (PyUnicode_Check(obj))
+    {
+        utf16_buffer = (FT_UInt16 *)PyUnicode_AS_UNICODE(obj);
+    } else
+#endif
+
+    /*
+     * If we pass a Bytes array, we assume it's standard
+     * text encoded in Latin1 (SDL_TTF does the same).
+     * We need to expand each character into 2 bytes because
+     * FreeType expects UTF16 encodings.
+     *
+     * TODO: What happens if the user passes a byte array
+     * representing e.g. a UTF8 string? He would be mostly
+     * stupid, yes, but we should probably handle it.
+     */
+    if (Bytes_Check(obj))
+    {
+        const char *latin1_buffer;
+        size_t i, len;
+
+        latin1_buffer = (const char *)Bytes_AsString(obj);
+        len = strlen(latin1_buffer);
+
+        utf16_buffer = malloc((len + 1) * sizeof(FT_UInt16));
+
+        for (i = 0; i < len; ++i)
+            utf16_buffer[i] = (FT_UInt16)latin1_buffer[i];
+
+        utf16_buffer[i] = 0;
+        *must_free = 1;
+    }
+
+    return utf16_buffer;
+}
+
 const char *
 PGFT_GetError(FreeTypeInstance *ft)
 {
 const char *
 PGFT_Face_GetFormat(PyFreeTypeFont *font)
 {
-    return FT_Get_X11_Font_Format(font->face); /* FIXME: Portable? */
+    /* 
+     * Disregard the X11, this function is portable
+     * across all supported platforms
+     */
+    return FT_Get_X11_Font_Format(font->face); 
 }
 
 int
     return font->face->height;
 }
 
+FT_Face
+_PGFT_GetFace(FreeTypeInstance *ft,
+        PyFreeTypeFont *font)
+{
+    FT_Error error;
+    FT_Face face;
+
+    error = FTC_Manager_LookupFace(ft->cache_manager,
+            (FTC_FaceID)font,
+            &face);
+
+    if (error)
+    {
+        _PGFT_SetError(ft, "Failed to load face", error);
+        return NULL;
+    }
+
+    return face;
+}
+
+void
+_PGFT_BuildScaler(PyFreeTypeFont *font, FTC_Scaler scale, int size)
+{
+    scale->face_id = (FTC_FaceID)font;
+    scale->width = scale->height = (pguint32)(size * 64);
+    scale->pixel = 0;
+    scale->x_res = scale->y_res = 0;
+}
+
+FT_Face
+_PGFT_GetFaceSized(FreeTypeInstance *ft,
+        PyFreeTypeFont *font,
+        int face_size)
+{
+    FT_Error error;
+    FTC_ScalerRec scale;
+    FT_Size _fts;
+
+    _PGFT_BuildScaler(font, &scale, face_size);
+
+    /*
+     * TODO: Check if face has already been sized?
+     */
+
+    error = FTC_Manager_LookupSize(ft->cache_manager, 
+            &scale, &_fts);
+
+    if (error)
+    {
+        _PGFT_SetError(ft, "Failed to resize face", error);
+        return NULL;
+    }
+
+    return _fts->face;
+}
+
+int
+_PGFT_LoadGlyph(FreeTypeInstance *ft, 
+        PyFreeTypeFont *font,
+        FTC_Scaler scale, 
+        int character)
+{
+    FT_Error error;
+    FT_Glyph glyph;
+    FT_UInt32 char_index;
+
+    char_index = FTC_CMapCache_Lookup(
+            ft->cache_charmap, 
+            (FTC_FaceID)font,
+            -1, (FT_UInt32)character);
+
+    if (char_index == 0)
+        return -1;
+
+    error = FTC_ImageCache_LookupScaler(
+            ft->cache_img,
+            scale,
+            FT_LOAD_DEFAULT, /* TODO: proper load flags */
+            char_index,
+            &glyph, NULL);
+
+    return error;
+}
+
+void _PGFT_GetMetrics_SCALABLE(FT_Glyph_Metrics *metrics, 
+        int *minx, int *maxx, int *miny, int *maxy, int *advance)
+{
+    *minx = FT_FLOOR(metrics->horiBearingX);
+    *maxx = *minx + FT_CEIL(metrics->width);
+
+    *maxy = FT_FLOOR(metrics->horiBearingY);
+    *miny = *maxy - FT_CEIL(metrics->height);
+
+    *advance = FT_CEIL(metrics->horiAdvance);
+}
+
+void _PGFT_GetMetrics_FIXED(FT_Glyph_Metrics *metrics, 
+        int *minx, int *maxx, int *miny, int *maxy, int *advance)
+{
+    *minx = FT_FLOOR(metrics->horiBearingX);
+    *maxx = *minx + FT_CEIL(metrics->horiAdvance);
+
+    *maxy = FT_FLOOR(metrics->horiBearingY);
+    *miny = *maxy - FT_CEIL(metrics->height);
+
+    *advance = FT_CEIL(metrics->horiAdvance);
+}
+
+int PGFT_GetMetrics(FreeTypeInstance *ft, PyFreeTypeFont *font,
+        int character, int font_size, 
+        int *minx, int *maxx, int *miny, int *maxy, int *advance)
+{
+    FT_Error error;
+    FTC_ScalerRec scale;
+    FT_Face face;
+
+    _PGFT_BuildScaler(font, &scale, font_size);
+    face = font->face;
+
+    error = _PGFT_LoadGlyph(ft, font, &scale, character);
+
+    if (error)
+    {
+        _PGFT_SetError(ft, "Failed to load glyph metrics", error);
+        return error;
+    }
+
+    if (FT_IS_SCALABLE(face))
+    {
+        _PGFT_GetMetrics_SCALABLE(&face->glyph->metrics, 
+                minx, maxx, miny, maxy, advance);
+    }
+    else
+    {
+        _PGFT_GetMetrics_FIXED(&face->glyph->metrics, 
+                minx, maxx, miny, maxy, advance);
+    }
+
+    return 0;
+}
+
+int
+PGFT_GetTextSize(FreeTypeInstance *ft, PyFreeTypeFont *font,
+        const FT_UInt16 *text, int font_size, int *w, int *h)
+{
+#define UNICODE_BOM_NATIVE	0xFEFF
+#define UNICODE_BOM_SWAPPED	0xFFFE
+
+    const FT_UInt16 *ch;
+    int swapped;
+    FTC_ScalerRec scale;
+    FT_Face face;
+
+    int minx, maxx, miny, maxy, x, z;
+    int gl_maxx, gl_maxy, gl_minx, gl_miny, gl_advance;
+
+    _PGFT_BuildScaler(font, &scale, font_size);
+
+    /* FIXME: Some way to set the system's default ? */
+    swapped = 0;
+    x = 0;
+    face = font->face;
+
+    minx = maxx = 0;
+    miny = maxy = 0;
+
+    for (ch = text; *ch; ++ch)
+    {
+        FT_UInt16 c = *ch;
+
+        if (c == UNICODE_BOM_NATIVE || c == UNICODE_BOM_SWAPPED)
+        {
+            swapped = (c == UNICODE_BOM_SWAPPED);
+            if (text == ch)
+                ++text;
+
+            continue;
+        }
+
+        if (swapped)
+            c = (FT_UInt16)((c << 8) | (c >> 8));
+
+        if (_PGFT_LoadGlyph(ft, font, &scale, c) != 0)
+            continue;
+
+        /* TODO: Handle kerning */
+
+        if (FT_IS_SCALABLE(face))
+        {
+            _PGFT_GetMetrics_SCALABLE(&face->glyph->metrics, 
+                    &gl_minx, &gl_maxx, &gl_miny, &gl_maxy, &gl_advance);
+        }
+        else
+        {
+            _PGFT_GetMetrics_FIXED(&face->glyph->metrics, 
+                    &gl_minx, &gl_maxx, &gl_miny, &gl_maxy, &gl_advance);
+        }
+
+        z = x + gl_minx;
+		if (minx > z) 
+            minx = z;
+
+        z = x + (gl_advance > gl_maxx) ? gl_advance : gl_maxx;
+		if (maxx < z) 
+            maxx = z;
+
+        if (gl_miny < miny)
+            miny = gl_miny;
+
+        if (gl_maxy > maxy)
+            maxy = gl_maxy;
+
+		x += gl_advance;
+    }
+
+    *w = (maxx - minx);
+    *h = (maxy - miny);
+    return 0;
+}
+
 
 int
 PGFT_TryLoadFont(FreeTypeInstance *ft, PyFreeTypeFont *font)
 {
     FT_Face face;
-    FT_Error error;
+    face = _PGFT_GetFace(ft, font);
 
-    error = FTC_Manager_LookupFace(ft->cache_manager, (FTC_FaceID)font, &face);
-
-    if (error)
-    {
-        _PGTF_SetError(ft, "Failed to load font", error);
-        return error;
-    }
+    if (!face)
+        return -1;
 
     font->face = face;
     return 0;
 
     /* TODO: Free caches */
 
-    FTC_Manager_Done(ft->cache_manager);
-    FT_Done_FreeType(ft->library);
+    if (ft->cache_manager)
+        FTC_Manager_Done(ft->cache_manager);
+
+    if (ft->library)
+        FT_Done_FreeType(ft->library);
 
     free(ft->_error_msg);
     free(ft);
 int
 PGFT_Init(FreeTypeInstance **_instance)
 {
-    FT_Error error = 0;
     FreeTypeInstance *inst = NULL;
 
     inst = malloc(sizeof(FreeTypeInstance));
     memset(inst, 0, sizeof(FreeTypeInstance));
     inst->_error_msg = malloc(1024);
     
-    error = FT_Init_FreeType(&inst->library);
-
-    if (error)
+    if (FT_Init_FreeType(&inst->library) != 0)
         goto error_cleanup;
 
-    error = FTC_Manager_New(
-        inst->library, 0, 0, 0,
-            &_PGTF_face_request,
-            NULL,
-            &inst->cache_manager);
+    if (FTC_Manager_New(inst->library, 0, 0, 0,
+            &_PGFT_face_request, NULL,
+            &inst->cache_manager) != 0)
+        goto error_cleanup;
 
-    if (error)
+    if (FTC_CMapCache_New(inst->cache_manager, 
+            &inst->cache_charmap) != 0)
+        goto error_cleanup;
+
+    if (FTC_SBitCache_New(inst->cache_manager,
+            &inst->cache_sbit) != 0)
+        goto error_cleanup;
+
+    if (FTC_ImageCache_New(inst->cache_manager,
+            &inst->cache_img) != 0)
         goto error_cleanup;
 
     *_instance = inst;
     return 0;
 
 error_cleanup:
-    free (inst->_error_msg); /* FIXME: Needed? */
-    free(inst);
+    PGFT_Quit(inst);
     *_instance = NULL;
 
-    return error ? error : -1;
+    return -1;
 }

File src/freetype/ft_wrap.h

View file
 {
     FT_Library library;
     FTC_Manager cache_manager;
-    FTC_SBitCache cache_bitmap;
+    FTC_SBitCache cache_sbit;
+    FTC_ImageCache cache_img;
     FTC_CMapCache cache_charmap;
 
     char *_error_msg;
 
 #define GET_FONT_ID(f) (&((PyFreeTypeFont *)f)->id)
 
+#define FT_FLOOR(X)	((X & -64) / 64)
+#define FT_CEIL(X)	(((X + 63) & -64) / 64)
+
 const char *PGFT_GetError(FreeTypeInstance *);
 void    PGFT_Quit(FreeTypeInstance *);
 int     PGFT_Init(FreeTypeInstance **);
 const char * PGFT_Face_GetName(PyFreeTypeFont *);
 const char * PGFT_Face_GetFormat(PyFreeTypeFont *);
 
+FT_UInt16 *PGFT_BuildUnicodeString(PyObject *, int *);
+
+int     PGFT_GetTextSize(FreeTypeInstance *, PyFreeTypeFont *,
+            const FT_UInt16 *, int, int *, int *);
+
+int     PGFT_GetMetrics(FreeTypeInstance *ft, PyFreeTypeFont *font,
+            int character, int font_size, 
+            int *minx, int *maxx, int *miny, int *maxy, int *advance);
 
 #endif