/*
  SDL_ttf:  A companion library to SDL for working with TrueType (tm) fonts
  Copyright (C) 1997-2004 Sam Lantinga

  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Library General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  Library General Public License for more details.

  You should have received a copy of the GNU Library General Public
  License along with this library; if not, write to the Free
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

  Sam Lantinga
  slouken@libsdl.org
*/

/* $Id: SDL_ttf.c 2304 2006-05-01 09:26:07Z slouken $ */

#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_ALLOCA_H
#include <alloca.h>
#endif

#ifdef HAVE_ALLOCA
#define ALLOCA(n) ((void*)alloca(n))
#define FREEA(p)
#else
#define ALLOCA(n) malloc(n)
#define FREEA(p) free(p)
#endif

#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_OUTLINE_H
#include FT_TRUETYPE_IDS_H
#include FT_STROKER_H
#include FT_BITMAP_H

#include "SDL.h"
#include "SDL_endian.h"
#include "renpy_ttf.h"

/* FIXME: Right now we assume the gray-scale renderer Freetype is using
   supports 256 shades of gray, but we should instead key off of num_grays
   in the result FT_Bitmap after the FT_Render_Glyph() call. */
#define NUM_GRAYS       256

/* Handy routines for converting from fixed point */
#define FT_FLOOR(X)	((X & -64) / 64)
#define FT_CEIL(X)	(((X + 63) & -64) / 64)

#define CACHED_METRICS	0x10
#define CACHED_BITMAP	0x01
#define CACHED_PIXMAP	0x02

/* Cached glyph information */
typedef struct cached_glyph {
    int stored;
    FT_UInt index;
    FT_Bitmap bitmap;
    FT_Bitmap pixmap;
    int minx;
    int maxx;
    int miny;
    int maxy;
    int yoffset;
    int advance;
    Uint16 cached;
} c_glyph;

/* The structure used to hold internal font information */
struct _RENPY_TTF_Font {
    /* Freetype2 maintains all sorts of useful info itself */
    FT_Face face;

    /* We'll cache these ourselves */
    int height;
    int ascent;
    int descent;
    int lineskip;

    /* The font style */
    int style;

    /* Extra width in glyph bounds for text styles */
    int glyph_overhang;
    float glyph_italics;

    /* Information in the font for underlining */
    int underline_offset;
    int underline_height;

    /* Cache for style-transformed glyphs */
    c_glyph *current;
    c_glyph cache[256];
    c_glyph scratch;

    /* We are responsible for closing the font stream */
    SDL_RWops *src;
    int freesrc;
    FT_Open_Args args;

    /* For non-scalable formats, we must remember which font index size */
    int font_size_family;

    // How much bigger than the default should we render text?
    int expand;

    // The stroker.
    FT_Stroker stroker;
};

/* The FreeType font engine/library */
static FT_Library library;
static int RENPY_TTF_initialized = 0;
static int RENPY_TTF_byteswapped = 0;

/* UNICODE string utilities */
static __inline__ int UNICODE_strlen(const Uint16 *text)
{
    int size = 0;
    while ( *text++ ) {
        ++size;
    }
    return size;
}
static __inline__ void UNICODE_strcpy(Uint16 *dst, const Uint16 *src, int swap)
{
    if ( swap ) {
        while ( *src ) {
            *dst = SDL_Swap16(*src);
            ++src;
            ++dst;
        }
        *dst = '\0';
    } else {
        while ( *src ) {
            *dst = *src;
            ++src;
            ++dst;
        }
        *dst = '\0';
    }
}

/* rcg06192001 get linked library's version. */
const SDL_version *RENPY_TTF_Linked_Version(void)
{
    static SDL_version linked_version;
    RENPY_TTF_VERSION(&linked_version);
    return(&linked_version);
}

/* This function tells the library whether UNICODE text is generally
   byteswapped.  A UNICODE BOM character at the beginning of a string
   will override this setting for that string.
*/
void RENPY_TTF_ByteSwappedUNICODE(int swapped)
{
    RENPY_TTF_byteswapped = swapped;
}

static void RENPY_TTF_SetFTError(const char *msg, FT_Error error)
{
#ifdef USE_FREETYPE_ERRORS
#undef FTERRORS_H
#define FT_ERRORDEF( e, v, s )  { e, s },
    static const struct
    {
        int          err_code;
        const char*  err_msg;
    } ft_errors[] = {
#include <freetype/fterrors.h>
    };
    int i;
    const char *err_msg;
    char buffer[1024];

    err_msg = NULL;
    for ( i=0; i<((sizeof ft_errors)/(sizeof ft_errors[0])); ++i ) {
        if ( error == ft_errors[i].err_code ) {
            err_msg = ft_errors[i].err_msg;
            break;
        }
    }
    if ( ! err_msg ) {
        err_msg = "unknown FreeType error";
    }
    sprintf(buffer, "%s: %s", msg, err_msg);
    RENPY_TTF_SetError(buffer);
#else
    RENPY_TTF_SetError(msg);
#endif /* USE_FREETYPE_ERRORS */
}

int RENPY_TTF_Init( void )
{
    int status = 0;

    if ( ! RENPY_TTF_initialized ) {
        FT_Error error = FT_Init_FreeType( &library );
        if ( error ) {
            RENPY_TTF_SetFTError("Couldn't init FreeType engine", error);
            status = -1;
        }
    }
    if ( status == 0 ) {
        ++RENPY_TTF_initialized;
    }
    return status;
}

static unsigned long RWread(
    FT_Stream stream,
    unsigned long offset,
    unsigned char* buffer,
    unsigned long count
    )
{
    SDL_RWops *src;

    src = (SDL_RWops *)stream->descriptor.pointer;
    SDL_RWseek( src, (int)offset, SEEK_SET );
    if ( count == 0 ) {
        return 0;
    }
    return SDL_RWread( src, buffer, 1, (int)count );
}

RENPY_TTF_Font* RENPY_TTF_OpenFontIndexRW( SDL_RWops *src, int freesrc, int ptsize, long index )
{
    RENPY_TTF_Font* font;
    FT_Error error;
    FT_Face face;
    FT_Fixed scale;
    FT_Stream stream;
    int position;

    if ( ! RENPY_TTF_initialized ) {
        RENPY_TTF_SetError( "Library not initialized" );
        return NULL;
    }

    /* Check to make sure we can seek in this stream */
    position = SDL_RWtell(src);
    if ( position < 0 ) {
        RENPY_TTF_SetError( "Can't seek in stream" );
        return NULL;
    }

    font = (RENPY_TTF_Font*) malloc(sizeof *font);
    if ( font == NULL ) {
        RENPY_TTF_SetError( "Out of memory" );
        return NULL;
    }
    memset(font, 0, sizeof(*font));

    font->src = src;
    font->freesrc = freesrc;

    stream = (FT_Stream)malloc(sizeof(*stream));
    if ( stream == NULL ) {
        RENPY_TTF_SetError( "Out of memory" );
        RENPY_TTF_CloseFont( font );
        return NULL;
    }
    memset(stream, 0, sizeof(*stream));

    stream->memory = NULL;  /* set by FT_Open_Face */
    stream->read = RWread;
    stream->descriptor.pointer = src;
    stream->pos = (unsigned long)position;
    SDL_RWseek(src, 0, SEEK_END);
    stream->size = (unsigned long)(SDL_RWtell(src) - position);
    SDL_RWseek(src, position, SEEK_SET);

    font->args.flags = FT_OPEN_STREAM;
    font->args.stream = stream;

    error = FT_Open_Face( library, &font->args, index, &font->face );
    if( error ) {
        RENPY_TTF_SetFTError( "Couldn't load font file", error );
        RENPY_TTF_CloseFont( font );
        return NULL;
    }
    face = font->face;

    /* Make sure that our font face is scalable (global metrics) */
    if ( FT_IS_SCALABLE(face) ) {

        /* Set the character size and use default DPI (72) */
        error = FT_Set_Char_Size( font->face, 0, ptsize * 64, 0, 0 );
        if( error ) {
            RENPY_TTF_SetFTError( "Couldn't set font size", error );
            RENPY_TTF_CloseFont( font );
            return NULL;
        }

        /* Get the scalable font metrics for this font */
        scale = face->size->metrics.y_scale;
        font->ascent  = FT_CEIL(FT_MulFix(face->ascender, scale));
        font->descent = FT_CEIL(FT_MulFix(face->descender, scale));
        font->height  = font->ascent - font->descent + /* baseline */ 1;
        font->lineskip = FT_CEIL(FT_MulFix(face->height, scale));
        font->underline_offset = FT_FLOOR(FT_MulFix(face->underline_position, scale));
        font->underline_height = FT_FLOOR(FT_MulFix(face->underline_thickness, scale));

    } else {
        /* Non-scalable font case.  ptsize determines which family
         * or series of fonts to grab from the non-scalable format.
         * It is not the point size of the font.
         * */
        if ( ptsize >= font->face->num_fixed_sizes )
            ptsize = font->face->num_fixed_sizes - 1;
        font->font_size_family = ptsize;
        error = FT_Set_Pixel_Sizes( face,
                                    face->available_sizes[ptsize].height,
                                    face->available_sizes[ptsize].width );
        /* With non-scalale fonts, Freetype2 likes to fill many of the
         * font metrics with the value of 0.  The size of the
         * non-scalable fonts must be determined differently
         * or sometimes cannot be determined.
         * */
        font->ascent = face->available_sizes[ptsize].height;
        font->descent = 0;
        font->height = face->available_sizes[ptsize].height;
        font->lineskip = FT_CEIL(font->ascent);
        font->underline_offset = FT_FLOOR(face->underline_position);
        font->underline_height = FT_FLOOR(face->underline_thickness);
    }

    if ( font->underline_height < 1 ) {
        font->underline_height = 1;
    }

    font->expand = 0;
    
#ifdef DEBUG_FONTS
    printf("Font metrics:\n");
    printf("\tascent = %d, descent = %d\n",
           font->ascent, font->descent);
    printf("\theight = %d, lineskip = %d\n",
           font->height, font->lineskip);
    printf("\tunderline_offset = %d, underline_height = %d\n",
           font->underline_offset, font->underline_height);
#endif

    /* Set the default font style */
    font->style = RENPY_TTF_STYLE_NORMAL;
    font->glyph_overhang = face->size->metrics.y_ppem / 10;
    /* x offset = cos(((90.0-12)/360)*2*M_PI), or 12 degree angle */
    font->glyph_italics = 0.207f;
    font->glyph_italics *= font->height;

    font->stroker = NULL;
    
    return font;
}

RENPY_TTF_Font* RENPY_TTF_OpenFontRW( SDL_RWops *src, int freesrc, int ptsize )
{
    return RENPY_TTF_OpenFontIndexRW(src, freesrc, ptsize, 0);
}

RENPY_TTF_Font* RENPY_TTF_OpenFontIndex( const char *file, int ptsize, long index )
{
    SDL_RWops *rw = SDL_RWFromFile(file, "rb");
    if ( rw == NULL ) {
        RENPY_TTF_SetError(SDL_GetError());
        return NULL;
    }
    return RENPY_TTF_OpenFontIndexRW(rw, 1, ptsize, index);
}

RENPY_TTF_Font* RENPY_TTF_OpenFont( const char *file, int ptsize )
{
    return RENPY_TTF_OpenFontIndex(file, ptsize, 0);
}

static void Flush_Glyph( c_glyph* glyph )
{
    glyph->stored = 0;
    glyph->index = 0;
    if( glyph->bitmap.buffer ) {
        free( glyph->bitmap.buffer );
        glyph->bitmap.buffer = 0;
    }
    if( glyph->pixmap.buffer ) {
        free( glyph->pixmap.buffer );
        glyph->pixmap.buffer = 0;
    }
    glyph->cached = 0;
}
	
static void Flush_Cache( RENPY_TTF_Font* font )
{
    int i;
    int size = sizeof( font->cache ) / sizeof( font->cache[0] );

    for( i = 0; i < size; ++i ) {
        if( font->cache[i].cached ) {
            Flush_Glyph( &font->cache[i] );
        }

    }
    if( font->scratch.cached ) {
        Flush_Glyph( &font->scratch );
    }

}

static FT_Error Load_Glyph( RENPY_TTF_Font* font, Uint16 ch, c_glyph* cached, int want )
{
    FT_Face face;
    FT_Error error;
    FT_GlyphSlot glyph;
    FT_Glyph_Metrics* metrics;
    FT_Outline* outline;
    FT_Glyph realglyph;
    
    if ( !font || !font->face ) {
        return FT_Err_Invalid_Handle;
    }

    face = font->face;

    /* Load the glyph */
    if ( ! cached->index ) {
        cached->index = FT_Get_Char_Index( face, ch );
    }
    error = FT_Load_Glyph( face, cached->index, FT_LOAD_DEFAULT );
    if( error ) {
        return error;
    }

    /* Get our glyph shortcuts */
    glyph = face->glyph;
    metrics = &glyph->metrics;
    outline = &glyph->outline;

    
    /* Get the glyph metrics if desired */
    if ( (want & CACHED_METRICS) && !(cached->stored & CACHED_METRICS) ) {
        if ( FT_IS_SCALABLE( face ) ) {
            /* Get the bounding box */
            cached->minx = FT_FLOOR(metrics->horiBearingX);
            cached->maxx = cached->minx + FT_CEIL(metrics->width);
            cached->maxy = FT_FLOOR(metrics->horiBearingY);
            cached->miny = cached->maxy - FT_CEIL(metrics->height);
            cached->yoffset = font->ascent - cached->maxy;
            cached->advance = FT_CEIL(metrics->horiAdvance);
        } else {
            /* Get the bounding box for non-scalable format.
             * Again, freetype2 fills in many of the font metrics
             * with the value of 0, so some of the values we
             * need must be calculated differently with certain
             * assumptions about non-scalable formats.
             * */
            cached->minx = FT_FLOOR(metrics->horiBearingX);
            cached->maxx = cached->minx + FT_CEIL(metrics->horiAdvance);
            cached->maxy = FT_FLOOR(metrics->horiBearingY);
            cached->miny = cached->maxy - FT_CEIL(face->available_sizes[font->font_size_family].height);
            cached->yoffset = 0;
            cached->advance = FT_CEIL(metrics->horiAdvance);
        }
		
        /* Adjust for bold and italic text */
        if( font->style & RENPY_TTF_STYLE_BOLD ) {
            cached->maxx += font->glyph_overhang;
        }
        if( font->style & RENPY_TTF_STYLE_ITALIC ) {
            cached->maxx += (int)ceil(font->glyph_italics);
        }

        /* Adjust sizes for expand. */
        cached->maxx += font->expand;
        cached->maxy += font->expand;
        
        cached->stored |= CACHED_METRICS;
    }

    if ( ((want & CACHED_BITMAP) && !(cached->stored & CACHED_BITMAP)) ||
         ((want & CACHED_PIXMAP) && !(cached->stored & CACHED_PIXMAP)) ) {
        int mono = (want & CACHED_BITMAP);
        int i;
        FT_Bitmap* src;
        FT_Bitmap* dst;

        /* Handle the italic style */
        if( font->style & RENPY_TTF_STYLE_ITALIC ) {
            FT_Matrix shear;

            shear.xx = 1 << 16;
            shear.xy = (int) ( font->glyph_italics * ( 1 << 16 ) ) / font->height;
            shear.yx = 0;
            shear.yy = 1 << 16;

            FT_Outline_Transform( outline, &shear );
       }

        error = FT_Get_Glyph(glyph, &realglyph);
        if (error) {
            return error;
        }
        
        /* Perhaps stroke the glyph. */
        if (font->stroker) {
            /* FT_Glyph_Stroke(&realglyph, font->stroker, 1); */
            FT_Glyph_StrokeBorder(&realglyph, font->stroker, 0, 1);
        }

        
        /* Render the glyph */
        if ( mono ) {
            error = FT_Glyph_To_Bitmap( &realglyph, ft_render_mode_mono, 0, 1);
        } else {
            error = FT_Glyph_To_Bitmap( &realglyph, ft_render_mode_normal, 0, 1);
        }

        if( error ) {
            return error;
        }
        
        /* Copy over information to cache */
        src = &((FT_BitmapGlyph) realglyph)->bitmap;
        if ( mono ) {
            dst = &cached->bitmap;
        } else {
            dst = &cached->pixmap;
        }

        memcpy( dst, src, sizeof( *dst ) );
        
        /* FT_Render_Glyph() and .fon fonts always generate a
         * two-color (black and white) glyphslot surface, even
         * when rendered in ft_render_mode_normal.  This is probably
         * a freetype2 bug because it is inconsistent with the
         * freetype2 documentation under FT_Render_Mode section.
         * */
        if ( mono || !FT_IS_SCALABLE(face) ) {
            dst->pitch *= 8;
        }

        /* Adjust for bold and italic text */
        if( font->style & RENPY_TTF_STYLE_BOLD ) {
            int bump = font->glyph_overhang;
            dst->pitch += bump;
            dst->width += bump;
        }
        if( font->style & RENPY_TTF_STYLE_ITALIC ) {
            int bump = (int)ceil(font->glyph_italics);
            dst->pitch += bump;
            dst->width += bump;
        }

        if (dst->rows != 0) {
            dst->buffer = (unsigned char *)malloc( dst->pitch * dst->rows );
            if( !dst->buffer ) {
                return FT_Err_Out_Of_Memory;
            }
            memset( dst->buffer, 0, dst->pitch * dst->rows );

            for( i = 0; i < src->rows; i++ ) {
                int soffset = i * src->pitch;
                int doffset = i * dst->pitch;
                if ( mono ) {
                    unsigned char *srcp = src->buffer + soffset;
                    unsigned char *dstp = dst->buffer + doffset;
                    int j;
                    for ( j = 0; j < src->width; j += 8 ) {
                        unsigned char ch = *srcp++;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                        ch <<= 1;
                        *dstp++ = (ch&0x80) >> 7;
                    }
                } else if ( !FT_IS_SCALABLE(face) ) {
                    /* This special case wouldn't
                     * be here if the FT_Render_Glyph()
                     * function wasn't buggy when it tried
                     * to render a .fon font with 256
                     * shades of gray.  Instead, it
                     * returns a black and white surface
                     * and we have to translate it back
                     * to a 256 gray shaded surface. 
                     * */
                    unsigned char *srcp = src->buffer + soffset;
                    unsigned char *dstp = dst->buffer + doffset;
                    unsigned char ch;
                    int j, k;
                    for ( j = 0; j < src->width; j += 8) {
                        ch = *srcp++;
                        for (k = 0; k < 8; ++k) {
                            if ((ch&0x80) >> 7) {
                                *dstp++ = NUM_GRAYS - 1;
                            } else {
                                *dstp++ = 0x00;
                            }
                            ch <<= 1;
                        }
                    }
                } else {
                    memcpy(dst->buffer+doffset,
                           src->buffer+soffset, src->pitch);
                }
            }
        }

        /* Handle the bold style */
        if ( font->style & RENPY_TTF_STYLE_BOLD ) {
            int row;
            int col;
            int offset;
            int pixel;
            Uint8* pixmap;

            /* The pixmap is a little hard, we have to add and clamp */
            for( row = dst->rows - 1; row >= 0; --row ) {
                pixmap = (Uint8*) dst->buffer + row * dst->pitch;
                for( offset=1; offset <= font->glyph_overhang; ++offset ) {
                    for( col = dst->width - 1; col > 0; --col ) {
                        pixel = (pixmap[col] + pixmap[col-1]);
                        if( pixel > NUM_GRAYS - 1 ) {
                            pixel = NUM_GRAYS - 1;
                        }
                        pixmap[col] = (Uint8) pixel;
                    }
                }
            }
        }

        /* Free the new glyph. */
        FT_Done_Glyph(realglyph);

        /* Mark that we rendered this format */
        if ( mono ) {
            cached->stored |= CACHED_BITMAP;
        } else {
            cached->stored |= CACHED_PIXMAP;
        }
    }

    /* We're done, mark this glyph cached */
    cached->cached = ch;

    return 0;
}

static FT_Error Find_Glyph( RENPY_TTF_Font* font, Uint16 ch, int want )
{
    int retval = 0;

    if( ch < 256 ) {
        font->current = &font->cache[ch];
    } else {
        if ( font->scratch.cached != ch ) {
            Flush_Glyph( &font->scratch );
        }
        font->current = &font->scratch;
    }
    if ( (font->current->stored & want) != want ) {
        retval = Load_Glyph( font, ch, font->current, want );
    }
    return retval;
}

void RENPY_TTF_CloseFont( RENPY_TTF_Font* font )
{
    Flush_Cache( font );
    if ( font->face ) {
        FT_Done_Face( font->face );
    }
    if ( font->args.stream ) {
        free( font->args.stream );
    }
    if ( font->freesrc ) {
        SDL_RWclose( font->src );
    }
    free( font );
}

static Uint16 *LATIN1_to_UNICODE(Uint16 *unicode, const char *text, int len)
{
    int i;

    for ( i=0; i < len; ++i ) {
        unicode[i] = ((const unsigned char *)text)[i];
    }
    unicode[i] = 0;

    return unicode;
}

static Uint16 *UTF8_to_UNICODE(Uint16 *unicode, const char *utf8, int len)
{
    int i, j;
    Uint16 ch;

    for ( i=0, j=0; i < len; ++i, ++j ) {
        ch = ((const unsigned char *)utf8)[i];
        if ( ch >= 0xF0 ) {
            ch  =  (Uint16)(utf8[i]&0x07) << 18;
            ch |=  (Uint16)(utf8[++i]&0x3F) << 12;
            ch |=  (Uint16)(utf8[++i]&0x3F) << 6;
            ch |=  (Uint16)(utf8[++i]&0x3F);
        } else
            if ( ch >= 0xE0 ) {
                ch  =  (Uint16)(utf8[i]&0x0F) << 12;
                ch |=  (Uint16)(utf8[++i]&0x3F) << 6;
                ch |=  (Uint16)(utf8[++i]&0x3F);
            } else
		if ( ch >= 0xC0 ) {
                    ch  =  (Uint16)(utf8[i]&0x1F) << 6;
                    ch |=  (Uint16)(utf8[++i]&0x3F);
		}
        unicode[j] = ch;
    }
    unicode[j] = 0;

    return unicode;
}

int RENPY_TTF_FontHeight(RENPY_TTF_Font *font)
{
    return(font->height);
}

int RENPY_TTF_FontAscent(RENPY_TTF_Font *font)
{
    return(font->ascent);
}

int RENPY_TTF_FontDescent(RENPY_TTF_Font *font)
{
    return(font->descent);
}

int RENPY_TTF_FontLineSkip(RENPY_TTF_Font *font)
{
    return(font->lineskip);
}

long RENPY_TTF_FontFaces(RENPY_TTF_Font *font)
{
    return(font->face->num_faces);
}

int RENPY_TTF_FontFaceIsFixedWidth(RENPY_TTF_Font *font)
{
    return(FT_IS_FIXED_WIDTH(font->face));
}

char *RENPY_TTF_FontFaceFamilyName(RENPY_TTF_Font *font)
{
    return(font->face->family_name);
}

char *RENPY_TTF_FontFaceStyleName(RENPY_TTF_Font *font)
{
    return(font->face->style_name);
}

int RENPY_TTF_GlyphMetrics(RENPY_TTF_Font *font, Uint16 ch,
                           int* minx, int* maxx, int* miny, int* maxy, int* advance)
{
    FT_Error error;

    error = Find_Glyph(font, ch, CACHED_METRICS);
    if ( error ) {
        RENPY_TTF_SetFTError("Couldn't find glyph", error);
        return -1;
    }

    if ( minx ) {
        *minx = font->current->minx;
    }
    if ( maxx ) {
        *maxx = font->current->maxx;
        if( font->style & RENPY_TTF_STYLE_BOLD ) {
            *maxx += font->glyph_overhang;
        }
    }
    if ( miny ) {
        *miny = font->current->miny;
    }
    if ( maxy ) {
        *maxy = font->current->maxy;
    }
    if ( advance ) {
        *advance = font->current->advance;
        if( font->style & RENPY_TTF_STYLE_BOLD ) {
            *advance += font->glyph_overhang;
        }
    }
    return 0;
}

int RENPY_TTF_SizeText(RENPY_TTF_Font *font, const char *text, int *w, int *h)
{
    Uint16 *unicode_text;
    int unicode_len;
    int status;

    /* Copy the Latin-1 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return -1;
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    LATIN1_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    status = RENPY_TTF_SizeUNICODE(font, unicode_text, w, h);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return status;
}

int RENPY_TTF_SizeUTF8(RENPY_TTF_Font *font, const char *text, int *w, int *h)
{
    Uint16 *unicode_text;
    int unicode_len;
    int status;

    /* Copy the UTF-8 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return -1;
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    UTF8_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    status = RENPY_TTF_SizeUNICODE(font, unicode_text, w, h);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return status;
}

int RENPY_TTF_SizeUNICODE(RENPY_TTF_Font *font, const Uint16 *text, int *w, int *h)
{
    int status;
    const Uint16 *ch;
    int swapped;
    int x, z;
    int minx, maxx;
    int miny, maxy;
    c_glyph *glyph;
    FT_Error error;
    FT_Long use_kerning;
    FT_UInt prev_index = 0;

    /* Initialize everything to 0 */
    if ( ! RENPY_TTF_initialized ) {
        RENPY_TTF_SetError( "Library not initialized" );
        return -1;
    }
    status = 0;
    minx = maxx = 0;
    miny = maxy = 0;
    swapped = RENPY_TTF_byteswapped;

    /* check kerning */
    use_kerning = FT_HAS_KERNING( font->face );

    /* Load each character and sum it's bounding box */
    x= 0;
    for ( ch=text; *ch; ++ch ) {
        Uint16 c = *ch;
        if ( c == UNICODE_BOM_NATIVE ) {
            swapped = 0;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( c == UNICODE_BOM_SWAPPED ) {
            swapped = 1;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( swapped ) {
            c = SDL_Swap16(c);
        }

        error = Find_Glyph(font, c, CACHED_METRICS);
        if ( error ) {
            return -1;
        }
        glyph = font->current;

        /* handle kerning */
        if ( use_kerning && prev_index && glyph->index ) {
            FT_Vector delta; 
            FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); 
            x += delta.x >> 6;
        }

#if 0
        if ( (ch == text) && (glyph->minx < 0) ) {
            /* Fixes the texture wrapping bug when the first letter
             * has a negative minx value or horibearing value.  The entire
             * bounding box must be adjusted to be bigger so the entire
             * letter can fit without any texture corruption or wrapping.
             *
             * Effects: First enlarges bounding box.
             * Second, xstart has to start ahead of its normal spot in the
             * negative direction of the negative minx value.
             * (pushes everything to the right).
             *
             * This will make the memory copy of the glyph bitmap data
             * work out correctly.
             * */
            z -= glyph->minx;
			
        }
#endif
		
        z = x + glyph->minx;
        if ( minx > z ) {
            minx = z;
        }
        if ( font->style & RENPY_TTF_STYLE_BOLD ) {
            x += font->glyph_overhang;
        }
        if ( glyph->advance > glyph->maxx ) {
            z = x + glyph->advance;
        } else {
            z = x + glyph->maxx;
        }
        if ( maxx < z ) {
            maxx = z;
        }
        x += glyph->advance;

        if ( glyph->miny < miny ) {
            miny = glyph->miny;
        }
        if ( glyph->maxy > maxy ) {
            maxy = glyph->maxy;
        }
        prev_index = glyph->index;
    }

    maxx += font->expand;
    
    /* Fill the bounds rectangle */
    if ( w ) {
        *w = (maxx - minx);
    }
    if ( h ) {
#if 0 /* This is correct, but breaks many applications */
        *h = (maxy - miny);
#else
        *h = font->height;
#endif
    }
        
    return status;
}

/* Convert the Latin-1 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderText_Solid(RENPY_TTF_Font *font,
                                        const char *text, SDL_Color fg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the Latin-1 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    LATIN1_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Solid(font, unicode_text, fg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

/* Convert the UTF-8 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderUTF8_Solid(RENPY_TTF_Font *font,
                                        const char *text, SDL_Color fg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the UTF-8 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    UTF8_to_UNICODE(unicode_text, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Solid(font, unicode_text, fg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

SDL_Surface *RENPY_TTF_RenderUNICODE_Solid(RENPY_TTF_Font *font,
                                           const Uint16 *text, SDL_Color fg)
{
    int xstart;
    int width;
    int height;
    SDL_Surface* textbuf;
    SDL_Palette* palette;
    const Uint16* ch;
    Uint8* src;
    Uint8* dst;
    Uint8 *dst_check;
    int swapped;
    int row, col;
    c_glyph *glyph;

    FT_Bitmap *current;
    FT_Error error;
    FT_Long use_kerning;
    FT_UInt prev_index = 0;

    /* Get the dimensions of the text surface */
    if( ( RENPY_TTF_SizeUNICODE(font, text, &width, NULL) < 0 ) || !width ) {
        RENPY_TTF_SetError( "Text has zero width" );
        return NULL;
    }
    height = font->height + font->expand;
    
    /* Create the target surface */
    textbuf = SDL_AllocSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
    if( textbuf == NULL ) {
        return NULL;
    }

    /* Adding bound checking to avoid all kinds of memory corruption errors
       that may occur. */
    dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;

    /* Fill the palette with the foreground color */
    palette = textbuf->format->palette;
    palette->colors[0].r = 255 - fg.r;
    palette->colors[0].g = 255 - fg.g;
    palette->colors[0].b = 255 - fg.b;
    palette->colors[1].r = fg.r;
    palette->colors[1].g = fg.g;
    palette->colors[1].b = fg.b;
    SDL_SetColorKey( textbuf, SDL_SRCCOLORKEY, 0 );

    /* check kerning */
    use_kerning = FT_HAS_KERNING( font->face );
	
    /* Load and render each character */
    xstart = 0;
    swapped = RENPY_TTF_byteswapped;
    for( ch=text; *ch; ++ch ) {
        Uint16 c = *ch;
        if ( c == UNICODE_BOM_NATIVE ) {
            swapped = 0;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( c == UNICODE_BOM_SWAPPED ) {
            swapped = 1;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( swapped ) {
            c = SDL_Swap16(c);
        }

        error = Find_Glyph(font, c, CACHED_METRICS|CACHED_BITMAP);
        if( error ) {
            SDL_FreeSurface( textbuf );
            return NULL;
        }
        glyph = font->current;
        current = &glyph->bitmap;
        /* Ensure the width of the pixmap is correct. On some cases,
         * freetype may report a larger pixmap than possible.*/
        width = current->width;
        if (width > glyph->maxx - glyph->minx) {
            width = glyph->maxx - glyph->minx;
        }
        /* do kerning, if possible AC-Patch */
        if ( use_kerning && prev_index && glyph->index ) {
            FT_Vector delta; 
            FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); 
            xstart += delta.x >> 6;
        }
        /* Compensate for wrap around bug with negative minx's */
        if ( (ch == text) && (glyph->minx < 0) ) {
            xstart -= glyph->minx;
        }
		
        for( row = 0; row < current->rows; ++row ) {
            /* Make sure we don't go either over, or under the
             * limit */
            if ( row+glyph->yoffset < 0 ) {
                continue;
            }
            if ( row+glyph->yoffset >= textbuf->h ) {
                continue;
            }
            dst = (Uint8*) textbuf->pixels +
                (row+glyph->yoffset) * textbuf->pitch +
                xstart + glyph->minx;
            src = current->buffer + row * current->pitch;

            for ( col=width; col>0 && dst < dst_check; --col ) {
                *dst++ |= *src++;
            }
        }

        xstart += glyph->advance;
        if ( font->style & RENPY_TTF_STYLE_BOLD ) {
            xstart += font->glyph_overhang;
        }
        prev_index = glyph->index;
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
        for ( row=font->underline_height; row>0; --row ) {
            /* 1 because 0 is the bg color */
            memset( dst, 1, textbuf->w );
            dst += textbuf->pitch;
        }
    }
    return textbuf;
}

SDL_Surface *RENPY_TTF_RenderGlyph_Solid(RENPY_TTF_Font *font, Uint16 ch, SDL_Color fg)
{
    SDL_Surface *textbuf;
    SDL_Palette *palette;
    Uint8 *src, *dst;
    int row;
    FT_Error error;
    c_glyph *glyph;

    /* Get the glyph itself */
    error = Find_Glyph(font, ch, CACHED_METRICS|CACHED_BITMAP);
    if ( error ) {
        return(NULL);
    }
    glyph = font->current;

    /* Create the target surface */
    textbuf = SDL_CreateRGBSurface( SDL_SWSURFACE,
                                    glyph->bitmap.pitch,
                                    glyph->bitmap.rows,
                                    8, 0, 0, 0, 0 );
    if ( ! textbuf ) {
        return(NULL);
    }

    /* Fill the palette with the foreground color */
    palette = textbuf->format->palette;
    palette->colors[0].r = 255-fg.r;
    palette->colors[0].g = 255-fg.g;
    palette->colors[0].b = 255-fg.b;
    palette->colors[1].r = fg.r;
    palette->colors[1].g = fg.g;
    palette->colors[1].b = fg.b;
    SDL_SetColorKey(textbuf, SDL_SRCCOLORKEY, 0);

    /* Copy the character from the pixmap */
    src = glyph->bitmap.buffer;
    dst = (Uint8*) textbuf->pixels;
    for ( row = 0; row < textbuf->h; ++row ) {
        memcpy( dst, src, glyph->bitmap.pitch );
        src += glyph->bitmap.pitch;
        dst += textbuf->pitch;
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
        for ( row=font->underline_height; row>0; --row ) {
            /* 1 because 0 is the bg color */
            memset( dst, 1, textbuf->w );
            dst += textbuf->pitch;
        }
    }
    return(textbuf);
}


/* Convert the Latin-1 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderText_Shaded(RENPY_TTF_Font *font,
                                         const char *text, SDL_Color fg, SDL_Color bg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the Latin-1 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    LATIN1_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Shaded(font, unicode_text, fg, bg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

/* Convert the UTF-8 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderUTF8_Shaded(RENPY_TTF_Font *font,
                                         const char *text, SDL_Color fg, SDL_Color bg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the UTF-8 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    UTF8_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Shaded(font, unicode_text, fg, bg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

SDL_Surface* RENPY_TTF_RenderUNICODE_Shaded( RENPY_TTF_Font* font,
                                             const Uint16* text,
                                             SDL_Color fg,
                                             SDL_Color bg )
{
    int xstart;
    int width;
    int height;
    SDL_Surface* textbuf;
    SDL_Palette* palette;
    int index;
    int rdiff;
    int gdiff;
    int bdiff;
    const Uint16* ch;
    Uint8* src;
    Uint8* dst;
    Uint8* dst_check;
    int swapped;
    int row, col;
    FT_Bitmap* current;
    c_glyph *glyph;
    FT_Error error;
    FT_Long use_kerning;
    FT_UInt prev_index = 0;

    /* Get the dimensions of the text surface */
    if( ( RENPY_TTF_SizeUNICODE(font, text, &width, NULL) < 0 ) || !width ) {
        RENPY_TTF_SetError("Text has zero width");
        return NULL;
    }
    height = font->height + font->expand;

    /* Create the target surface */
    textbuf = SDL_AllocSurface(SDL_SWSURFACE, width, height, 8, 0, 0, 0, 0);
    if( textbuf == NULL ) {
        return NULL;
    }

    /* Adding bound checking to avoid all kinds of memory corruption errors
       that may occur. */
    dst_check = (Uint8*)textbuf->pixels + textbuf->pitch * textbuf->h;

    /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */
    palette = textbuf->format->palette;
    rdiff = fg.r - bg.r;
    gdiff = fg.g - bg.g;
    bdiff = fg.b - bg.b;

    for( index = 0; index < NUM_GRAYS; ++index ) {
        palette->colors[index].r = bg.r + (index*rdiff) / (NUM_GRAYS-1);
        palette->colors[index].g = bg.g + (index*gdiff) / (NUM_GRAYS-1);
        palette->colors[index].b = bg.b + (index*bdiff) / (NUM_GRAYS-1);
    }

    /* check kerning */
    use_kerning = FT_HAS_KERNING( font->face );
	
    /* Load and render each character */
    xstart = 0;
    swapped = RENPY_TTF_byteswapped;
    for( ch = text; *ch; ++ch ) {
        Uint16 c = *ch;
        if ( c == UNICODE_BOM_NATIVE ) {
            swapped = 0;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( c == UNICODE_BOM_SWAPPED ) {
            swapped = 1;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( swapped ) {
            c = SDL_Swap16(c);
        }

        error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP);
        if( error ) {
            SDL_FreeSurface( textbuf );
            return NULL;
        }
        glyph = font->current;
        /* Ensure the width of the pixmap is correct. On some cases,
         * freetype may report a larger pixmap than possible.*/
        width = glyph->pixmap.width;
        if (width > glyph->maxx - glyph->minx) {
            width = glyph->maxx - glyph->minx;
        }
        /* do kerning, if possible AC-Patch */
        if ( use_kerning && prev_index && glyph->index ) {
            FT_Vector delta; 
            FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); 
            xstart += delta.x >> 6;
        }
        /* Compensate for the wrap around with negative minx's */
        if ( (ch == text) && (glyph->minx < 0) ) {
            xstart -= glyph->minx;
        }
		
        current = &glyph->pixmap;
        for( row = 0; row < current->rows; ++row ) {
            /* Make sure we don't go either over, or under the
             * limit */
            if ( row+glyph->yoffset < 0 ) {
                continue;
            }
            if ( row+glyph->yoffset >= textbuf->h ) {
                continue;
            }
            dst = (Uint8*) textbuf->pixels +
                (row+glyph->yoffset) * textbuf->pitch +
                xstart + glyph->minx;
            src = current->buffer + row * current->pitch;
            for ( col=width; col>0 && dst < dst_check; --col ) {
                *dst++ |= *src++;
            }
        }

        xstart += glyph->advance;
        if( font->style & RENPY_TTF_STYLE_BOLD ) {
            xstart += font->glyph_overhang;
        }
        prev_index = glyph->index;
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
        for ( row=font->underline_height; row>0; --row ) {
            memset( dst, NUM_GRAYS - 1, textbuf->w );
            dst += textbuf->pitch;
        }
    }
    return textbuf;
}

SDL_Surface* RENPY_TTF_RenderGlyph_Shaded( RENPY_TTF_Font* font,
                                           Uint16 ch,
                                           SDL_Color fg,
                                           SDL_Color bg )
{
    SDL_Surface* textbuf;
    SDL_Palette* palette;
    int index;
    int rdiff;
    int gdiff;
    int bdiff;
    Uint8* src;
    Uint8* dst;
    int row;
    FT_Error error;
    c_glyph* glyph;

    /* Get the glyph itself */
    error = Find_Glyph(font, ch, CACHED_METRICS|CACHED_PIXMAP);
    if( error ) {
        return NULL;
    }
    glyph = font->current;

    /* Create the target surface */
    textbuf = SDL_CreateRGBSurface( SDL_SWSURFACE,
                                    glyph->pixmap.width,
                                    glyph->pixmap.rows,
                                    8, 0, 0, 0, 0 );
    if( !textbuf ) {
        return NULL;
    }

    /* Fill the palette with NUM_GRAYS levels of shading from bg to fg */
    palette = textbuf->format->palette;
    rdiff = fg.r - bg.r;
    gdiff = fg.g - bg.g;
    bdiff = fg.b - bg.b;
    for( index = 0; index < NUM_GRAYS; ++index ) {
        palette->colors[index].r = bg.r + (index*rdiff) / (NUM_GRAYS-1);
        palette->colors[index].g = bg.g + (index*gdiff) / (NUM_GRAYS-1);
        palette->colors[index].b = bg.b + (index*bdiff) / (NUM_GRAYS-1);
    }

    /* Copy the character from the pixmap */
    src = glyph->pixmap.buffer;
    dst = (Uint8*) textbuf->pixels;
    for ( row = 0; row < textbuf->h; ++row ) {
        memcpy( dst, src, glyph->pixmap.pitch );
        src += glyph->pixmap.pitch;
        dst += textbuf->pitch;
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint8 *)textbuf->pixels + row * textbuf->pitch;
        for ( row=font->underline_height; row>0; --row ) {
            memset( dst, NUM_GRAYS - 1, textbuf->w );
            dst += textbuf->pitch;
        }
    }
    return textbuf;
}

/* Convert the Latin-1 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderText_Blended(RENPY_TTF_Font *font,
                                          const char *text, SDL_Color fg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the Latin-1 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    LATIN1_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Blended(font, unicode_text, fg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

/* Convert the UTF-8 text to UNICODE and render it
 */
SDL_Surface *RENPY_TTF_RenderUTF8_Blended(RENPY_TTF_Font *font,
                                          const char *text, SDL_Color fg)
{
    SDL_Surface *textbuf;
    Uint16 *unicode_text;
    int unicode_len;

    /* Copy the UTF-8 text to a UNICODE text buffer */
    unicode_len = strlen(text);
    unicode_text = (Uint16 *)ALLOCA((1+unicode_len+1)*(sizeof *unicode_text));
    if ( unicode_text == NULL ) {
        RENPY_TTF_SetError("Out of memory");
        return(NULL);
    }
    *unicode_text = UNICODE_BOM_NATIVE;
    UTF8_to_UNICODE(unicode_text+1, text, unicode_len);

    /* Render the new text */
    textbuf = RENPY_TTF_RenderUNICODE_Blended(font, unicode_text, fg);

    /* Free the text buffer and return */
    FREEA(unicode_text);
    return(textbuf);
}

SDL_Surface *RENPY_TTF_RenderUNICODE_Blended(RENPY_TTF_Font *font,
                                             const Uint16 *text, SDL_Color fg)
{
    int xstart;
    int width, height;
    SDL_Surface *textbuf;
    Uint32 alpha;
    Uint32 pixel;
    const Uint16 *ch;
    Uint8 *src;
    Uint32 *dst;
    Uint32 *dst_check;
    int swapped;
    int row, col;
    c_glyph *glyph;
    FT_Error error;
    FT_Long use_kerning;
    FT_UInt prev_index = 0;

    /* Get the dimensions of the text surface */
    if ( (RENPY_TTF_SizeUNICODE(font, text, &width, NULL) < 0) || !width ) {
        RENPY_TTF_SetError("Text has zero width");
        return(NULL);
    }
    height = font->height + font->expand;

    textbuf = SDL_AllocSurface(SDL_SWSURFACE, width, height, 32,
                               0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
    if ( textbuf == NULL ) {
        return(NULL);
    }

    /* Adding bound checking to avoid all kinds of memory corruption errors
       that may occur. */
    dst_check = (Uint32*)textbuf->pixels + textbuf->pitch/4 * textbuf->h;

    /* check kerning */
    use_kerning = FT_HAS_KERNING( font->face );
	
    /* Load and render each character */
    xstart = 0;
    swapped = RENPY_TTF_byteswapped;
    pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
    SDL_FillRect(textbuf, NULL, pixel);	/* Initialize with fg and 0 alpha */

    for ( ch=text; *ch; ++ch ) {
        Uint16 c = *ch;
        if ( c == UNICODE_BOM_NATIVE ) {
            swapped = 0;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( c == UNICODE_BOM_SWAPPED ) {
            swapped = 1;
            if ( text == ch ) {
                ++text;
            }
            continue;
        }
        if ( swapped ) {
            c = SDL_Swap16(c);
        }
        error = Find_Glyph(font, c, CACHED_METRICS|CACHED_PIXMAP);
        if( error ) {
            SDL_FreeSurface( textbuf );
            return NULL;
        }
        glyph = font->current;
        /* Ensure the width of the pixmap is correct. On some cases,
         * freetype may report a larger pixmap than possible.*/
        width = glyph->pixmap.width;
        if (width > glyph->maxx - glyph->minx) {
            width = glyph->maxx - glyph->minx;
        }
        /* do kerning, if possible AC-Patch */
        if ( use_kerning && prev_index && glyph->index ) {
            FT_Vector delta; 
            FT_Get_Kerning( font->face, prev_index, glyph->index, ft_kerning_default, &delta ); 
            xstart += delta.x >> 6;
        }
		
        /* Compensate for the wrap around bug with negative minx's */
        if ( (ch == text) && (glyph->minx < 0) ) {
            xstart -= glyph->minx;
        }

        for ( row = 0; row < glyph->pixmap.rows; ++row ) {
            /* Make sure we don't go either over, or under the
             * limit */
            if ( row+glyph->yoffset < 0 ) {
                continue;
            }
            if ( row+glyph->yoffset >= textbuf->h ) {
                continue;
            }
            dst = (Uint32*) textbuf->pixels +
                (row+glyph->yoffset) * textbuf->pitch/4 +
                xstart + glyph->minx;

            /* Added code to adjust src pointer for pixmaps to
             * account for pitch.
             * */
            src = (Uint8*) (glyph->pixmap.buffer + glyph->pixmap.pitch * row);
            for ( col = width; col>0 && dst < dst_check; --col) {
                alpha = *src++;
                *dst++ |= pixel | (alpha << 24);
            }
        }

        xstart += glyph->advance;
        if ( font->style & RENPY_TTF_STYLE_BOLD ) {
            xstart += font->glyph_overhang;
        }
        prev_index = glyph->index;
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
        pixel |= 0xFF000000; /* Amask */
        for ( row=font->underline_height; row>0; --row ) {
            for ( col=0; col < textbuf->w; ++col ) {
                dst[col] = pixel;
            }
            dst += textbuf->pitch/4;
        }
    }
    return(textbuf);
}

SDL_Surface *RENPY_TTF_RenderGlyph_Blended(RENPY_TTF_Font *font, Uint16 ch, SDL_Color fg)
{
    SDL_Surface *textbuf;
    Uint32 alpha;
    Uint32 pixel;
    Uint8 *src;
    Uint32 *dst;
    int row, col;
    FT_Error error;
    c_glyph *glyph;

    /* Get the glyph itself */
    error = Find_Glyph(font, ch, CACHED_METRICS|CACHED_PIXMAP);
    if ( error ) {
        return(NULL);
    }
    glyph = font->current;

    textbuf = SDL_CreateRGBSurface(SDL_SWSURFACE,
                                   glyph->pixmap.width, glyph->pixmap.rows, 32,
                                   0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
    if ( ! textbuf ) {
        return(NULL);
    }

    /* Copy the character from the pixmap */
    pixel = (fg.r<<16)|(fg.g<<8)|fg.b;
    SDL_FillRect(textbuf, NULL, pixel);	/* Initialize with fg and 0 alpha */

    for ( row=0; row<textbuf->h; ++row ) {
        /* Changed src to take pitch into account, not just width */
        src = glyph->pixmap.buffer + row * glyph->pixmap.pitch;
        dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
        for ( col=0; col<glyph->pixmap.width; ++col ) {
            alpha = *src++;
            *dst++ = pixel | (alpha << 24);
        }
    }

    /* Handle the underline style */
    if( font->style & RENPY_TTF_STYLE_UNDERLINE ) {
        row = font->ascent - font->underline_offset - 1;
        if ( row >= textbuf->h) {
            row = (textbuf->h-1) - font->underline_height;
        }
        dst = (Uint32 *)textbuf->pixels + row * textbuf->pitch/4;
        pixel |= 0xFF000000; /* Amask */
        for ( row=font->underline_height; row>0; --row ) {
            for ( col=0; col < textbuf->w; ++col ) {
                dst[col] = pixel;
            }
            dst += textbuf->pitch/4;
        }
    }
    return(textbuf);
}

void RENPY_TTF_SetFontStyle( RENPY_TTF_Font* font, int style )
{
    font->style = style;
    Flush_Cache( font );
}

int RENPY_TTF_GetFontStyle( RENPY_TTF_Font* font )
{
    return font->style;
}

void RENPY_TTF_Quit( void )
{
    if ( RENPY_TTF_initialized ) {
        if ( --RENPY_TTF_initialized == 0 ) {
            FT_Done_FreeType( library );
        }
    }
}

int RENPY_TTF_WasInit( void )
{
    return RENPY_TTF_initialized;
}

void RENPY_TTF_SetExpand(RENPY_TTF_Font *font, float expand) {

    if (expand == 0) return;

    font->expand = expand * 2;
    
    FT_Stroker_New(library, &font->stroker);
    FT_Stroker_Set( font->stroker, expand * 64,
                    FT_STROKER_LINECAP_ROUND,
                    FT_STROKER_LINEJOIN_ROUND,
                    0 ); 
}
