/*
 * KFontInst - KDE Font Installer
 *
 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
 *
 * ----
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file COPYING.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 */

//
// This class contains code inspired/copied/nicked from mkfontscale. Specifically
// the getName(), lookupName(), and the getFoundry() routines...
//
// mkfontscale's (C) notice is:
/*
  Copyright (c) 2002 by Juliusz Chroboczek

  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:

  The above copyright notice and this permission notice shall be included in
  all copies or substantial portions of the Software.

  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  THE SOFTWARE.
*/

#include "FontEngine.h"
#include "Misc.h"
#include "Fc.h"
#include <KDE/KGlobal>
#include <kascii.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <ft2build.h>
#include FT_SFNT_NAMES_H
#include FT_TRUETYPE_IDS_H
#include FT_TRUETYPE_TABLES_H
#include FT_TYPE1_TABLES_H
#include <QtCore/QBuffer>
#include <QtCore/QTextStream>

namespace KFI
{

static unsigned long ftStreamRead(FT_Stream stream, unsigned long offset, unsigned char *buffer, unsigned long count)
{
    QByteArray *in((QByteArray *)stream->descriptor.pointer);

    if((offset+count)<=(unsigned long)in->size())
    {
        memcpy(buffer, &(in->data()[offset]), count);
        return count;
    }

    return 0;
}

static FT_Error openFtFace(FT_Library library, QByteArray &in, FT_Long index, FT_Face *face)
{
    FT_Open_Args args;
    FT_Stream    stream;
    FT_Error     error;

    if(NULL==(stream=(FT_Stream)calloc(1, sizeof(*stream))))
        return FT_Err_Out_Of_Memory;

    stream->descriptor.pointer = &in;
    stream->pathname.pointer   = NULL;
    stream->size               = in.size();
    stream->pos                = 0;

    stream->read  = ftStreamRead;
    args.flags  = FT_OPEN_STREAM;
    args.stream = stream;

    error = FT_Open_Face(library, &args, index, face);

    if (FT_Err_Ok!=error)
        free(stream);
    else
        (*face)->face_flags &= ~FT_FACE_FLAG_EXTERNAL_STREAM;

    return error;
}

// TTF  : 00 01 00 00 00                         5
//      : FFIL                                   4
//      : <65>  FFIL                            69
// TTC  : ttcf                                   4
// OTF  : OTTO                                   4
// Type1: %!PS-AdobeFont-1.                     17
//      : ?? ?? ?? ?? ?? ?? %!PS-AdobeFont-1.   23
//      : %!FontType1-1.                        14
//      : ?? ?? ?? ?? ?? ?? %!FontType1-1.      20
//      : LWFN                                   4
//      : <65> LWFN                             69
// AFM  : StartFontMetrics                      16
// BDF  : STARTFONT 20                          10
// PCF  : 01 fcp                                 4
static const int constHeaderLen=69;

CFontEngine::EType CFontEngine::getType(const char *fileName)
{
    EType type(TYPE_UNKNOWN);
    int   fd=::open(fileName, O_RDONLY);

    if(-1!=fd)
    {
        unsigned char header[constHeaderLen];

        if(constHeaderLen==::read(fd, header, constHeaderLen))
            type=getType(fileName, header);
        ::close(fd);
    }

    return type;
}

CFontEngine::EType CFontEngine::getType(const char *fileName, Strigi::InputStream *in)
{
    Q_ASSERT( in );

    static const int constHeaderLen=69;
    const char * h;
    int          n=in->read(h, constHeaderLen, constHeaderLen);

    in->reset(0);

    return getType(fileName, n==constHeaderLen ? (const unsigned char *)h : NULL);
}

CFontEngine::EType CFontEngine::getType(const char *fileName, const unsigned char *header)
{
    if(header)
    {
        if( (0x00==header[0] && 0x01==header[1] && 0x00==header[2] && 0x00==header[3] && 0x00==header[4]) ||
            ('F'==header[0] && 'F'==header[1] && 'I'==header[2] && 'L'==header[3]) ||
            ('F'==header[65] && 'F'==header[66] && 'I'==header[67] && 'L'==header[68]))
            return TYPE_TTF;

        if('t'==header[0] && 't'==header[1] && 'c'==header[2] && 'f'==header[3])
            return TYPE_TTC;

        if('O'==header[0] && 'T'==header[1] && 'T'==header[2] && 'O'==header[3])
            return TYPE_OTF;

        if(0x01==header[0] && 'f'==header[1] && 'c'==header[2] && 'p'==header[3])
            return TYPE_PCF;

        if(0==memcmp(header, "STARTFONT", 9) && 0x20==header[9])
            return TYPE_BDF;

        if(0==memcmp(header, "%!PS-AdobeFont-1.", 17) ||
           0==memcmp(&header[6], "%!PS-AdobeFont-1.", 17) ||
           0==memcmp(header, "%!FontType1-1.", 14) ||
           0==memcmp(&header[6], "%!FontType1-1.", 14) ||
           ('L'==header[0] && 'W'==header[1] && 'F'==header[2] && 'N'==header[3]) ||
           ('L'==header[65] && 'W'==header[66] && 'F'==header[67] && 'N'==header[68]))
            return TYPE_TYPE1;

        if(0==memcmp(header, "StartFontMetrics", 16))
            return TYPE_AFM;
    }

    // Right mime 'magic' failed, try by extension...
    if(Misc::checkExt(fileName, "ttf"))
        return TYPE_TTF;

    if(Misc::checkExt(fileName, "ttc"))
        return TYPE_TTC;

    if(Misc::checkExt(fileName, "otf"))
        return TYPE_OTF;

    if(Misc::checkExt(fileName, "pfa") || Misc::checkExt(fileName, "pfb"))
        return TYPE_TYPE1;

    //
    // NOTE: Do not accept .gz extension - strigi will decompress for us.
    if(Misc::checkExt(fileName, "pcf"))
        return TYPE_PCF;

    if(Misc::checkExt(fileName, "bdf"))
        return TYPE_BDF;

    if(Misc::checkExt(fileName, "afm"))
        return TYPE_AFM;

    return TYPE_UNKNOWN;
}

QString & CFontEngine::fixFoundry(QString &foundry)
{
    // Try to make foundry similar to that of AFMs...
    if(foundry==QString::fromLatin1("ibm"))
        foundry=QString::fromLatin1("IBM");
    else if(foundry==QString::fromLatin1("urw"))
        foundry=QString::fromLatin1("URW");
    else if(foundry==QString::fromLatin1("itc"))
        foundry=QString::fromLatin1("ITC");
    else if(foundry==QString::fromLatin1("nec"))
        foundry=QString::fromLatin1("NEC");
    else if(foundry==QString::fromLatin1("b&h"))
        foundry=QString::fromLatin1("B&H");
    else
    {
        QChar *ch(foundry.data());
        int   len(foundry.length());
        bool  isSpace(true);

        while(len--)
        {
            if (isSpace)
                *ch=ch->toUpper();

            isSpace=ch->isSpace();
            ++ch;
        }
    }

    return foundry;
}

bool CFontEngine::openFont(EType type, QByteArray &in, const char *fileName, int face)
{
    bool ok=false;

    closeFont();

    itsWeight=FC_WEIGHT_MEDIUM;
    itsWidth=FC_WIDTH_NORMAL;
    itsSpacing=FC_PROPORTIONAL;
    itsItalic=FC_SLANT_ROMAN;
    itsFamily=itsFoundry=itsVersion=QString();

    if(in.size()<=0 && fileName && TYPE_UNKNOWN==type)
        type=getType(fileName); // Read file header to obtain type...

    switch(type)
    {
        default:
            ok=openFontFt(in, fileName, face, TYPE_TYPE1==type);
            break;
#ifndef HAVE_FcFreeTypeQueryFace
        case TYPE_PCF:
            ok=openFontPcf(in);
            break;
        case TYPE_BDF:
            ok=openFontBdf(in);
            break;
#endif
        case TYPE_AFM:
            ok=openFontAfm(in);
        case TYPE_UNKNOWN:
            break;
    }

    return ok;
}

void CFontEngine::closeFont()
{
    if(itsFt.open)
    {
        FT_Done_Face(itsFt.face);
        itsFt.open=false;
    }
}

static int strToWeight(const QString &str)
{
    if(str.isEmpty())
        return FC_WEIGHT_MEDIUM;
    else if(str.contains("Bold", Qt::CaseInsensitive))
        return FC_WEIGHT_BOLD;
    else if(str.contains("Heavy", Qt::CaseInsensitive))
        return FC_WEIGHT_HEAVY;
    else if(str.contains("Black", Qt::CaseInsensitive))
        return FC_WEIGHT_BLACK;
    else if(str.contains("ExtraBold", Qt::CaseInsensitive))
        return FC_WEIGHT_EXTRABOLD;
    else if(str.contains("UltraBold", Qt::CaseInsensitive))
        return FC_WEIGHT_ULTRABOLD;
    else if(str.contains("ExtraLight", Qt::CaseInsensitive))
        return FC_WEIGHT_EXTRALIGHT;
    else if(str.contains("UltraLight", Qt::CaseInsensitive))
        return FC_WEIGHT_ULTRALIGHT;
    else if(str.contains("Light", Qt::CaseInsensitive))
        return FC_WEIGHT_LIGHT;
    else if(str.contains("Medium", Qt::CaseInsensitive) ||
                    str.contains("Normal", Qt::CaseInsensitive) ||
                    str.contains("Roman", Qt::CaseInsensitive))
        return FC_WEIGHT_MEDIUM;
    else if(str.contains("Regular", Qt::CaseInsensitive))
        return FC_WEIGHT_REGULAR;
    else if(str.contains("SemiBold", Qt::CaseInsensitive))
        return FC_WEIGHT_SEMIBOLD;
    else if(str.contains("DemiBold", Qt::CaseInsensitive))
        return FC_WEIGHT_DEMIBOLD;
    else if(str.contains("Thin", Qt::CaseInsensitive))
        return FC_WEIGHT_THIN;
    else if(str.contains("Book", Qt::CaseInsensitive))
        return FC_WEIGHT_NORMAL;
    else if(str.contains("Demi", Qt::CaseInsensitive))
        return FC_WEIGHT_NORMAL;
    else
        return FC_WEIGHT_MEDIUM;
}

static int strToWidth(const QString &str)
{
    if(str.isEmpty())
        return KFI_FC_WIDTH_NORMAL;
    else if(str.contains("UltraCondensed", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_ULTRACONDENSED;
    else if(str.contains("ExtraCondensed", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_EXTRACONDENSED;
    else if(str.contains("SemiCondensed", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_SEMICONDENSED;
    else if(str.contains("Condensed", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_CONDENSED;
    else if(str.contains("SemiExpanded", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_SEMIEXPANDED;
    else if(str.contains("UltraExpanded", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_ULTRAEXPANDED;
    else if(str.contains("ExtraExpanded", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_EXTRAEXPANDED;
    else if(str.contains("Expanded", Qt::CaseInsensitive))
        return KFI_FC_WIDTH_EXPANDED;
    else
        return KFI_FC_WIDTH_NORMAL;
}

static int checkItalic(int it, const QString &full)
{
    return (FC_SLANT_ITALIC==it && (-1!=full.indexOf("Oblique") || -1!=full.indexOf("Slanted")))
           ? FC_SLANT_OBLIQUE
           : it;
}

static const char * getFoundry(const char *notice)
{
    struct Map
    {
        const char *noticeStr,
                   *foundry;
    };

    static const Map map[]=
    {
        { "Bigelow",                            "B&H"},
        { "Adobe",                              "Adobe"},
        { "Bitstream",                          "Bitstream"},
        { "Monotype",                           "Monotype"},
        { "Linotype",                           "Linotype"},
        { "LINOTYPE-HELL",                      "Linotype"},
        { "IBM",                                "IBM"},
        { "URW",                                "URW"},
        { "International Typeface Corporation", "ITC"},
        { "Tiro Typeworks",                     "Tiro"},
        { "XFree86",                            "XFree86"},
        { "Microsoft",                          "Microsoft"},
        { "Omega",                              "Omega"},
        { "Font21",                             "Hwan"},
        { "HanYang System",                     "Hanyang"},
        { NULL,                                 NULL }
    };

    const Map *entry;

    if(notice)
        for(entry=map; NULL!=entry->foundry; entry++)
            if(strstr(notice, entry->noticeStr)!=NULL)
                return entry->foundry;

    return NULL;
}

#ifndef HAVE_FcFreeTypeQueryFace
static bool lookupName(FT_Face face, int nid, int pid, int eid, FT_SfntName *nameReturn)
{
    int n = FT_Get_Sfnt_Name_Count(face);

    if(n>0)
    {
        int         i;
        FT_SfntName name;

        for(i=0; i<n; i++)
            if(0==FT_Get_Sfnt_Name(face, i, &name) && name.name_id == nid && name.platform_id == pid &&
               (eid < 0 || name.encoding_id == eid))
            {
                switch(name.platform_id)
                {
                    case TT_PLATFORM_APPLE_UNICODE:
                    case TT_PLATFORM_MACINTOSH:
                        if(name.language_id != TT_MAC_LANGID_ENGLISH)
                            continue;
                        break;
                    case TT_PLATFORM_MICROSOFT:
                        if(name.language_id != TT_MS_LANGID_ENGLISH_UNITED_STATES &&
                           name.language_id != TT_MS_LANGID_ENGLISH_UNITED_KINGDOM)
                            continue;
                        break;
                    default:
                        continue;
                }

                if(name.string_len > 0)
                {
                    *nameReturn = name;
                    return true;
                }
            }
    }

    return false;
}

static QByteArray getName(FT_Face face, int nid)
{
    FT_SfntName name;
    QByteArray  str;

    if(lookupName(face, nid, TT_PLATFORM_MICROSOFT, TT_MS_ID_UNICODE_CS, &name) ||
       lookupName(face, nid, TT_PLATFORM_APPLE_UNICODE, -1, &name))
        for(unsigned int i=0; i < name.string_len / 2; i++)
            str+=0 == name.string[2*i] ? name.string[(2*i)+1] : '_';
    else if(lookupName(face, nid, TT_PLATFORM_MACINTOSH, TT_MAC_ID_ROMAN, &name)) // Pretend that Apple Roman is ISO 8859-1
        for(unsigned int i=0; i < name.string_len; i++)
            str+=name.string[i];

    return str;
}

static const char * getFoundry(const FT_Face face, TT_OS2 *os2)
{
    struct Map
    {
        const char *vendorId,
                   *foundry;
    };

    static const int constVendLen=4;

    // These are taken from ttmkfdir
    static const Map map[]=
    {
        { "ADBE", "Adobe"},
        { "AGFA", "Agfa"},
        { "ALTS", "Altsys"},
        { "APPL", "Apple"},
        { "ARPH", "Arphic"},
        { "ATEC", "Alltype"},
        { "B&H",  "B&H"},
        { "BITS", "Bitstream"},
        { "CANO", "Cannon"},
        { "DYNA", "DynaLab"},
        { "EPSN", "Epson"},
        { "FJ",   "Fujitsu"},
        { "IBM",  "IBM"},
        { "ITC",  "ITC"},
        { "IMPR", "Impress"},
        { "LARA", "Larabie"},
        { "LEAF", "Interleaf"},
        { "LETR", "letraset"},
        { "LINO", "Linotype"},
        { "MACR", "Macromedia"},
        { "MONO", "Monotype"},
        { "MS",   "Microsoft"},
        { "MT",   "Monotype"},
        { "NEC",  "NEC"},
        { "PARA", "ParaType"},
        { "QMSI", "QMS"},
        { "RICO", "Ricoh"},
        { "URW",  "URW"},
        { "Y&Y" , "Z&Y"},
        { NULL  ,  NULL}
    };

    static char vendor[constVendLen+1];

    vendor[0]='\0';

    if(NULL!=os2 && 0xFFFF!=os2->version)
    {
        const Map *entry;
        char      vend[constVendLen+1];

        strncpy(vendor, (const char *)(os2->achVendID), constVendLen);
        vendor[constVendLen]='\0';

        for(int i=0; i<constVendLen; ++i)
            vend[i]=toupper(vendor[i]);

        for(entry=map; NULL!=entry->vendorId; entry++)
        {
            unsigned int len=strlen(entry->vendorId);

            if(0==memcmp(entry->vendorId, vend, len))
            {
                bool ok=true;

                for(int i=len; i<constVendLen && ok; i++)
                    if(vend[i]!=' ' && entry->vendorId[i]!='\0')
                        ok=false;

                if(ok)
                    return entry->foundry;
            }
        }
    }

    const char *foundry=NULL;

    if(!foundry)
        foundry=getFoundry(getName(face, TT_NAME_ID_TRADEMARK));

    if(!foundry)
        foundry=getFoundry(getName(face, TT_NAME_ID_MANUFACTURER));

    if(!foundry && vendor[0] && !isspace(vendor[0]) && '-'!=vendor[0])  // Some fonts have a totally blank vendor field
    {
       int i;

        // Remove any dashes...
        for(int i=constVendLen-1; i>0; i--)
            if('-'==vendor[i])
                vendor[i]=' ';

        // Strip any trailing whitepace
        for(i=constVendLen-1; i>0; i--)
            if(isspace(vendor[i]))
               vendor[i]='\0';
            else
               break;

        foundry=vendor;
    }

    return foundry ;
}
#endif

bool CFontEngine::openFontFt(QByteArray &in, const char *fileName, int face, bool type1)
{
    bool status=in.size()<=0
            ? FT_New_Face(itsFt.library, fileName, face, &itsFt.face) ? false : true
            : openFtFace(itsFt.library, in, face, &itsFt.face) ? false : true;

    if(status)
        itsFt.open=true;

    if(status)
    {
        PS_FontInfoRec t1info;

        if(type1)
            FT_Get_PS_Font_Info(itsFt.face, &t1info);

#ifdef HAVE_FcFreeTypeQueryFace
        FcPattern *pat=FcFreeTypeQueryFace(itsFt.face, (FcChar8*) fileName, face, NULL);

        itsWeight=FC_WEIGHT_REGULAR;
        itsWidth=KFI_FC_WIDTH_NORMAL;
        itsSpacing=FC_PROPORTIONAL;

        if(pat)
        {
            itsFamily=FC::getFcString(pat, FC_FAMILY, face);
            FcPatternGetInteger(pat, FC_WEIGHT, face, &itsWeight);
#ifndef KFI_FC_NO_WIDTHS
            FcPatternGetInteger(pat, FC_WIDTH, face, &itsWidth);
#endif
            FcPatternGetInteger(pat, FC_SLANT, face, &itsItalic);
            FcPatternGetInteger(pat, FC_SPACING, face, &itsSpacing);
            itsFoundry=FC::getFcString(pat, FC_FOUNDRY, face);

            if(type1)
                itsVersion=t1info.version;
            else
            {
                int version;

                FcPatternGetInteger(pat, FC_FONTVERSION, face, &version);
                if(version>0)
                    itsVersion.setNum(decodeFixed(version));
            }

            FcPatternDestroy(pat);
            fixFoundry(itsFoundry);
        }
        else
            status=false;

#else

        enum ETtfWidth
        {
            TTF_WIDTH_ULTRA_CONDENSED = 1,
            TTF_WIDTH_EXTRA_CONDENSED = 2,
            TTF_WIDTH_CONDENSED       = 3,
            TTF_WIDTH_SEMI_CONDENSED  = 4,
            TTF_WIDTH_NORMAL          = 5,
            TTF_WIDTH_SEMI_EXPANDED   = 6,
            TTF_WIDTH_EXPANDED        = 7,
            TTF_WIDTH_EXTRA_EXPANDED  = 8,
            TTF_WIDTH_ULTRA_EXPANDED  = 9
        };

        QString fullName;

        if(type1)
        {
            fullName=t1info.full_name;
            itsFamily=t1info.family_name;
        }
        else
        {
            fullName=getName(itsFt.face, TT_NAME_ID_FULL_NAME);
            itsFamily=getName(itsFt.face, TT_NAME_ID_FONT_FAMILY);
        }

        if(itsFamily.isEmpty())
            if(fullName.isEmpty())
                itsFamily=fullName=FT_Get_Postscript_Name(itsFt.face);
            else
                itsFamily=fullName;
        else
            if(fullName.isEmpty())
                fullName=itsFamily;

        if(fullName.isEmpty())
            status=false;   // Hmm... couldn't find any of the names!

        if(status)
        {
            if(type1)
            {
                itsWeight=strToWeight(t1info.weight);
                itsItalic=t1info.italic_angle <= -4 || t1info.italic_angle >= 4 ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
                itsWidth=strToWidth(fullName);
                itsItalic=checkItalic(itsItalic, fullName);
                itsSpacing=t1info.is_fixed_pitch ? FC_MONO : FC_PROPORTIONAL;
                itsFoundry=KFI::getFoundry(t1info.notice);
                itsVersion=t1info.version;
            }
            else // TrueType...
            {
                #define WEIGHT_UNKNOWN 0xFFFF
                #define WIDTH_UNKNOWN  0xFFFF

                TT_Postscript *post=(TT_Postscript *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_post);
                TT_OS2        *os2=(TT_OS2 *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_os2);
                TT_Header     *head=(TT_Header *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_head);
                bool          gotItalic=false;

                itsWidth=WIDTH_UNKNOWN;
                itsWeight=WEIGHT_UNKNOWN;

                if(NULL!=os2 && 0xFFFF!=os2->version)
                {
                    if (os2->usWeightClass == 0)
                        ;
                    else if (os2->usWeightClass < 150)
                        itsWeight=FC_WEIGHT_THIN;
                    else if (os2->usWeightClass < 250)
                        itsWeight=FC_WEIGHT_EXTRALIGHT;
                    else if (os2->usWeightClass < 350)
                        itsWeight=FC_WEIGHT_LIGHT;
                    else if (os2->usWeightClass < 450)
                        itsWeight=FC_WEIGHT_REGULAR;
                    else if (os2->usWeightClass < 550)
                        itsWeight=FC_WEIGHT_MEDIUM;
                    else if (os2->usWeightClass < 650)
                        itsWeight=FC_WEIGHT_SEMIBOLD;
                    else if (os2->usWeightClass < 750)
                        itsWeight=FC_WEIGHT_BOLD;
                    else if (os2->usWeightClass < 850)
                        itsWeight=FC_WEIGHT_EXTRABOLD;
                    else if (os2->usWeightClass < 950)
                        itsWeight=FC_WEIGHT_BLACK;

                    switch(os2->usWidthClass)
                    {
                        case TTF_WIDTH_ULTRA_CONDENSED:
                            itsWidth=FC_WIDTH_ULTRACONDENSED;
                            break;
                        case TTF_WIDTH_EXTRA_CONDENSED:
                            itsWidth=FC_WIDTH_EXTRACONDENSED;
                            break;
                        case TTF_WIDTH_CONDENSED:
                            itsWidth=FC_WIDTH_CONDENSED;
                            break;
                        case TTF_WIDTH_SEMI_CONDENSED:
                            itsWidth=FC_WIDTH_SEMICONDENSED;
                            break;
                        case TTF_WIDTH_NORMAL:
                            itsWidth=FC_WIDTH_NORMAL;
                            break;
                        case TTF_WIDTH_SEMI_EXPANDED:
                            itsWidth=FC_WIDTH_SEMIEXPANDED;
                            break;
                        case TTF_WIDTH_EXPANDED:
                            itsWidth=FC_WIDTH_EXPANDED;
                            break;
                        case TTF_WIDTH_EXTRA_EXPANDED:
                            itsWidth=FC_WIDTH_EXTRAEXPANDED;
                            break;
                        case TTF_WIDTH_ULTRA_EXPANDED:
                            itsWidth=FC_WIDTH_ULTRAEXPANDED;
                            break;
                    }

                    itsItalic=os2->fsSelection&(1 << 0) ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
                    gotItalic=true;
                }

                if(WEIGHT_UNKNOWN==itsWeight)
                    itsWeight=itsFt.face->style_flags&FT_STYLE_FLAG_BOLD
                                ? FC_WEIGHT_BOLD
                                : FC_WEIGHT_MEDIUM;

                if(WIDTH_UNKNOWN==itsWidth)
                    itsWidth=FC_WIDTH_NORMAL;

                if(!gotItalic && head!=NULL)
                {
                    gotItalic=true;
                    itsItalic=head->Mac_Style & 2 ? FC_SLANT_ITALIC : FC_SLANT_ROMAN;
                }

                if(head)
                {
                    double version=decodeFixed(head->Font_Revision);

                    if(version>0)
                        itsVersion.setNum(version);
                }

                if(!gotItalic && NULL!=post)
                {
                    gotItalic=true;
                    itsItalic=0.0f==decodeFixed(post->italicAngle) ? FC_SLANT_ROMAN : FC_SLANT_ITALIC;
                }

                itsItalic=checkItalic(itsItalic, fullName);

                if(NULL!=post &&  post->isFixedPitch)
                {
                    TT_HoriHeader *hhea=NULL;

                    if(NULL!=(hhea=(TT_HoriHeader *)FT_Get_Sfnt_Table(itsFt.face, ft_sfnt_hhea)) &&
                    hhea->min_Left_Side_Bearing >= 0 && hhea->xMax_Extent <= hhea->advance_Width_Max)
                        itsSpacing=FC_CHARCELL;
                    else
                        itsSpacing=FC_MONO;
                }
                else
                    itsSpacing=FC_PROPORTIONAL;

                itsFoundry=KFI::getFoundry(itsFt.face, os2);
            }
        }
#endif
    }
    if(!status)
        closeFont();

    return status;
}

bool CFontEngine::openFontAfm(QByteArray &in)
{
    bool        inMetrics=false;
    QString     full;
    QTextStream ds(&in, QIODevice::ReadOnly);

    while(!ds.atEnd())
    {
        QString line(ds.readLine());
        line=line.simplified();

        if(inMetrics)
        {
            if(0==line.indexOf("FullName "))
            {
                full=line.mid(9);
                itsWidth=strToWidth(full);
            }
            else if(0==line.indexOf("FamilyName "))
                itsFamily=line.mid(11);
            else if(0==line.indexOf("Weight "))
                itsWeight=strToWeight(line.mid(7));
            else if(0==line.indexOf("ItalicAngle "))
                itsItalic=0.0f==line.mid(12).toFloat() ? FC_SLANT_ROMAN : FC_SLANT_ITALIC;
            else if(0==line.indexOf("IsFixedPitch "))
                itsSpacing= ( line.mid(13).contains("false", Qt::CaseInsensitive)
                            ? FC_PROPORTIONAL : FC_MONO );
            else if(0==line.indexOf("Notice "))
                itsFoundry=KFI::getFoundry(line.mid(7).toLatin1());
             else if(0==line.indexOf("Version "))
                 itsVersion=line.mid(8);
            else if(0==line.indexOf("StartCharMetrics"))
                break;
        }
        else
            if(0==line.indexOf("StartFontMetrics"))
                inMetrics=true;
    };

    if(itsFamily.isEmpty() && !full.isEmpty())
        itsFamily=full;

    checkItalic(itsItalic, full);

    return !full.isEmpty() && !itsFamily.isEmpty();
}

#ifndef HAVE_FcFreeTypeQueryFace
static int charToItalic(char c)
{
    switch(c)
    {
        case 'i':
        case 'I':
            return FC_SLANT_ITALIC;
        case 'o':
        case 'O':
            return FC_SLANT_OBLIQUE;
        case 'r':
        case 'R':
        default:
            return FC_SLANT_ROMAN;
    }
}

static int charToSpacing(char c)
{
    switch(c)
    {
        case 'm':
        case 'M':
            return FC_MONO;
        case 'c':
        case 'C':
            return FC_CHARCELL;
        default:
        case 'p':
        case 'P':
            return FC_PROPORTIONAL;
    }
}

void CFontEngine::parseXlfdBmp(const QString &xlfd)
{
    enum EXlfd
    {
        XLFD_FOUNDRY=0,
        XLFD_FAMILY,
        XLFD_WEIGHT,
        XLFD_SLANT,
        XLFD_WIDTH,
        XLFD_STYLE,
        XLFD_PIXEL_SIZE,
        XLFD_POINT_SIZE,
        XLFD_RESX,
        XLFD_RESY,
        XLFD_SPACING,
        XLFD_AV_WIDTH,
        XLFD_ENCODING,
        XLFD_END
    };

    int     pos=0,
            oldPos=1;
    int     entry;

    // XLFD:
    //     -foundry-family-weight-slant-width-?-pixelSize-pointSize-resX-resY-spacing-avWidth-csReg-csEnc
    for(entry=XLFD_FOUNDRY; -1!=(pos=xlfd.indexOf('-', pos+1)) && entry<XLFD_END; ++entry)
    {
        switch(entry)
        {
            default:
                break;
            case XLFD_FOUNDRY:
                itsFoundry=xlfd.mid(oldPos, pos-oldPos);
                break;
            case XLFD_FAMILY:
                itsFamily=xlfd.mid(oldPos, pos-oldPos);
                break;
            case XLFD_WEIGHT:
                itsWeight=strToWeight(xlfd.mid(oldPos, pos-oldPos).toLocal8Bit());
                break;
            case XLFD_SLANT:
                if(pos>0)
                    itsItalic=charToItalic(xlfd[pos-1].toLatin1());
                break;
            case XLFD_WIDTH:
                itsWidth=strToWidth(xlfd.mid(oldPos, pos-oldPos));
                break;
            case XLFD_STYLE:
            case XLFD_PIXEL_SIZE:
            case XLFD_POINT_SIZE:
            case XLFD_RESX:
            case XLFD_RESY:
                break;
            case XLFD_SPACING:
                if(pos>0)
                    itsSpacing=charToSpacing(xlfd[pos-1].toLatin1());
                break;
            case XLFD_AV_WIDTH:
            case XLFD_ENCODING:
                break;
        }

        oldPos=pos+1;
    }
}

bool CFontEngine::openFontBdf(QByteArray &in)
{
    QTextStream ds(&in, QIODevice::ReadOnly);

    ds.readLine(); // Skip 1st line.
    while(!ds.atEnd())
    {
        QString line(ds.readLine());

        if(0==line.indexOf("FONT ", Qt::CaseInsensitive))
        {
            parseXlfdBmp(line.mid(5));
            return true;
        }
    }

    return false;
}

static unsigned int readLsb32(QBuffer &in)
{
    unsigned char num[4];

    return 4==in.read((char *)num, 4)
            ? (num[0])+(num[1]<<8)+(num[2]<<16)+(num[3]<<24)
            : 0;
}

static unsigned int read32(QBuffer &in, bool msb)
{
    if(msb)
    {
        unsigned char num[4];

        return 4==in.read((char *)num, 4)
                ? (num[0]<<24)+(num[1]<<16)+(num[2]<<8)+(num[3])
                : 0;
    }

    return readLsb32(in);
}

static const unsigned int constBitmapMaxProps=1024;

bool CFontEngine::openFontPcf(QByteArray &in)
{
    bool               foundXlfd=false;
    const unsigned int contPcfVersion=(('p'<<24)|('c'<<16)|('f'<<8)|1);
    QBuffer            buf;

    buf.setBuffer(&in);
    buf.open(QIODevice::ReadOnly);

    if(contPcfVersion==readLsb32(buf))
    {
        const unsigned int constPropertiesType=1;

        unsigned int numTables=readLsb32(buf),
                     table,
                     type,
                     format,
                     size,
                     offset;

        for(table=0; table<numTables && !buf.atEnd() && !foundXlfd; ++table)
        {
            type=readLsb32(buf);
            format=readLsb32(buf);
            size=readLsb32(buf);
            offset=readLsb32(buf);
            if(constPropertiesType==type)
            {
                if(buf.seek(offset))
                {
                    const unsigned int constFormatMask=0xffffff00;

                    format=readLsb32(buf);
                    if(0==(format&constFormatMask))
                    {
                        const unsigned int constByteMask=0x4;

                        bool         msb=format&constByteMask;
                        unsigned int numProps=read32(buf, msb);

                        if(numProps>0 && numProps<constBitmapMaxProps)
                        {
                            unsigned int strSize,
                                         skip;

                            struct TProp
                            {
                                unsigned int name,
                                             value;
                                bool         isString;
                            } *props=new struct TProp [numProps];

                            if(props)
                            {
                                char           tmp;
                                unsigned short prop;

                                for(prop=0; prop<numProps; ++prop)
                                {
                                    props[prop].name=read32(buf, msb);
                                    buf.read(&tmp, 1);
                                    props[prop].isString=tmp ? true : false;
                                    props[prop].value=read32(buf, msb);
                                }

                                skip=4-((numProps*9)%4);
                                if(skip!=4)
                                    buf.seek(buf.pos()+skip);

                                strSize=read32(buf, msb);

                                if(strSize>0)
                                {
                                    QByteArray str=buf.read(strSize);

                                    if(str.size()==(int)strSize)
                                    {
                                        // Finally we have the data............
                                        const int constMaxStrLen=1024;

                                        char tmp[constMaxStrLen];

                                        for(prop=0; prop<numProps && !foundXlfd; ++prop)
                                            if(kasciistricmp(&(str.data()[props[prop].name]), "FONT")==0)
                                            {
                                                if(props[prop].isString && strlen(&(str.data()[props[prop].value])))
                                                {
                                                    foundXlfd=true;
                                                    strncpy(tmp, &(str.data()[props[prop].value]), constMaxStrLen);
                                                    tmp[constMaxStrLen-1]='\0';
                                                    parseXlfdBmp(tmp);
                                                }
                                                break;
                                            }
                                    }
                                }
                                delete [] props;
                            }
                        }
                    }
                }
                break;   // Forget the other tables...
            }
        }
    }

    return foundXlfd;
}
#endif

CFontEngine::TFtData::TFtData()
                    : open(false)
{
    if(FT_Init_FreeType(&library))
    {
        std::cerr << "ERROR: FreeType2 failed to initialise\n";
        exit(0);
    }
}

CFontEngine::TFtData::~TFtData()
{
    FT_Done_FreeType(library);
}

}
