[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/WeatherGod/matplotlib/mplot3d/asanyarray/src/ft2font.cpp [Back]  [Original]

/* -*- mode: c++; c-basic-offset: 4 -*- */

#include "ft2font.h"
#include "mplutils.h"
#include 

#include "numpy/arrayobject.h"

/*
 By definition, FT_FIXED as 2 16bit values stored in a single long.
 We cast to long to ensure the correct Py::Int convertor is called
 */
#define FIXED_MAJOR(val) (long) ((val & 0xffff000) >> 16)
#define FIXED_MINOR(val) (long) (val & 0xffff)

/**
 To improve the hinting of the fonts, this code uses a hack
 presented here:

 http://antigrain.com/research/font_rasterization/index.html

 The idea is to limit the effect of hinting in the x-direction, while
 preserving hinting in the y-direction.  Since freetype does not
 support this directly, the dpi in the x-direction is set higher than
 in the y-direction, which affects the hinting grid.  Then, a global
 transform is placed on the font to shrink it back to the desired
 size.  While it is a bit surprising that the dpi setting affects
 hinting, whereas the global transform does not, this is documented
 behavior of freetype, and therefore hopefully unlikely to change.
 The freetype 2 tutorial says:

      NOTE: The transformation is applied to every glyph that is
      loaded through FT_Load_Glyph and is completely independent of
      any hinting process. This means that you won't get the same
      results if you load a glyph at the size of 24 pixels, or a glyph
      at the size at 12 pixels scaled by 2 through a transform,
      because the hints will have been computed differently (except
      you have disabled hints).

 This hack is enabled only when VERTICAL_HINTING is defined, and will
 only be effective when load_char and set_text are called with 'flags=
 LOAD_DEFAULT', which is the default.
 */
#define VERTICAL_HINTING
#ifdef VERTICAL_HINTING
#define HORIZ_HINTING 8
#else
#define HORIZ_HINTING 1
#endif

FT_Library _ft2Library;

// FT2Image::FT2Image() :
//   _isDirty(true),
//   _buffer(NULL),
//   _width(0), _height(0),
//   _rgbCopy(NULL),
//   _rgbaCopy(NULL) {
//   _VERBOSE("FT2Image::FT2Image");
// }

FT2Image::FT2Image(unsigned long width, unsigned long height) :
    _isDirty(true),
    _buffer(NULL),
    _width(0), _height(0),
    _rgbCopy(NULL),
    _rgbaCopy(NULL)
{
    _VERBOSE("FT2Image::FT2Image");
    resize(width, height);
}

FT2Image::~FT2Image()
{
    _VERBOSE("FT2Image::~FT2Image");
    delete [] _buffer;
    _buffer = NULL;
    delete _rgbCopy;
    delete _rgbaCopy;
}

void
FT2Image::resize(long width, long height)
{
    if (width < 0)
    {
        width = 1;
    }
    if (height < 0)
    {
        height = 1;
    }
    size_t numBytes = width * height;

    if ((unsigned long)width != _width || (unsigned long)height != _height)
    {
        if (numBytes > _width*_height)
        {
            delete [] _buffer;
            _buffer = NULL;
            _buffer = new unsigned char [numBytes];
        }

        _width = (unsigned long)width;
        _height = (unsigned long)height;
    }

    memset(_buffer, 0, numBytes);

    _isDirty = true;
}

void
FT2Image::draw_bitmap(FT_Bitmap*  bitmap,
                      FT_Int      x,
                      FT_Int      y)
{
    _VERBOSE("FT2Image::draw_bitmap");
    FT_Int image_width = (FT_Int)_width;
    FT_Int image_height = (FT_Int)_height;
    FT_Int char_width =  bitmap->width;
    FT_Int char_height = bitmap->rows;

    FT_Int x1 = CLAMP(x, 0, image_width);
    FT_Int y1 = CLAMP(y, 0, image_height);
    FT_Int x2 = CLAMP(x + char_width, 0, image_width);
    FT_Int y2 = CLAMP(y + char_height, 0, image_height);

    FT_Int x_start = MAX(0, -x);
    FT_Int y_offset = y1 - MAX(0, -y);

    for (FT_Int i = y1; i < y2; ++i)
    {
        unsigned char* dst = _buffer + (i * image_width + x1);
        unsigned char* src = bitmap->buffer + (((i - y_offset) * bitmap->pitch) + x_start);
        for (FT_Int j = x1; j < x2; ++j, ++dst, ++src)
            *dst |= *src;
    }

    _isDirty = true;
}

void
FT2Image::write_bitmap(const char* filename) const
{
    FILE *fh = fopen(filename, "w");

    for (size_t i = 0; i < _height; i++)
    {
        for (size_t j = 0; j < _width; ++j)
        {
            if (_buffer[j + i*_width])
            {
                fputc('#', fh);
            }
            else
            {
                fputc(' ', fh);
            }
        }
        fputc('\n', fh);
    }

    fclose(fh);
}

char FT2Image::write_bitmap__doc__[] =
    "write_bitmap(fname)\n"
    "\n"
    "Write the bitmap to file fname\n"
    ;
Py::Object
FT2Image::py_write_bitmap(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::write_bitmap");

    args.verify_length(1);

    std::string filename = Py::String(args[0]);

    write_bitmap(filename.c_str());

    return Py::Object();
}

void
FT2Image::draw_rect(unsigned long x0, unsigned long y0,
                    unsigned long x1, unsigned long y1)
{
    if (x0 > _width || x1 > _width ||
        y0 > _height || y1 > _height)
    {
        throw Py::ValueError("Rect coords outside image bounds");
    }

    size_t top = y0 * _width;
    size_t bottom = y1 * _width;
    for (size_t i = x0; i < x1 + 1; ++i)
    {
        _buffer[i + top] = 255;
        _buffer[i + bottom] = 255;
    }

    for (size_t j = y0 + 1; j < y1; ++j)
    {
        _buffer[x0 + j*_width] = 255;
        _buffer[x1 + j*_width] = 255;
    }

    _isDirty = true;
}

char FT2Image::draw_rect__doc__[] =
    "draw_rect(x0, y0, x1, y1)\n"
    "\n"
    "Draw a rect to the image.\n"
    "\n"
    ;
Py::Object
FT2Image::py_draw_rect(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::draw_rect");

    args.verify_length(4);

    long x0 = Py::Int(args[0]);
    long y0 = Py::Int(args[1]);
    long x1 = Py::Int(args[2]);
    long y1 = Py::Int(args[3]);

    draw_rect(x0, y0, x1, y1);

    return Py::Object();
}

void
FT2Image::draw_rect_filled(unsigned long x0, unsigned long y0,
                           unsigned long x1, unsigned long y1)
{
    x0 = std::min(x0, _width);
    y0 = std::min(y0, _height);
    x1 = std::min(x1, _width);
    y1 = std::min(y1, _height);

    for (size_t j = y0; j < y1 + 1; j++)
    {
        for (size_t i = x0; i < x1 + 1; i++)
        {
            _buffer[i + j*_width] = 255;
        }
    }

    _isDirty = true;
}

char FT2Image::draw_rect_filled__doc__[] =
    "draw_rect_filled(x0, y0, x1, y1)\n"
    "\n"
    "Draw a filled rect to the image.\n"
    "\n"
    ;
Py::Object
FT2Image::py_draw_rect_filled(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::draw_rect_filled");

    args.verify_length(4);

    long x0 = Py::Int(args[0]);
    long y0 = Py::Int(args[1]);
    long x1 = Py::Int(args[2]);
    long y1 = Py::Int(args[3]);

    draw_rect_filled(x0, y0, x1, y1);

    return Py::Object();
}

char FT2Image::as_str__doc__[] =
    "width, height, s = image_as_str()\n"
    "\n"
    "Return the image buffer as a string\n"
    "\n"
    ;
Py::Object
FT2Image::py_as_str(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::as_str");
    args.verify_length(0);

    return Py::asObject
      (PyString_FromStringAndSize((const char *)_buffer,
                                  _width*_height)
       );
}

char FT2Image::as_array__doc__[] =
    "x = image.as_array()\n"
    "\n"
    "Return the image buffer as a width x height numpy array of ubyte \n"
    "\n"
    ;
Py::Object
FT2Image::py_as_array(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::as_array");
    args.verify_length(0);

    npy_intp dimensions[2];
    dimensions[0] = get_height();  //numrows
    dimensions[1] = get_width();   //numcols


    PyArrayObject *A = (PyArrayObject *) PyArray_SimpleNewFromData(2, dimensions, PyArray_UBYTE, _buffer);

    return Py::asObject((PyObject*)A);
}

void
FT2Image::makeRgbCopy()
{
    if (!_isDirty)
    {
        return;
    }

    if (!_rgbCopy)
    {
        _rgbCopy = new FT2Image(_width * 3, _height);
    }
    else
    {
        _rgbCopy->resize(_width * 3, _height);
    }
    unsigned char *src            = _buffer;
    unsigned char *src_end        = src + (_width * _height);
    unsigned char *dst            = _rgbCopy->_buffer;

    unsigned char tmp;
    while (src != src_end)
    {
        tmp = 255 - *src++;
        *dst++ = tmp;
        *dst++ = tmp;
        *dst++ = tmp;
    }
}

char FT2Image::as_rgb_str__doc__[] =
    "width, height, s = image_as_rgb_str()\n"
    "\n"
    "Return the image buffer as a 24-bit RGB string.\n"
    "\n"
    ;
Py::Object
FT2Image::py_as_rgb_str(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::as_str_rgb");
    args.verify_length(0);

    makeRgbCopy();

    return _rgbCopy->py_as_str(args);
}

void FT2Image::makeRgbaCopy()
{
    if (!_isDirty)
    {
        return;
    }

    if (!_rgbaCopy)
    {
        _rgbaCopy = new FT2Image(_width * 4, _height);
    }
    else
    {
        _rgbaCopy->resize(_width * 4, _height);
    }
    unsigned char *src            = _buffer;
    unsigned char *src_end        = src + (_width * _height);
    unsigned char *dst            = _rgbaCopy->_buffer;

    while (src != src_end)
    {
        // We know the array has already been zero'ed out in
        // the resize method, so we just skip over the r, g and b.
        dst += 3;
        *dst++ = *src++;
    }
}

char FT2Image::as_rgba_str__doc__[] =
    "width, height, s = image_as_rgb_str()\n"
    "\n"
    "Return the image buffer as a 32-bit RGBA string.\n"
    "\n"
    ;
Py::Object
FT2Image::py_as_rgba_str(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::as_str_rgba");
    args.verify_length(0);

    makeRgbaCopy();

    return _rgbaCopy->py_as_str(args);
}

Py::Object
FT2Image::py_get_width(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::get_width");
    args.verify_length(0);

    return Py::Int((long)get_width());
}

Py::Object
FT2Image::py_get_height(const Py::Tuple & args)
{
    _VERBOSE("FT2Image::get_height");
    args.verify_length(0);

    return Py::Int((long)get_height());
}

Glyph::Glyph(const FT_Face& face, const FT_Glyph& glyph, size_t ind) :
        glyphInd(ind)
{
    _VERBOSE("Glyph::Glyph");

    FT_BBox bbox;
    FT_Glyph_Get_CBox(glyph, ft_glyph_bbox_subpixels, &bbox);

    setattr("width",        Py::Int(face->glyph->metrics.width / HORIZ_HINTING));
    setattr("height",       Py::Int(face->glyph->metrics.height));
    setattr("horiBearingX", Py::Int(face->glyph->metrics.horiBearingX / HORIZ_HINTING));
    setattr("horiBearingY", Py::Int(face->glyph->metrics.horiBearingY));
    setattr("horiAdvance",  Py::Int(face->glyph->metrics.horiAdvance));
    setattr("linearHoriAdvance",  Py::Int(face->glyph->linearHoriAdvance / HORIZ_HINTING));
    setattr("vertBearingX", Py::Int(face->glyph->metrics.vertBearingX));

    setattr("vertBearingY", Py::Int(face->glyph->metrics.vertBearingY));
    setattr("vertAdvance",  Py::Int(face->glyph->metrics.vertAdvance));
    //setattr("bitmap_left",  Py::Int( face->glyph->bitmap_left) );
    //setattr("bitmap_top",  Py::Int( face->glyph->bitmap_top) );

    Py::Tuple abbox(4);

    abbox[0] = Py::Int(bbox.xMin);
    abbox[1] = Py::Int(bbox.yMin);
    abbox[2] = Py::Int(bbox.xMax);
    abbox[3] = Py::Int(bbox.yMax);
    setattr("bbox", abbox);
}

Glyph::~Glyph()
{
    _VERBOSE("Glyph::~Glyph");
}

int
Glyph::setattr(const char *name, const Py::Object &value)
{
    _VERBOSE("Glyph::setattr");
    __dict__[name] = value;
    return 0;
}

Py::Object
Glyph::getattr(const char *name)
{
    _VERBOSE("Glyph::getattr");
    if (__dict__.hasKey(name)) return __dict__[name];
    else return getattr_default(name);
}

inline double conv(int v)
{
    return double(v) / 64.0;
}


//see http://freetype.sourceforge.net/freetype2/docs/glyphs/glyphs-6.html
Py::Object
FT2Font::get_path()
{
    //get the glyph as a path, a list of (COMMAND, *args) as desribed in matplotlib.path
    // this code is from agg's decompose_ft_outline with minor modifications

    if (!face->glyph) {
        throw Py::ValueError("No glyph loaded");
    }

    enum {STOP = 0,
          MOVETO = 1,
          LINETO = 2,
          CURVE3 = 3,
          CURVE4 = 4,
          ENDPOLY = 0x4f};
    FT_Outline& outline = face->glyph->outline;
    bool flip_y = false; //todo, pass me as kwarg

    FT_Vector   v_last;
    FT_Vector   v_control;
    FT_Vector   v_start;

    FT_Vector*  point;
    FT_Vector*  limit;
    char*       tags;

    int   n;         // index of contour in outline
    int   first;     // index of first point in contour
    char  tag;       // current point's state
    int   count;

    count = 0;
    first = 0;
    for (n = 0; n < outline.n_contours; n++)
    {
        int  last;  // index of last point in contour

        last  = outline.contours[n];
        limit = outline.points + last;

        v_start = outline.points[first];
        v_last  = outline.points[last];

        v_control = v_start;

        point = outline.points + first;
        tags  = outline.tags  + first;
        tag   = FT_CURVE_TAG(tags[0]);

        // A contour cannot start with a cubic control point!
        if (tag == FT_CURVE_TAG_CUBIC)
        {
            throw Py::RuntimeError("A contour cannot start with a cubic control point");
        }

        count++;

        while (point < limit)
        {
            point++;
            tags++;

            tag = FT_CURVE_TAG(tags[0]);
            switch (tag)
            {
            case FT_CURVE_TAG_ON:  // emit a single line_to
            {
                count++;
                continue;
            }

            case FT_CURVE_TAG_CONIC:  // consume conic arcs
            {
            Count_Do_Conic:
                if (point < limit)
                {
                    point++;
                    tags++;
                    tag = FT_CURVE_TAG(tags[0]);

                    if (tag == FT_CURVE_TAG_ON)
                    {
                        count += 2;
                        continue;
                    }

                    if (tag != FT_CURVE_TAG_CONIC)
                    {
                        throw Py::RuntimeError("Invalid font");
                    }

                    count += 2;

                    goto Count_Do_Conic;
                }

                count += 2;

                goto Count_Close;
            }

            default:  // FT_CURVE_TAG_CUBIC
            {
                if (point + 1 > limit || FT_CURVE_TAG(tags[1]) != FT_CURVE_TAG_CUBIC)
                {
                    throw Py::RuntimeError("Invalid font");
                }

                point += 2;
                tags  += 2;

                if (point x);
                double y = flip_y ? -conv(point->y) : conv(point->y);
                *(outpoints++) = x;
                *(outpoints++) = y;
                *(outcodes++) = LINETO;
                continue;
            }

            case FT_CURVE_TAG_CONIC:  // consume conic arcs
            {
                v_control.x = point->x;
                v_control.y = point->y;

            Do_Conic:
                if (point < limit)
                {
                    FT_Vector vec;
                    FT_Vector v_middle;

                    point++;
                    tags++;
                    tag = FT_CURVE_TAG(tags[0]);

                    vec.x = point->x;
                    vec.y = point->y;

                    if (tag == FT_CURVE_TAG_ON)
                    {
                        double xctl = conv(v_control.x);
                        double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
                        double xto = conv(vec.x);
                        double yto = flip_y ? -conv(vec.y) : conv(vec.y);
                        *(outpoints++) = xctl;
                        *(outpoints++) = yctl;
                        *(outpoints++) = xto;
                        *(outpoints++) = yto;
                        *(outcodes++) = CURVE3;
                        *(outcodes++) = CURVE3;
                        continue;
                    }

                    v_middle.x = (v_control.x + vec.x) / 2;
                    v_middle.y = (v_control.y + vec.y) / 2;

                    double xctl = conv(v_control.x);
                    double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
                    double xto = conv(v_middle.x);
                    double yto = flip_y ? -conv(v_middle.y) : conv(v_middle.y);
                    *(outpoints++) = xctl;
                    *(outpoints++) = yctl;
                    *(outpoints++) = xto;
                    *(outpoints++) = yto;
                    *(outcodes++) = CURVE3;
                    *(outcodes++) = CURVE3;

                    v_control = vec;
                    goto Do_Conic;
                }
                double xctl = conv(v_control.x);
                double yctl = flip_y ? -conv(v_control.y) : conv(v_control.y);
                double xto = conv(v_start.x);
                double yto = flip_y ? -conv(v_start.y) : conv(v_start.y);

                *(outpoints++) = xctl;
                *(outpoints++) = yctl;
                *(outpoints++) = xto;
                *(outpoints++) = yto;
                *(outcodes++) = CURVE3;
                *(outcodes++) = CURVE3;

                goto Close;
            }

            default:  // FT_CURVE_TAG_CUBIC
            {
                FT_Vector vec1, vec2;

                vec1.x = point[0].x;
                vec1.y = point[0].y;
                vec2.x = point[1].x;
                vec2.y = point[1].y;

                point += 2;
                tags  += 2;

                if (point x;
                    vec.y = point->y;

                    double xctl1 = conv(vec1.x);
                    double yctl1 = flip_y ? -conv(vec1.y) : conv(vec1.y);
                    double xctl2 = conv(vec2.x);
                    double yctl2 = flip_y ? -conv(vec2.y) : conv(vec2.y);
                    double xto = conv(vec.x);
                    double yto = flip_y ? -conv(vec.y) : conv(vec.y);

                    (*outpoints++) = xctl1;
                    (*outpoints++) = yctl1;
                    (*outpoints++) = xctl2;
                    (*outpoints++) = yctl2;
                    (*outpoints++) = xto;
                    (*outpoints++) = yto;
                    (*outcodes++) = CURVE4;
                    (*outcodes++) = CURVE4;
                    (*outcodes++) = CURVE4;
                    continue;
                }

                double xctl1 = conv(vec1.x);
                double yctl1 = flip_y ? -conv(vec1.y) : conv(vec1.y);
                double xctl2 = conv(vec2.x);
                double yctl2 = flip_y ? -conv(vec2.y) : conv(vec2.y);
                double xto = conv(v_start.x);
                double yto = flip_y ? -conv(v_start.y) : conv(v_start.y);
                (*outpoints++) = xctl1;
                (*outpoints++) = yctl1;
                (*outpoints++) = xctl2;
                (*outpoints++) = yctl2;
                (*outpoints++) = xto;
                (*outpoints++) = yto;
                (*outcodes++) = CURVE4;
                (*outcodes++) = CURVE4;
                (*outcodes++) = CURVE4;

                goto Close;
            }
            }
        }

        (*outpoints++) = 0.0;
        (*outpoints++) = 0.0;
        (*outcodes++) = ENDPOLY;

    Close:
        first = last + 1;
    }

    if (outcodes - (unsigned char *)PyArray_DATA(codes) != count) {
        throw Py::RuntimeError("Font path size doesn't match");
    }

    return result;
}


FT2Font::FT2Font(std::string facefile) :
    image(NULL)
{
    _VERBOSE(Printf("FT2Font::FT2Font %s", facefile.c_str()).str());
    clear(Py::Tuple(0));

    int error = FT_New_Face(_ft2Library, facefile.c_str(), 0, &face);

    if (error == FT_Err_Unknown_File_Format)
    {
        std::ostringstream s;
        s underline_thickness));
    }

    setattr("fname", Py::String(facefile));

    _VERBOSE("FT2Font::FT2Font done");
}

FT2Font::~FT2Font()
{
    _VERBOSE("FT2Font::~FT2Font");

    Py_XDECREF(image);
    FT_Done_Face(face);

    for (size_t i = 0; i < glyphs.size(); i++)
    {
        FT_Done_Glyph(glyphs[i]);
    }
}

int
FT2Font::setattr(const char *name, const Py::Object &value)
{
    _VERBOSE("FT2Font::setattr");
    __dict__[name] = value;
    return 1;
}

Py::Object
FT2Font::getattr(const char *name)
{
    _VERBOSE("FT2Font::getattr");
    if (__dict__.hasKey(name)) return __dict__[name];
    else return getattr_default(name);
}

char FT2Font::clear__doc__[] =
    "clear()\n"
    "\n"
    "Clear all the glyphs, reset for a new set_text"
    ;

Py::Object
FT2Font::clear(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::clear");
    args.verify_length(0);

    Py_XDECREF(image);
    image = NULL;

    angle = 0.0;

    pen.x = 0;
    pen.y = 0;

    for (size_t i = 0; i < glyphs.size(); i++)
    {
        FT_Done_Glyph(glyphs[i]);
    }

    glyphs.clear();

    return Py::Object();
}


char FT2Font::set_size__doc__[] =
    "set_size(ptsize, dpi)\n"
    "\n"
    "Set the point size and dpi of the text.\n"
    ;

Py::Object
FT2Font::set_size(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::set_size");
    args.verify_length(2);

    double ptsize = Py::Float(args[0]);
    double dpi = Py::Float(args[1]);

#ifdef VERTICAL_HINTING
    int error = FT_Set_Char_Size(face, (long)(ptsize * 64), 0,
                                 (unsigned int)dpi * HORIZ_HINTING,
                                 (unsigned int)dpi);
    static FT_Matrix transform = { 65536 / HORIZ_HINTING, 0, 0, 65536 };
    FT_Set_Transform(face, &transform, 0);
#else
    int error = FT_Set_Char_Size(face, (long)(ptsize * 64), 0,
                                 (unsigned int)dpi,
                                 (unsigned int)dpi);
#endif
    if (error)
    {
        throw Py::RuntimeError("Could not set the fontsize");
    }
    return Py::Object();
}


char FT2Font::set_charmap__doc__[] =
    "set_charmap(i)\n"
    "\n"
    "Make the i-th charmap current\n"
    ;

Py::Object
FT2Font::set_charmap(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::set_charmap");
    args.verify_length(1);

    int i = Py::Int(args[0]);
    if (i >= face->num_charmaps)
    {
        throw Py::ValueError("i exceeds the available number of char maps");
    }
    FT_CharMap charmap = face->charmaps[i];
    if (FT_Set_Charmap(face, charmap))
    {
        throw Py::ValueError("Could not set the charmap");
    }
    return Py::Object();
}

char FT2Font::select_charmap__doc__[] =
    "select_charmap(i)\n"
    "\n"
    "select charmap i where i is one of the FT_Encoding number\n"
    ;

Py::Object
FT2Font::select_charmap(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::set_charmap");
    args.verify_length(1);

    unsigned long i = Py::Long(args[0]);
    //if (FT_Select_Charmap( face, FT_ENCODING_ADOBE_CUSTOM ))
    if (FT_Select_Charmap(face, (FT_Encoding) i))
    {
        throw Py::ValueError("Could not set the charmap");
    }
    return Py::Object();
}

FT_BBox
FT2Font::compute_string_bbox()
{
    _VERBOSE("FT2Font::compute_string_bbox");

    FT_BBox bbox;
    /* initialize string bbox to "empty" values */
    bbox.xMin = bbox.yMin = 32000;
    bbox.xMax = bbox.yMax = -32000;

    int right_side = 0;
    for (size_t n = 0; n < glyphs.size(); n++)
    {
        FT_BBox glyph_bbox;
        FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_subpixels, &glyph_bbox);
        if (glyph_bbox.xMin < bbox.xMin) bbox.xMin = glyph_bbox.xMin;
        if (glyph_bbox.yMin < bbox.yMin) bbox.yMin = glyph_bbox.yMin;
        if (glyph_bbox.xMin == glyph_bbox.xMax)
        {
            right_side += glyphs[n]->advance.x >> 10;
            if (right_side > bbox.xMax) bbox.xMax = right_side;
        }
        else
        {
            if (glyph_bbox.xMax > bbox.xMax) bbox.xMax = glyph_bbox.xMax;
        }
        if (glyph_bbox.yMax > bbox.yMax) bbox.yMax = glyph_bbox.yMax;
    }
    /* check that we really grew the string bbox */
    if (bbox.xMin > bbox.xMax)
    {
        bbox.xMin = 0;
        bbox.yMin = 0;
        bbox.xMax = 0;
        bbox.yMax = 0;
    }
    return bbox;
}


char FT2Font::get_kerning__doc__[] =
    "dx = get_kerning(left, right, mode)\n"
    "\n"
    "Get the kerning between left char and right glyph indices\n"
    "mode is a kerning mode constant\n"
    "  KERNING_DEFAULT  - Return scaled and grid-fitted kerning distances\n"
    "  KERNING_UNFITTED - Return scaled but un-grid-fitted kerning distances\n"
    "  KERNING_UNSCALED - Return the kerning vector in original font units\n"
    ;
Py::Object
FT2Font::get_kerning(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::get_kerning");
    args.verify_length(3);
    int left = Py::Int(args[0]);
    int right = Py::Int(args[1]);
    int mode = Py::Int(args[2]);


    if (!FT_HAS_KERNING(face))
    {
        return Py::Int(0);
    }
    FT_Vector delta;

    if (!FT_Get_Kerning(face, left, right, mode, &delta))
    {
        return Py::Int(delta.x / HORIZ_HINTING);
    }
    else
    {
        return Py::Int(0);

    }
}


char FT2Font::set_text__doc__[] =
    "set_text(s, angle)\n"
    "\n"
    "Set the text string and angle.\n"
    "You must call this before draw_glyphs_to_bitmap\n"
    "A sequence of x,y positions is returned";
Py::Object
FT2Font::set_text(const Py::Tuple & args, const Py::Dict & kwargs)
{
    _VERBOSE("FT2Font::set_text");
    args.verify_length(2);


    Py::String text(args[0]);
    std::string stdtext = "";
    Py_UNICODE* pcode = NULL;
    size_t N = 0;
    if (PyUnicode_Check(text.ptr()))
    {
        pcode = PyUnicode_AsUnicode(text.ptr());
        N = PyUnicode_GetSize(text.ptr());
    }
    else
    {
        stdtext = text.as_std_string();
        N = stdtext.size();
    }


    angle = Py::Float(args[1]);

    angle = angle / 360.0 * 2 * 3.14159;

    long flags = FT_LOAD_FORCE_AUTOHINT;
    if (kwargs.hasKey("flags"))
    {
        flags = Py::Long(kwargs["flags"]);
    }

    //this computes width and height in subpixels so we have to divide by 64
    matrix.xx = (FT_Fixed)(cos(angle) * 0x10000L);
    matrix.xy = (FT_Fixed)(-sin(angle) * 0x10000L);
    matrix.yx = (FT_Fixed)(sin(angle) * 0x10000L);
    matrix.yy = (FT_Fixed)(cos(angle) * 0x10000L);

    FT_Bool use_kerning = FT_HAS_KERNING(face);
    FT_UInt previous = 0;

    glyphs.resize(0);
    pen.x = 0;
    pen.y = 0;

    Py::Tuple xys(N);
    for (unsigned int n = 0; n < N; n++)
    {
        std::string thischar("?");
        FT_UInt glyph_index;


        if (pcode == NULL)
        {
            // plain ol string
            thischar = stdtext[n];
            glyph_index = FT_Get_Char_Index(face, stdtext[n]);
        }
        else
        {
            //unicode
            glyph_index = FT_Get_Char_Index(face, pcode[n]);
        }

        // retrieve kerning distance and move pen position
        if (use_kerning && previous && glyph_index)
        {
            FT_Vector delta;
            FT_Get_Kerning(face, previous, glyph_index,
                           FT_KERNING_DEFAULT, &delta);
            pen.x += delta.x / HORIZ_HINTING;
        }
        error = FT_Load_Glyph(face, glyph_index, flags);
        if (error)
        {
            std::cerr left - (string_bbox.xMin / 64.));
        FT_Int y = (FT_Int)((string_bbox.yMax / 64.) - bitmap->top + 1);

        image->draw_bitmap(&bitmap->bitmap, x, y);
    }

    return Py::Object();
}


char FT2Font::get_xys__doc__[] =
    "get_xys()\n"
    "\n"
    "Get the xy locations of the current glyphs\n"
    ;
Py::Object
FT2Font::get_xys(const Py::Tuple & args)
{

    _VERBOSE("FT2Font::get_xys");
    args.verify_length(0);

    FT_BBox string_bbox = compute_string_bbox();
    Py::Tuple xys(glyphs.size());

    for (size_t n = 0; n < glyphs.size(); n++)
    {

        FT_BBox bbox;
        FT_Glyph_Get_CBox(glyphs[n], ft_glyph_bbox_pixels, &bbox);

        error = FT_Glyph_To_Bitmap(&glyphs[n],
                                   ft_render_mode_normal,
                                   0,
                                   1
                                  );
        if (error)
        {
            throw Py::RuntimeError("Could not convert glyph to bitmap");
        }

        FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[n];


        //bitmap left and top in pixel, string bbox in subpixel
        FT_Int x = (FT_Int)(bitmap->left - string_bbox.xMin / 64.);
        FT_Int y = (FT_Int)(string_bbox.yMax / 64. - bitmap->top + 1);
        //make sure the index is non-neg
        x = x < 0 ? 0 : x;
        y = y < 0 ? 0 : y;
        Py::Tuple xy(2);
        xy[0] = Py::Float(x);
        xy[1] = Py::Float(y);
        xys[n] = xy;
    }

    return xys;
}

char FT2Font::draw_glyph_to_bitmap__doc__[] =
    "draw_glyph_to_bitmap(bitmap, x, y, glyph)\n"
    "\n"
    "Draw a single glyph to the bitmap at pixel locations x,y\n"
    "Note it is your responsibility to set up the bitmap manually\n"
    "with set_bitmap_size(w,h) before this call is made.\n"
    "\n"
    "If you want automatic layout, use set_text in combinations with\n"
    "draw_glyphs_to_bitmap.  This function is intended for people who\n"
    "want to render individual glyphs at precise locations, eg, a\n"
    "a glyph returned by load_char\n";

Py::Object
FT2Font::draw_glyph_to_bitmap(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::draw_glyph_to_bitmap");
    args.verify_length(4);

    if (!FT2Image::check(args[0].ptr()))
    {
        throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)");
    }
    FT2Image* im = static_cast(args[0].ptr());

    double xd = Py::Float(args[1]);
    double yd = Py::Float(args[2]);
    long x = (long)xd;
    long y = (long)yd;
    FT_Vector sub_offset;
    sub_offset.x = 0; // int((xd - (double)x) * 64.0);
    sub_offset.y = 0; // int((yd - (double)y) * 64.0);

    if (!Glyph::check(args[3].ptr()))
    {
        throw Py::TypeError("Usage: draw_glyph_to_bitmap(bitmap, x,y,glyph)");
    }
    Glyph* glyph = static_cast(args[3].ptr());

    if ((size_t)glyph->glyphInd >= glyphs.size())
    {
        throw Py::ValueError("glyph num is out of range");
    }

    error = FT_Glyph_To_Bitmap(&glyphs[glyph->glyphInd],
                               ft_render_mode_normal,
                               &sub_offset,  //no additional translation
                               1   //destroy image;
                              );
    if (error)
    {
        throw Py::RuntimeError("Could not convert glyph to bitmap");
    }

    FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyphs[glyph->glyphInd];

    im->draw_bitmap(&bitmap->bitmap, x + bitmap->left, y);
    return Py::Object();
}

char FT2Font::get_glyph_name__doc__[] =
    "get_glyph_name(index)\n"
    "\n"
    "Retrieves the ASCII name of a given glyph in a face.\n"
    ;
Py::Object
FT2Font::get_glyph_name(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::get_glyph_name");
    args.verify_length(1);

    if (!FT_HAS_GLYPH_NAMES(face))
    {
        throw Py::RuntimeError("Face has no glyph names");
    }

    char buffer[128];
    if (FT_Get_Glyph_Name(face, (FT_UInt) Py::Int(args[0]), buffer, 128))
    {
        throw Py::RuntimeError("Could not get glyph names.");
    }
    return Py::String(buffer);
}

char FT2Font::get_charmap__doc__[] =
    "get_charmap()\n"
    "\n"
    "Returns a dictionary that maps the character codes of the selected charmap\n"
    "(Unicode by default) to their corresponding glyph indices.\n"
    ;
Py::Object
FT2Font::get_charmap(const Py::Tuple & args)
{
    _VERBOSE("FT2Font::get_charmap");
    args.verify_length(0);

    FT_UInt index;
    Py::Dict charmap;

    //std::cout encoding 

Web Proxy Viewer  |  New URL  |  Original Page