/*
 *  Licensed to the Apache Software Foundation (ASF) under one or more
 *  contributor license agreements.  See the NOTICE file distributed with
 *  this work for additional information regarding copyright ownership.
 *  The ASF licenses this file to You under the Apache License, Version 2.0
 *  (the "License"); you may not use this file except in compliance with
 *  the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
/**
 * @author Ilya S. Okomin
 *
 */
#include <X11/Xft/Xft.h>
#include <freetype/tttables.h>
#include <freetype/t1tables.h>

#include <jni.h>

#include "FontLibExports.h"

#if (FREETYPE_MAJOR >= 2 && FREETYPE_MINOR >= 2)

#undef HAS_FREETYPE_INTERNAL
#include <freetype/ftsizes.h>
#include <freetype/ftglyph.h>
#ifndef FALSE
#define FALSE (0)
#define TRUE (1)
#endif /* FALSE */

#else /* FREETYPE_MAJOR ... */

#define HAS_FREETYPE_INTERNAL 1
#include <freetype/internal/tttypes.h>

#endif /* FREETYPE_MAJOR ... */

#include <freetype/ftsnames.h>

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

#include<string.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

#include <wchar.h>
#include <math.h>

#include "LinuxNativeFont.h"
#include "exceptions.h"
#include "org_apache_harmony_awt_gl_font_LinuxNativeFont.h"
/* debug code marker - to print debug information change to DEBUG */
#   define NO_DEBUG

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getFontFamiliesNames
 * Signature: ()[Ljava/lang/String;
 * Returns array of Strings that represents list of all font families names
 * available on the system.  
 */
JNIEXPORT jobjectArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getFontFamiliesNames(JNIEnv *env, jclass jobj){

    jobjectArray families;

    XftObjectSet *os = 0;
    XftPattern *xftPattern;
    XftFontSet *fs;
    int j, i;
    int len;
    int numFamilies;
    FcChar8 *family;
    char** famList;
    jclass strClass;
    jstring initStr;


    /* Create pattern */
    xftPattern = XftPatternCreate();

    if (!XftPatternAddBool (xftPattern, XFT_OUTLINE, True)){
        throwNPException(env, "Outline value can't be added to XftPattern");
    }

    /* Just need to add which fields you want to list */
    os = XftObjectSetBuild (XFT_FAMILY, (char *) 0);

    fs = FcFontList (0, xftPattern, os);

    XftObjectSetDestroy (os);

    if(xftPattern){
        XftPatternDestroy(xftPattern);
    }

    if (!fs){
        throwNPException(env, "Font list can't be created");
        return NULL;
    } else {
        numFamilies = fs->nfont;
        famList = (char** )malloc(numFamilies * sizeof(char *));

        for (j = 0; j < numFamilies; j++) {

#ifdef DEBUG
            FcChar8* font = FcNameUnparse (fs->fonts[j]);
            printf ("%s\n", font);
            free (font);
#endif /* DEBUG */

            if (XftPatternGetString (fs->fonts[j], XFT_FAMILY, 0, &family) == XftResultMatch){
        
#ifdef DEBUG
                printf ("       %s", family);
#endif /* DEBUG */

                len = (strlen((char *)family)+1);
                famList[j] = (char*)malloc(sizeof(char) * len);

                strncpy(famList[j], (char *)family, len);
            } else {
                /* 
                 * TODO
                 * We should add special handling for this case
                 */
                famList[j] = 0;
            }
        }
        XftFontSetDestroy (fs);
    }

    strClass = (*env)->FindClass(env, "java/lang/String");
    initStr = (*env)->NewStringUTF(env, "");

    families = (jobjectArray)(*env)->NewObjectArray(env, 
        numFamilies,  
        strClass,
        initStr);

    if (families == NULL){
        for (i = 0;i < numFamilies;i++){
            free(famList[i]);
        }
        free(famList);
        throwNPException(env, "Not enough memory to create families list");
        return NULL;
    }

    for (i = 0;i < numFamilies;i++){
        // number of chars == length of string -1
        (*env)->SetObjectArrayElement(env, families,i,(*env)->NewStringUTF(env, famList[i]));
        free(famList[i]);
    }
    free(famList);

    return families;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    embedFontNative
 * Signature: (Ljava/lang/String;)Z
 *
 * Returns true if the new font was added to the system, false otherwise.
 * Methods checks if the number of system fonts changed after font configuration
 * was rebuilt.
 */
JNIEXPORT jstring JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_embedFontNative(JNIEnv *env, jclass obj, jstring fName){

    int fontAdded = FALSE;
    FcConfig *config;

    config = FcConfigGetCurrent();
    if (!config){
        return 0;
    }

    char *path = (char *)(*env)->GetStringUTFChars(env, fName, 0);
    fontAdded = FcConfigAppFontAddFile(config, (unsigned char*)path);

    unsigned short *familyName = 0;

    if (fontAdded) {
        getFontFamilyName(path, &familyName);
    }    

    (*env)->ReleaseStringUTFChars(env, fName, path);    
    
    jstring res = 0;
    if (fontAdded && familyName) {
        int len = 0;
        for (; familyName[len] != 0; len++);
        res = (*env)->NewString(env, (jchar *)familyName, len);
        free(familyName);
    }

    return res;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    initializeFont
 * Signature: (Lorg/apache/harmony/awt/gl/font/LinuxFont;Ljava/lang/String;IILjava/lang/String;)J
 * Initiailzes native Xft font object from specified parameters and returns 
 * font handle, also sets font type to the font peer parameter. 
 * NullPointerException is thrown if there are errors in native code.
 */
JNIEXPORT jlong JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_initializeFont(JNIEnv *env, jclass jobj, jobject linuxFont, jstring jName, jint jStyle, jint size, jstring jFaceStyle){

    jclass cls;
    jfieldID fid;
    jmethodID mid;
    jboolean iscopy;

    int slant;
    int weight;
    const char* name;

    XftPattern *pattern;
    XftPattern *matchPattern;
    XftResult result;
    Display *dpy;
    int scr;
    FcChar8 *faceStyle = NULL;
    XftFont *xftFnt;
    int font_type = FONT_TYPE_UNDEF;
    double fSize;
    FT_Face face;
    
    /* Initialize part */
    cls = (*env)->GetObjectClass(env, linuxFont);

    fid=(*env)->GetFieldID(env, cls, "display", "J");
    if (fid == 0) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }
    dpy = (Display *)(long)(*env)->GetLongField(env, linuxFont, fid);

    if (dpy == NULL){
        throwNPException(env, "Cannot connect to XServer");
        return (jlong)(IDATA)NULL;
    }

    if (jStyle & FONT_BOLD) {
        // TODO: need to be defined from TextAttributes
        weight = XFT_WEIGHT_BOLD;
#ifdef DEBUG
        printf("Weight is bold");
#endif // DEBUG 
    } else {
        // TODO: need to be defined from TextAttributes
        weight = XFT_WEIGHT_MEDIUM;
#ifdef DEBUG
        printf("Weight is medium");
#endif // DEBUG 
    }

    if (jStyle & FONT_ITALIC){
        slant = XFT_SLANT_ITALIC;
    } else {
        slant = XFT_SLANT_ROMAN;
    }

    fid=(*env)->GetFieldID(env, cls, "screen", "I");
    if (fid == 0) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }
    scr = (*env)->GetIntField(env, linuxFont, fid);

    name = (*env)->GetStringUTFChars(env, jName, &iscopy);
    if (jFaceStyle){
        faceStyle = (FcChar8 *)(*env)->GetStringUTFChars(env, jFaceStyle, &iscopy);
    }
        
    /* Xft part */
    /* Create pattern */
    pattern = XftPatternCreate();

    if (!XftPatternAddString (pattern, XFT_FAMILY, name)){
        if(name){
            (*env)->ReleaseStringUTFChars(env, jName, name);
        }
        if(faceStyle){
            (*env)->ReleaseStringUTFChars(env, jFaceStyle, (char *)faceStyle);
        }
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding family name to XFTPattern structure");
    }

    /* We do not need name any more */
    if(name){
        (*env)->ReleaseStringUTFChars(env, jName, name);
    }

    if (faceStyle && !XftPatternAddString (pattern, XFT_STYLE, faceStyle)){
        if(faceStyle){
            (*env)->ReleaseStringUTFChars(env, jFaceStyle, (char *)faceStyle);
        }
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding style name to XFTPattern structure");
    }

    /* We do not need faceStyle any more */
    if(faceStyle){
        XftPatternDestroy (pattern);
        (*env)->ReleaseStringUTFChars(env, jFaceStyle, (char *)faceStyle);
    }

    if (!XftPatternAddInteger(pattern, XFT_SLANT, slant)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font slant to XFTPattern structure");
    }

    if (!XftPatternAddInteger(pattern, XFT_WEIGHT, weight)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font weight to XFTPattern structure");
    }

    /* We set antialias mode for simple text rendering without antialiasing */
    if (!XftPatternAddBool(pattern, XFT_ANTIALIAS, False)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font antialias set to false to XFTPattern structure");
    }

    /*
     *
     *  To comply with Java specification and results we have  to use DPI value
     *  equals to 96. Actually, it is properly to use resolution Y value instead of DPI value
     *  the correct formula  resolutionY = (XDisplayHeight(dpy, scr)/(XDisplayHeightMM(dpy, scr)/25.4));
     *  hence size = size / (double)resolutionY * 72;
     */

    fSize = (double)size / 96 * 72;

    if (!XftPatternAddDouble (pattern, XFT_SIZE, fSize)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font size to XFTPattern structure");
    }

/*  if (!XftPatternAddBool (pattern, FC_HINTING, True)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font hinting set to false to XFTPattern structure");
    }
*/

    if (!XftPatternAddBool (pattern, XFT_RENDER, True)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font RENDER set to true to XFTPattern structure");
    }


    if (!XftPatternAddBool (pattern, FC_AUTOHINT, True)){
        XftPatternDestroy (pattern);
        throwNPException(env, "Error during adding font autohinting set to false to XFTPattern structure");
    }
        
    matchPattern = XftFontMatch (dpy, scr, pattern, &result);

#ifdef DEBUG
    printf ("Pattern ");
    FcPatternPrint (pattern);
    if (matchPattern)
    {
        printf ("Match ");
        FcPatternPrint (matchPattern);
    }
    else {
        printf ("No Match\n");
        }
#endif /* DEBUG */

    XftPatternDestroy (pattern);

    if (!matchPattern){
        XftPatternDestroy (matchPattern);
        return (jlong)(IDATA)NULL;
    }
    
    xftFnt = XftFontOpenPattern (dpy, matchPattern);

    if (!xftFnt){
        XftPatternDestroy (matchPattern);
        return (jlong)(IDATA)NULL;
    }

    /* defining font type */
    face = XftLockFace(xftFnt);
    if ((face->face_flags & FT_FACE_FLAG_SCALABLE) &&
            !(face->face_flags & FT_FACE_FLAG_FIXED_SIZES) &&
            !(face->face_flags & FT_FACE_FLAG_SFNT)) {
            font_type = FONT_TYPE_T1;
    } else {
        font_type = FONT_TYPE_TT;
    }
    XftUnlockFace(xftFnt);

    if (font_type == FONT_TYPE_UNDEF){
        XftFontClose (dpy, xftFnt);
        return (jlong)(IDATA)NULL;
    }

    /* Set Font type in LinuxFont object (upcall) */
    mid=(*env)->GetMethodID(env, cls, "setFontType", "(I)V");

    if (mid == 0) {
#ifdef DEBUG
        printf("Can't find \"setFontType\" method");
#endif // DEBUG 
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }

    (*env)->CallVoidMethod(env, linuxFont, mid, font_type);

    if((*env)->ExceptionOccurred(env)) {
#ifdef DEBUG
        printf("Error occured when calling \"setFontType\" method");
#endif // DEBUG 
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }

    return (jlong)(IDATA)xftFnt;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    initializeFontFromFP
 * Signature: (Lorg/apache/harmony/awt/gl/font/LinuxFont;Ljava/lang/String;)J
 * Initializes native Xft font object from xlfd string and returns font handle,  
 * also sets font type to the font peer parameter. If font 
 * that is described by the given xlfd doesn't exist onto a system returned value
 * is null. NullPointerException is thrown if there are errors in native code.
 */
JNIEXPORT jlong JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_initializeFontFromFP(JNIEnv *env, jclass jobj, jobject linuxFont, jstring jXLFD, jint size){

    jclass cls;
    jfieldID fid;
    jmethodID mid;
    jboolean iscopy;

    const char* xlfd;
    Display *dpy;
    int scr;
    XftFont *xftFnt;
    int font_type = FONT_TYPE_UNDEF;
    FT_Face face;

    char **fn; 
    int n = 0;
    int buf_size;
    char *buffer;
    
    XftPattern *pattern;
    XftPattern *matchPattern;
    XftResult result;

    // Initialize part
    cls = (*env)->GetObjectClass(env, linuxFont);

    /* get display value */
    fid=(*env)->GetFieldID(env, cls, "display", "J");
    if (fid == 0) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }
    dpy = (Display *)(long)(*env)->GetLongField(env, linuxFont, fid);

    if (dpy == NULL){
        throwNPException(env, "Cannot connect to XServer");
        return (jlong)(IDATA)NULL;
    }
    
    xlfd = (*env)->GetStringUTFChars(env, jXLFD, &iscopy);
    buf_size = (*env)->GetStringLength(env, jXLFD) + 8;
    buffer = (char *)malloc(buf_size);

    snprintf(buffer, buf_size, xlfd, (int)((double)(size*10) / 96 * 72));
    (*env)->ReleaseStringUTFChars(env, jXLFD, xlfd);

    /* get screen value */
    fid=(*env)->GetFieldID(env, cls, "screen", "I");
    if (fid == 0) {
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        free(buffer);
        return (jlong)(IDATA)NULL;
    }
    scr = (*env)->GetIntField(env, linuxFont, fid);

    /* check if there any fonts with specified xlfd structure */
    fn = XListFonts( dpy, buffer, 10, &n );
    if (fn != NULL){
        XFreeFontNames(fn);
    }

    if (n == 0){
        free(buffer);
        return (jlong)(IDATA)NULL;
    }

    /* Xft part */
    /* Create xlfd pattern */
    pattern = XftXlfdParse(buffer, False, True);
    
    /* We set antialias mode for simple text rendering without antialiasing */
    if (!XftPatternAddBool(pattern, XFT_ANTIALIAS, False)){
        throwNPException(env, "Error during adding font antialias set to false to XFTPattern structure");
    }

    if (!XftPatternAddBool (pattern, XFT_RENDER, True)){
        throwNPException(env, "Error during adding font RENDER set to true to XFTPattern structure");
    }


    if (!XftPatternAddBool (pattern, FC_AUTOHINT, True)){
        throwNPException(env, "Error during adding font autohinting set to false to XFTPattern structure");
    }

    
    matchPattern = XftFontMatch (dpy, scr, pattern, &result);

#ifdef DEBUG
    printf ("Pattern ");
    FcPatternPrint (pattern);
    if (matchPattern)
    {
        printf ("Match ");
        FcPatternPrint (matchPattern);
    }
    else {
        printf ("No Match\n");
        }
#endif // DEBUG 

    XftPatternDestroy (pattern);

    if (!matchPattern){
        XftPatternDestroy (matchPattern);
        return (long)NULL;
    }
    
    xftFnt = XftFontOpenPattern (dpy, matchPattern);

    if (!xftFnt){
        XftPatternDestroy (matchPattern);
        return (long)NULL;
    }

    free(buffer);

    if (!xftFnt){
        return (long)NULL;
    }

    face = XftLockFace(xftFnt);

    if ((face->face_flags & FT_FACE_FLAG_SCALABLE) &&
            !(face->face_flags & FT_FACE_FLAG_FIXED_SIZES)){
        if (face->face_flags & FT_FACE_FLAG_SFNT){
            font_type = FONT_TYPE_TT;
        } else {
            font_type = FONT_TYPE_T1;
        }
    }
    XftUnlockFace(xftFnt);
    

    if (font_type == FONT_TYPE_UNDEF){
        XftFontClose (dpy, xftFnt);
        return (jlong)(IDATA)NULL;
    }

    /* Set Font type in LinuxFont object (upcall) */
    mid=(*env)->GetMethodID(env, cls, "setFontType", "(I)V");

    if (mid == 0) {
#ifdef DEBUG
        printf("Can't find \"setFontType\" method");
#endif // DEBUG 
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }
 
    (*env)->CallVoidMethod(env, linuxFont, mid, font_type);

    if((*env)->ExceptionOccurred(env)) {
#ifdef DEBUG
        printf("Error occured when calling \"setFontType\" method");
#endif // DEBUG 
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        return (jlong)(IDATA)NULL;
    }

    return (long)xftFnt;

}


/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getNumGlyphsNative
 * Signature: (J)I
 * Returns number of glyphs in specified XftFont if success.
 */
JNIEXPORT jint JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getNumGlyphsNative(JNIEnv *env, jclass obj, jlong fnt){

    jint numGlyphs = 0;
    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;

    if(!xftFnt){
        return 0;
    }
    face = XftLockFace(xftFnt);

    if(!face)
        return 0;
    numGlyphs = face->num_glyphs;

#ifdef DEBUG
    printf("Num glyphs = %d\n", numGlyphs);
#endif /* DEBUG */

    XftUnlockFace(xftFnt);
    

    return numGlyphs;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    canDisplayCharNative
 * Signature: (JC)Z
 * Returns true, if XftFont object can display specified char.
 */
JNIEXPORT jboolean JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_canDisplayCharNative(JNIEnv *env, jclass obj, jlong fnt, jchar c){

    jboolean canDisplay = 0;

    /* TODO: implement method - or we can use getGlyphCode results to find out,
     * whether we can display char or not.
     */

    return canDisplay;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getFamilyNative
 * Signature: (J)Ljava/lang/String;
 * Returns family name of the XftFont object.
 */
JNIEXPORT jstring JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getFamilyNative(JNIEnv *env, jclass obj, jlong fnt){

    XftFont *xftFnt = (XftFont *)(long)fnt;

    jstring familyName = NULL;
    char* family;

    if (XftPatternGetString (xftFnt->pattern, XFT_FAMILY, 0, &family) != XftResultMatch){
        throwNPException(env, "Can not get font family value");
    }

    familyName = (*env)->NewStringUTF(env, family);

    return familyName;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getFontNameNative
 * Signature: (J)Ljava/lang/String;
 * Returns face name of the XftFont object.
 */
JNIEXPORT jstring JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getFontNameNative(JNIEnv *env, jclass obj, jlong fnt){

    // !! at the moment only EN locale font names used
    jstring faceName = NULL;
    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;
    const int BUF_SIZE = 64;
    char name[BUF_SIZE];

    if(!xftFnt){
        return 0;
    }
    face = XftLockFace(xftFnt);

    if(!face)
        return 0;
    
    snprintf(name, BUF_SIZE, "%s %s", face->family_name, face->style_name);

#ifdef DEBUG
        printf("Face name = %s\n", name);
#endif // DEBUG 

    XftUnlockFace(xftFnt);
  

    faceName = (*env)->NewStringUTF(env, name); 

    return faceName;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getFontPSNameNative
 * Signature: (J)Ljava/lang/String;
 * Returns XftFont's postscript name.
 * Returned value is the name of the font in system default locale or
 * for english langid if there is no results for default locale settings.
 */
JNIEXPORT jstring JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getFontPSNameNative(JNIEnv *env, jclass obj, jlong fnt){

    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;
    const char* name;
    jstring psName;

    if(!xftFnt){
        return 0;
    }
    face = XftLockFace(xftFnt);

    if(!face)
        return 0;
    
    name = FT_Get_Postscript_Name(face);
#ifdef DEBUG
        printf("PostScript name = %s\n", name);
#endif /* DEBUG */


    psName = (*env)->NewStringUTF(env, name);
    
    XftUnlockFace(xftFnt);
    

    return psName;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    pFontFree
 * Signature: (JJ)V
 * Disposes XftFont object.
 */
JNIEXPORT void JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_pFontFree(JNIEnv *env, jclass obj, jlong fnt, jlong display){

    Display *dpy = (Display *)(long)display;
    XftFont *xftFnt = (XftFont *)(long)fnt;
    if(xftFnt){
        XftFontClose (dpy, xftFnt);
    }
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getItalicAngleNative
 * Signature: (JI)F
 * Returns tangent of Italic angle of given Font.
 * Returned value is null and NullPointerException is thrown if there is Xft error.
 */
JNIEXPORT jfloat JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getItalicAngleNative(JNIEnv *env, jclass obj, jlong fnt, int fontType){

    jfloat italicAngle = 0.0;
    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;
#if HAS_FREETYPE_INTERNAL
    TT_Face tt_face;
#endif
    TT_HoriHeader* hh;
    float rise;
    float run;
    PS_FontInfoRec afont_info;
    FT_Error err;
    double pi = 3.1415926535;

    if(!xftFnt){
        return 0;
    }
    face = XftLockFace(xftFnt);

    if(!face){
        return 0;
    }
    if (fontType == FONT_TYPE_TT){
#if HAS_FREETYPE_INTERNAL
            tt_face = (TT_Face)face;
            hh = &(tt_face->horizontal);
#else
            hh = (TT_HoriHeader*)FT_Get_Sfnt_Table(face, ft_sfnt_hhea);
#endif
            rise = (float)hh->caret_Slope_Rise;
            run = (float)hh->caret_Slope_Run;
            italicAngle = run / rise ;
    } else {
        err =  FT_Get_PS_Font_Info( face, &afont_info);
        if (err){
            XftUnlockFace(xftFnt);
            
            return italicAngle;
        }
        italicAngle = tan(((double)afont_info.italic_angle * pi) / 180);
    }

#ifdef DEBUG
    printf("Italic angle value = %f\n", italicAngle);
#endif /* DEBUG */

    XftUnlockFace(xftFnt);
    

    return italicAngle;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getFonts
 * Signature: ()[Ljava/lang/String;
 * Returns an array of available system fonts names.
 * In case of errors NullPointerException is thrown.
 */
JNIEXPORT jobjectArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getFonts(JNIEnv *env, jclass obj){

    jobjectArray fonts;

    XftObjectSet *os = 0;
    XftPattern *xftPattern;
    int len;
        XftFontSet *fs;
    int j, i;
    int numFonts;
    const int BUF_SIZE = 128;
    FcChar8 font[BUF_SIZE];
    FcChar8 *family;
    FcChar8 *style;
    char** fontList;
    char* fstr="%s-%s-%d"; // "family name"-"styleName"-"style"
    int fontStyle;
    int weight;
    int slant;
    jclass strClass;
    jstring initStr;

    /* Create pattern */
    xftPattern = XftPatternCreate();

    if (!XftPatternAddBool (xftPattern, XFT_OUTLINE, True)){
        throwNPException(env, "Outline value can't be added to XftPattern");
    }

    /* Just need to add which fields you want to list */
    os = XftObjectSetBuild (XFT_FAMILY, XFT_STYLE, XFT_SLANT, XFT_WEIGHT, (char *) 0);

    fs = FcFontList (0, xftPattern, os);

    XftObjectSetDestroy (os);

    if(xftPattern){
        XftPatternDestroy(xftPattern);
    }

    if (!fs){
        throwNPException(env, "Font list can't be created");
        return NULL;
    } else  {
        numFonts = fs->nfont;
        fontList = (char** )malloc(numFonts * sizeof(char *));

        for (j = 0; j < numFonts; j++){
            if (XftPatternGetString (fs->fonts[j], XFT_FAMILY, 0, &family) != XftResultMatch){
                throwNPException(env, "Couldn't get font family name");
            }

#ifdef DEBUG
            {
             FcChar8* font = FcNameUnparse (fs->fonts[j]);
             printf ("%s\n", font);
             free(font);
            }
#endif /* DEBUG */

            if (XftPatternGetString (fs->fonts[j], XFT_STYLE, 0, &style) != XftResultMatch) {
                throwNPException(env, "Couldn't get font style name");
            }
            
            if (XftPatternGetInteger (fs->fonts[j], XFT_SLANT, 0, &slant) != XftResultMatch) {
                throwNPException(env, "Couldn't get font slant");
            }

            if (XftPatternGetInteger (fs->fonts[j], XFT_WEIGHT, 0, &weight) != XftResultMatch) {
                throwNPException(env, "Couldn't get font weight");
            }

            if (weight <= XFT_WEIGHT_MEDIUM) {
                fontStyle = FONT_PLAIN;
            } else {
                fontStyle = FONT_BOLD;
            }

            if (slant != XFT_SLANT_ROMAN){
                fontStyle |= FONT_ITALIC;
            }

            len = snprintf((char *)font, BUF_SIZE, fstr, family, style, fontStyle);

            if (len < 0){
                len = BUF_SIZE;
            }

            fontList[j] = (char*)malloc(sizeof(char) * (len+1));
            strncpy(fontList[j], (char *)font, len);
            fontList[j][len] = 0;
        }
        XftFontSetDestroy (fs);
    }


    strClass = (*env)->FindClass(env, "java/lang/String");
    initStr = (*env)->NewStringUTF(env, "");

    fonts = (jobjectArray)(*env)->NewObjectArray(env,
        numFonts,
        strClass,
        initStr);

    if (fonts == NULL){
        for (i = 0;i < numFonts;i++){
            free(fontList[i]);
        }
        free(fontList);

        throwNPException(env, "Not enough memory to create families list");
        return (jobjectArray)NULL;
    }

    for (i = 0;i < numFonts;i++){
        (*env)->SetObjectArrayElement(env, fonts,i,(*env)->NewStringUTF(env, fontList[i])); // number of chars == length of string -1
        free(fontList[i]);
    }
    free(fontList);

    return fonts;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getNativeLineMetrics
 * Signature: (JIZZI)[F
 * Returns array of values of font metrics corresponding to the given XftFont 
 * font object. NullPointerException is thrown in case of errors.
 */
JNIEXPORT jfloatArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getNativeLineMetrics(JNIEnv *env, jclass obj, jlong fnt, jint fontHeight, jboolean isAntialiased, jboolean usesFractionalMetrics, jint fontType){
    jfloatArray metrics;
    jfloat values[17];

    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;
    TT_OS2* os2;
#if HAS_FREETYPE_INTERNAL
    TT_Face tt_face;
#endif
    int units_per_EM;
    FT_Size_Metrics size_metrics;
    FT_Size size;
    float mltpl;
    
    if (!xftFnt){
        return NULL;
    }
#ifdef DEBUG
    printf("XFT Ascent = %d\n", xftFnt->ascent);
    printf("XFT Descent = %d\n", xftFnt->descent);
    printf("XFT Height = %d\n", xftFnt->height);
#endif /* DEBUG */

    face = XftLockFace(xftFnt);

    if(!face){
        return NULL;
    }
    units_per_EM = face->units_per_EM;
    if (units_per_EM == 0){
//          throwNPException(env, "Units per EM value is equals to zero");
        XftUnlockFace(xftFnt);
        
        
        return NULL;
    }
    values[16] = units_per_EM;
    mltpl = (float)fontHeight / units_per_EM;
    values[0] = (float)(face->ascender) * mltpl;    // Ascent value
    values[1] = (float)(face->descender) * mltpl;   // -Descent value
    values[2] = (float)(face->height) * mltpl  - values[0] + values[1]; // External Leading value
    values[3] = (float)(face->underline_thickness) * mltpl;     // Underline size value
    values[4] = (float)(face->underline_position) * mltpl;  // Underline position value

    if (fontType == FONT_TYPE_TT){
#if HAS_FREETYPE_INTERNAL
        tt_face = (TT_Face)face;
        os2 = &(tt_face->os2);
#else
        os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
#endif
        values[5] = (float)(os2->yStrikeoutSize) * mltpl;    // Strikeout size value
        values[6] = (float)(os2->yStrikeoutPosition) * mltpl;    // Strikeout position value
    } else {
        values[5] = values[3];
        // !!Workaround: for Type1 fonts strikethrough position = (-ascent+descent)/2
        values[6] = (-values[0] - values[1])/2;
    }

    values[7] = (float)(face->bbox.xMax - face->bbox.xMin)* mltpl;  // Max char width

    size = face->size;
    size_metrics = size->metrics;
    values[8] = (int)size_metrics.ascender >> 6;    // Ascent value
    values[9] = (int)size_metrics.descender >> 6;   // Descent value
    values[10] = (int)(size_metrics.height >> 6)  - values[8] + values[9]; // External Leading value
    values[11] = (int)values[3];    // Underline size value
    values[12] = (int)values[4];    // Underline position value

    values[13] = (int)values[5];    // Strikeout size value
    values[14] = (int)values[6];    // Strikeout sposition value

    values[15] = (int)values[7];    // Max char width

#ifdef DEBUG
    printf("Ascent = %f\n", values[0]);
    printf("Descent = %f\n", values[1]);
    printf("External Leading = %f\n", values[2]);
    printf("Underline size = %f\n", values[3]);
    printf("Underline position = %f\n", values[4]);
    printf("Strikeout size = %f\n", values[5]);
    printf("Strikeout position = %f\n", values[6]);
    printf("Max char width = %f\n", values[7]);

    printf("Pixel Ascent = %f\n", values[8]);
    printf("Pixel Descent = %f\n", values[9]);
    printf("Pixel External Leading = %f\n", values[10]);
    printf("Pixel Underline size = %f\n", values[11]);
    printf("Pixel Underline position = %f\n", values[12]);
    printf("Pixel Strikeout size = %f\n", values[13]);
    printf("Pixel Strikeout position = %f\n", values[14]);
    printf("Pixel Max char width = %f\n", values[15]);

#endif /* DEBUG */

    XftUnlockFace(xftFnt);
    

    metrics = (*env)->NewFloatArray(env, 17);
    (*env)->SetFloatArrayRegion(env, metrics, 0, 17, values);

    return metrics;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getGlyphInfoNative
 * Signature: (JCI)[F
 * Returns array of glyph metrics values for the specified character
 * null is returned and NullPointerException is thrown in case of FreeType 
 * errors.
 */
JNIEXPORT jfloatArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getGlyphInfoNative(JNIEnv *env, jclass obj, jlong fnt, jchar chr, jint fontHeight){

    jfloatArray results = 0;
    jfloat values[11];

    XftFont *font = (XftFont *)(long)fnt;
    const float CONST_96_DIV_76 = (float)96 / 72;
    float mltpl;
    FT_Face face;
    FT_GlyphSlot glyphslot;
    FT_Glyph_Metrics  metrics;

    FT_Pos  width;         /* glyph width  */
    FT_Pos  height;        /* glyph height */

    FT_Pos  horiBearingX;  /* left side bearing in horizontal layouts */
    FT_Pos  horiBearingY;  /* top side bearing in horizontal layouts  */
    FT_Pos  horiAdvance;   /* advance width for horizontal layout     */

    FT_Pos  vertBearingX;  /* left side bearing in vertical layouts */
    FT_Pos  vertBearingY;  /* top side bearing in vertical layouts  */
    FT_Pos  vertAdvance;   /* advance height for vertical layout    */
    int units_per_EM;
    FT_Error error;

    if (!font){
        return results;
    }
    face = XftLockFace(font);

    if(!face){
        return results;
    }
    units_per_EM = face->units_per_EM;

    error = FT_Load_Char(face, (FT_ULong)chr, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO);

    if(error){
//        throwNPException(env, "FT_Load_char : FreeType error");
        XftUnlockFace(font);
    
        return NULL;
    }
    
    glyphslot = face->glyph;
    metrics = glyphslot->metrics;          

    width      = metrics.width;         /* glyph width  */
    height     = metrics.height;        /* glyph height */

    horiBearingX = metrics.horiBearingX;  /* left side bearing in horizontal layouts */
    horiBearingY = metrics.horiBearingY;  /* top side bearing in horizontal layouts  */
    horiAdvance  = metrics.horiAdvance;   /* advance width for horizontal layout     */

    vertBearingX = metrics.vertBearingX;  /* left side bearing in vertical layouts */
    vertBearingY = metrics.vertBearingY;  /* top side bearing in vertical layouts  */
    vertAdvance  = metrics.vertAdvance;   /* advance height for vertical layout    */

    /* Multyplier to obtain proper values in pixels of the metrics */
    mltpl = CONST_96_DIV_76 / units_per_EM;

#ifdef DEBUG

    printf("\n   glyph metrics char = %d: : \n", chr);
    printf("width = %d : height = %d \n", (int)width >> 6, (int)height >> 6);
    printf("ghoriBearingX = %d : ghoriBearingY = %d \n", horiBearingX >> 6, horiBearingY>> 6);
    printf("gvertBearingX = %d : gvertBearingY = %d \n", vertBearingX>> 6, vertBearingY>> 6);
    printf("ghoriAdvance = %d : gvertAdvance = %d \n", horiAdvance>> 6, vertAdvance>> 6);
#endif /* DEBUG*/

    XftUnlockFace(font);
    
    values[0] = (horiBearingX >> 6) + (horiBearingX & 0x3F)/64; // Glyph Precise Bounds : X
    values[1] = (horiBearingY  >> 6) + (horiBearingY & 0x3F)/64;// Glyph Precise Bounds : Y
    values[2] = (horiAdvance >> 6) + (horiAdvance  & 0x3F)/64; // Precise AdvanceX
    values[3] = 0;//(vertAdvance >> 6) + (vertAdvance  & 0x3F)/64; // Precise AdvanceY ?= Ascent+Descent
    values[4] = (width >> 6) + (width  & 0x3F)/64; // Glyph Precise Bounds : width
    values[5] = (height >> 6) + (height  & 0x3F)/64; // Glyph Precise Bounds : height
    
    results = (*env)->NewFloatArray(env, 6);
    (*env)->SetFloatArrayRegion(env, results, 0, 6, values);

    return results;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getGlyphPxlInfoNative
 * Signature: (JJC)[I
 * Returns array of glyph metrics values in pixels for the specified character
 * null is returned and NullPointerException is thrown in case of FreeType errors.
 */
JNIEXPORT jintArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getGlyphPxlInfoNative(JNIEnv *env, jclass obj, jlong display, jlong fnt, jchar chr){

    Display * dpy = (Display *)(long)display;
    jintArray metricsArr = NULL;
    jint values[6];
    XftFont *font = (XftFont *)(long)fnt;
    XGlyphInfo extents;
    FT_Face face;
    FT_BBox  acbox;
    FT_Glyph        glyph;                                         
    FT_Error error;
    
    if (!font){
        return metricsArr;
    }
    face = XftLockFace(font);

    if (!face){
        return metricsArr;
    }
    error = FT_Load_Char(face, (FT_ULong)chr, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO);

    if(error){
        XftUnlockFace(font);
    
        return metricsArr;
    }

    error = FT_Get_Glyph( face->glyph, &glyph );

    if(error){
        throwNPException(env, "getGlyphPxlInfoNative 1 : FreeType error");
        XftUnlockFace(font);
        
        return metricsArr;
    }

    FT_Glyph_Get_CBox(glyph,
                      2,   //FT_GLYPH_BBOX_PIXELS
                     &acbox);
    FT_Done_Glyph(glyph);
    XftUnlockFace(font);
    

    XftTextExtents16 (dpy,
              font,
              &chr,
              1,
              &extents);
#ifdef DEBUG

    printf("char = %d; y = %d; x = %d; height = %d; width = %d; advX = %d; advY = %d \n",
            chr, extents.y, extents.x, extents.height, extents.width, extents.xOff, extents.yOff);
#endif // DEBUG 

    values[0] = - extents.x ; // Glyph Pixels Bounds : X
    values[1] = extents.y ; // Glyph Pixels Bounds : Y
    values[2] = extents.xOff; // Pixels AdvanceX
    values[3] = extents.yOff; // Pixels AdvanceY ?= Ascent+Descent
    values[4] = acbox.xMax-acbox.xMin;  // Glyph Pixels Bounds : width
    values[5] = acbox.yMax-acbox.yMin; // Glyph Pixels Bounds : height
    
    metricsArr = (*env)->NewIntArray(env, 6);
    (*env)->SetIntArrayRegion(env, metricsArr, 0, 6, values);

    return metricsArr;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getGlyphCodesNative
 * Signature: (JLjava/lang/String;I)[I
 * Returns glyphs code corresponding to the characters in String specified, null 
 * is returned if failure. NullPointerException is thrown in case of Display 
 * is null.
 */
JNIEXPORT jintArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getGlyphCodesNative(JNIEnv *env, jclass obj, jlong fnt, jstring str, jint len){

    jintArray glyphsCodes = NULL;

    // TODO: implement method

    return glyphsCodes;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getGlyphCodeNative
 * Signature: (JCJ)I
 * Returns glyph code corresponding to the specified character, null is 
 * returned if failure. NullPointerException is thrown in case of Display is null.
 */
JNIEXPORT jint JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getGlyphCodeNative(JNIEnv *env, jclass obj, jlong fnt, jchar chr, jlong display){

    jint code = 0xFFFF;
    Display *dpy = (Display *)(long)display;
    XftFont *font = (XftFont *)(long)fnt;

    if (!font){
        return 0xFFFF;
    }
    
    code = XftCharIndex (dpy, font, chr);

    return (code == 0) ? 0xFFFF : code;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    RemoveFontResource
 * Signature: (Ljava/lang/String;)I
 * Description: Re-cache ~/.fonts directory, after temporary font-file is deleted.
 */
JNIEXPORT jint JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_RemoveFontResource(JNIEnv *env, jclass obj, jstring fName){

    FcChar8* dirName;
    jboolean iscopy;
    FcConfig *config;
    FcFontSet *set;
    FcStrSet *subdirs;
    FcStrList *list;
    jboolean result = TRUE;

    /* get current congif */
    config = FcConfigGetCurrent();

    if (!config){
        return FALSE;
    }

    dirName = (FcChar8*)((*env)->GetStringUTFChars(env, fName, &iscopy));

    list = FcConfigGetConfigDirs (config);
    set = FcFontSetCreate ();
    subdirs = FcStrSetCreate ();

    /* scan dir for changes */
    result = result && FcDirScan (set, subdirs, 0, FcConfigGetBlanks (config), dirName, FcFalse);

    /* save changes to the config */
    result = result && FcDirSave (set, subdirs, dirName);

    /* rebuild fonts list */
    FcConfigBuildFonts(config);

    FcFontSetDestroy (set);
    FcStrSetDestroy (subdirs);
    FcStrListDone (list);

    (*env)->ReleaseStringUTFChars(env, fName, (char *)dirName);

    return result;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    NativeInitGlyphBitmap
 * Signature: (JC)J
 * Returns pointer to FreeType FT_Bitmap that represents bitmap
 * of the character specified or 0 if failures in native code.
 */
JNIEXPORT jlong JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_NativeInitGlyphBitmap(JNIEnv *env, jclass jobj,  jlong fnt, jchar chr){

    XftFont *font = (XftFont *)(long)fnt;
    FT_Glyph glyph;
    FT_BitmapGlyph  glyph_bitmap;                                  
    int size;

    FT_Error error;
    FT_Face face;
    FT_Bitmap ft_bitmap;
                                             
    if (!font){
        return 0;
    }
    face = XftLockFace(font);

    if (!face){
        return 0;
    }
    
    error = FT_Load_Char(face, (FT_ULong)chr, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO);
    
    if(error){        
        throwNPException(env, "NativeInitGlyphBitmap : FreeType error");

        XftUnlockFace(font);
    
        return (jlong)(IDATA)NULL;
    }

    error = FT_Get_Glyph( face->glyph, &glyph );

    if(error){
//        throwNPException(env, "NativeInitGlyphBitmap 1 : FreeType error");
        XftUnlockFace(font);
    
        return (jlong)(IDATA)NULL;
    }        
    // convert to a bitmap (default render mode + destroy old)     
    if ( glyph->format != FT_GLYPH_FORMAT_BITMAP ) {                                                              
        error = FT_Glyph_To_Bitmap( &glyph, (FT_LOAD_RENDER | FT_LOAD_TARGET_MONO),  
                                      0, 1 );                          
        if ( error ){
            // glyph unchanged                              
            FT_Done_Glyph( glyph );
            XftUnlockFace(font);
            return (jlong)(IDATA)NULL;
        }
    }                                                              
    
    glyph_bitmap = (FT_BitmapGlyph)(glyph);                          
    ft_bitmap = glyph_bitmap->bitmap;
        
    
    GlyphBitmap *gbmp = (GlyphBitmap *)malloc(sizeof(GlyphBitmap));
    gbmp->left = glyph_bitmap->left;
    gbmp->top = glyph_bitmap->top;
    gbmp->bitmap.rows = ft_bitmap.rows;
    gbmp->bitmap.width = ft_bitmap.width;
    gbmp->bitmap.pitch = ft_bitmap.pitch;
    gbmp->bitmap.buffer = malloc(ft_bitmap.rows * ft_bitmap.pitch);
    size= ft_bitmap.rows * ft_bitmap.pitch;
    memcpy(gbmp->bitmap.buffer, ft_bitmap.buffer, size);

    FT_Done_Glyph(glyph);
    XftUnlockFace(font);
    return (long)gbmp;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    NativeFreeGlyphBitmap
 * Signature: (J)V
 * Disposes GlyphBitmap memory block. 
 */
JNIEXPORT void JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_NativeFreeGlyphBitmap(JNIEnv *env, jclass jobj,  
            jlong bitmap){
    GlyphBitmap *gbmp = (GlyphBitmap *)(long)bitmap;
    free(gbmp->bitmap.buffer);
    free(gbmp);
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    drawStringNative
 * Signature: (JJJJII[CIJ)V
 * Draws text on XftDraw with specified parameters.
 */
JNIEXPORT void 
    JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_drawStringNative
      (JNIEnv *env, jclass jobj,  jlong xftDraw, jlong display,
            jlong colormap, jlong font, jint x, jint y, jcharArray str, jint len, jlong color){

    jboolean iscopy;

    XftDraw *draw = (XftDraw *)(long)xftDraw;
    Display *dpy = (Display *)(long)display;
    Colormap  cmap = (Colormap)colormap;
    XftFont *fnt = (XftFont *)(long)font;
    XftColor xftColor;
    XRenderColor renderColor;
    XftChar16 *string;
    XColor *xcolor = (XColor *)(long)color;

    XftDrawSetSubwindowMode(draw, IncludeInferiors/*mode*/); 

    string = (XftChar16 *)(*env)->GetCharArrayElements(env, str, &iscopy);

    /* Creating XRenderColor structure */
    renderColor.red = xcolor->red;
    renderColor.green = xcolor->green;
    renderColor.blue = xcolor->blue;
    renderColor.alpha = 0xFFFF;

    /* Creating XftColor structure */
    if (XAllocColor (dpy, cmap, xcolor)){
        xftColor.pixel = xcolor->pixel;
    }
    xftColor.color.red = renderColor.red;
    xftColor.color.green = renderColor.green;
    xftColor.color.blue = renderColor.blue;
    xftColor.color.alpha = renderColor.alpha;

    XftDrawString16 (draw,
             &xftColor,
             fnt,
             x,
             y,
             string,
             len);

    (*env)->ReleaseCharArrayElements(env, str, string, iscopy);
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    createXftDrawNative
 * Signature: (JJJ)J
 * Returns XftDraw handle created from specified parameters.
 */
JNIEXPORT jlong JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_createXftDrawNative
  (JNIEnv *env, jclass jobj, jlong display, jlong drawable, jlong visual){

        XftDraw *draw;
    Display *dpy = (Display *)(long)display;
    Drawable drwbl = (Drawable)drawable;

    /* Creating xftDraw structure */
    draw = XftDrawCreate (dpy, drwbl, (Visual *)(long)visual, 0);

    return (long)draw;

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    xftDrawSetSubwindowModeNative
 * Signature: (JI)V
 * Set new subwindow mode to XftDraw object.
 */
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_xftDrawSetSubwindowModeNative
  (JNIEnv *env, jclass jobj, jlong xftDraw, jint mode){

        XftDraw *draw = (XftDraw *)(long)xftDraw;

        XftDrawSetSubwindowMode(draw, mode); 

}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    XftDrawSetClipRectangles
 * Signature: (JIIJI)Z
 * Sets clipping rectangles in Xft drawable to the specified clipping rectangles. 
 */
JNIEXPORT jboolean JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_XftDrawSetClipRectangles
  (JNIEnv *env, jclass jobj, jlong xftDraw, jint xOrigin, jint yOrigin, jlong rects, jint n){

    XftDraw *draw = (XftDraw *)(long)xftDraw;
    XRectangle *xrects = (XRectangle *)(long)rects;

    return XftDrawSetClipRectangles (draw, xOrigin, yOrigin, xrects, n);

}


/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    freeXftDrawNative
 * Signature: (J)V
 * Destroys XftDraw object.
 */
JNIEXPORT void JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_freeXftDrawNative
  (JNIEnv *env, jclass jobj, jlong xftDraw){

    XftDraw *draw = (XftDraw *)(long)xftDraw;

    XftDrawDestroy(draw);
}

/* Initializes FreeType FT_Outline structure with the source FT_Outline data. */
int createOutline(FT_Outline *outline, FT_Outline *srcOutline){
    int size;
    outline->flags = srcOutline->flags | FT_OUTLINE_OWNER;
    outline->n_points = srcOutline->n_points;
    outline->n_contours = srcOutline->n_contours;

    size = srcOutline->n_points * sizeof(FT_Vector);
    outline->points = (FT_Vector *)malloc(size);
    if (!outline->points){
        return 0;
    }
    memcpy(outline->points, srcOutline->points, size);

    size = srcOutline->n_points * sizeof(char);
    outline->tags = (char *)malloc(size);
    if (!outline->tags){
        free(outline->points);
        return 0;
    }
    memcpy(outline->tags, srcOutline->tags, size);

    size = srcOutline->n_contours * sizeof(short);
    outline->contours = (short *)malloc(size);
    if (!outline->contours){
        free(outline->points);
        free(outline->tags);
        return 0;
    }
    memcpy(outline->contours, srcOutline->contours, size);

    return 1;
}

/* Disposes FreeType FT_Outline structure. */
void freeOutline(FT_Outline *outline){
    free(outline->points);
    free(outline->tags);
    free(outline->contours);

    free(outline);
}


/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    getGlyphOutline
 * Signature: (JC)J
 * Returns pointer to the FreeType FT_Outline structure. 
 */
JNIEXPORT jlong JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getGlyphOutline(JNIEnv *env, jclass obj, jlong fnt, jchar chr){

    XftFont *font = (XftFont *)(long)fnt;
    FT_Face face;
    FT_Error error;
    FT_Outline *outline = NULL;
    
    if (!font){
        return 0;
    }
    face = XftLockFace(font);

    if(!face){
        return 0;
    }
    error = FT_Load_Char(face, (FT_ULong)chr, FT_LOAD_RENDER | FT_LOAD_TARGET_MONO);
    
    if(error){
        throwNPException(env, "getGlyphOutline : FreeType error");
        XftUnlockFace(font);
    
        return (jlong)(IDATA)NULL;
    }

    if ((face->glyph->format & ft_glyph_format_outline) != 0){
        outline = (FT_Outline *)malloc(sizeof(FT_Outline));
        if (!createOutline(outline, &face->glyph->outline)){
            free(outline);
        }
    }   
    
    XftUnlockFace(font);
    
    return (long)outline;
}

/*
 * Class:     org_apache_harmony_awt_gl_font_LinuxNativeFont
 * Method:    freeGlyphOutline
 * Signature: (JC)J
 * Disposes FT_Outline memory block.
 */
JNIEXPORT void JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_freeGlyphOutline(JNIEnv *env, jclass obj, jlong j_outline){
    FT_Outline *outline = (FT_Outline *)(long)j_outline;
    
    freeOutline(outline);
}

/*
 *  Returns desierd tag index from the list.
 */
int getTagIndex(Tag* tagList, int count, Tag value){
    int result = -1;
    int i;

    for (i = 0; i < count; i++){
        if (* (DWORD *)tagList[i] == * (DWORD *)value){
            return result = i;
        }
    }
    return result;

}

/*
 * Returns an array of pairs of coordinates [x1, y1, x2, y2...] from 
 * FreeType FT_Vector structure.  
 */
JNIEXPORT jfloatArray JNICALL Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getPointsFromFTVector
    (JNIEnv *env, jclass obj, jlong points, jint size){

    jfloatArray flArray;
    float * fpPoints = (float *)malloc(sizeof(float) * size * 2);
    int i;
    FT_Vector *ft_points = (FT_Vector *)(long)points;
    for(i = 0; i < size; i++){
        fpPoints[i*2] = (float)((int)ft_points[i].x + 32)/64;
        fpPoints[i*2 + 1] = (float)(-(int)ft_points[i].y + 32)/64;
    }

    flArray=(*env)->NewFloatArray(env, size*2);
    (*env)->SetFloatArrayRegion(env, flArray, 0, size*2, fpPoints);
    free(fpPoints);
    return flArray;
}

/* Returns an array of extrametrics of the font. */
JNIEXPORT jfloatArray JNICALL 
    Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getExtraMetricsNative(JNIEnv *env, jclass obj, jlong fnt, jint fontHeight, jint fontType) {

    jfloatArray metrics;
    jfloat values[9];

    XftFont *xftFnt = (XftFont *)(long)fnt;
    FT_Face face;
    TT_OS2* os2;
#if HAS_FREETYPE_INTERNAL
     TT_Face tt_face;
#endif
    int units_per_EM;
    float mltpl;
    
    if (!xftFnt){
        return NULL;
    }

    face = XftLockFace(xftFnt);

    if(!face){
        return NULL;
    }
    units_per_EM = face->units_per_EM;
    if (units_per_EM == 0){
//          throwNPException(env, "Units per EM value is equals to zero");
        XftUnlockFace(xftFnt);
        
        return NULL;
    }
    mltpl = (float)fontHeight / units_per_EM;
    
    if (fontType == FONT_TYPE_TT){
#if HAS_FREETYPE_INTERNAL
        tt_face = (TT_Face)face;
        os2 = &(tt_face->os2);
#else
        os2 = (TT_OS2*)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
#endif

        values[0] = (float)(os2->xAvgCharWidth) * mltpl; // the average width of characters in the font
        values[1] = (float)(os2->ySubscriptXSize) * mltpl; // horizontal size for subscripts 
        values[2] = (float)(os2->ySubscriptYSize) * mltpl; // vertical size for subscripts 
        values[3] = (float)(os2->ySubscriptXOffset) * mltpl; // horizontal offset for subscripts
        values[4] = (float)(os2->ySubscriptYOffset) * mltpl; // vertical offset value for subscripts
        values[5] = (float)(os2->ySuperscriptXSize) * mltpl; // horizontal size for superscripts
        values[6] = (float)(os2->ySuperscriptYSize) * mltpl; // vertical size for superscripts
        values[7] = (float)(os2->ySuperscriptXOffset) * mltpl; // horizontal offset for superscripts 
        values[8] = (float)(os2->ySuperscriptYOffset) * mltpl; // vertical offset for superscripts 
    } else {
        values[0] = 0.0f; // the average width of characters in the font
        values[1] = 0.7f * fontHeight; // horizontal size for subscripts 
        values[2] = 0.65f * fontHeight;; // vertical size for subscripts 
        values[3] = 0.0f; // horizontal offset for subscripts
        values[4] = 0.15f * fontHeight; // vertical offset value for subscripts
        values[5] = 0.7f * fontHeight; // horizontal size for superscripts
        values[6] = 0.65f * fontHeight; // vertical size for superscripts
        values[7] = 0.0f; // horizontal offset for superscripts 
        values[8] = 0.45 * fontHeight; // vertical offset for superscripts 
    }  
    XftUnlockFace(xftFnt);
    
    metrics = (*env)->NewFloatArray(env, 9);
    (*env)->SetFloatArrayRegion(env, metrics, 0, 9, values);

    return metrics;
       
}

/*
 * Getting antialiased font from existing font 
 */
JNIEXPORT jlong JNICALL
	Java_org_apache_harmony_awt_gl_font_LinuxNativeFont_getAntialiasedFont(
		JNIEnv *env, jclass jobj, jlong font, jlong display, jboolean isAntialiasing){
	
    XftFont *fnt = (XftFont *)(long)font;
    Display *dpy = (Display *)(long)display;

    XftResult result;
    XftPattern *mpattern = XftFontMatch(dpy, DefaultScreen(dpy),fnt->pattern,&result); 

    XftPatternDel(mpattern, XFT_ANTIALIAS);
    if (isAntialiasing) {
        if (!XftPatternAddBool(mpattern, XFT_ANTIALIAS, True)) {
            throwNPException(env,
                "Error during adding font antialias set to true to XFTPattern structure");
        }
	}
    else {
        if (!XftPatternAddBool(mpattern, XFT_ANTIALIAS, False)) {
            throwNPException(env,
                "Error during adding font antialias set to false to XFTPattern structure");
        }
    }

    XftFont *aaFnt = XftFontOpenPattern(dpy, mpattern);
    return (long)aaFnt;
}
