////////////////////////////////////////////////////////////////////////////////
//
// Class Name    : KFI::KFileFont
// Author        : Craig Drummond
// Project       : K Font Installer
// Creation Date : 20/03/2003
// Version       : $Revision: 465343 $ $Date: 2005-09-29 15:24:25 +0200 (Do, 29 Sep 2005) $
//
////////////////////////////////////////////////////////////////////////////////
//
// 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; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
//
////////////////////////////////////////////////////////////////////////////////
// (C) Craig Drummond, 2003, 2004
////////////////////////////////////////////////////////////////////////////////

#include "KFileFont.h"
#include "KfiConstants.h"
#include <qfile.h>
#include <qtextstream.h>
#include <kgenericfactory.h>
#include <kio/netaccess.h>

static void addEntry(int face, QString &existing, const QString &add)
{
    if(face>0)
        existing.append(", ");
    existing.append(add);
}

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

#ifndef KFI_FC_NO_WIDTHS
static int strToWidth(const QString &str)
{   
    if(str.isEmpty())
        return FC_WIDTH_NORMAL;
    else if(str.contains("UltraCondensed", false))
        return FC_WIDTH_ULTRACONDENSED;
    else if(str.contains("ExtraCondensed", false))
        return FC_WIDTH_EXTRACONDENSED;
    else if(str.contains("SemiCondensed", false))
        return FC_WIDTH_SEMICONDENSED;
    else if(str.contains("Condensed", false))
        return FC_WIDTH_CONDENSED;
    else if(str.contains("SemiExpanded", false))
        return FC_WIDTH_SEMIEXPANDED;
    else if(str.contains("UltraExpanded", false))
        return FC_WIDTH_ULTRAEXPANDED;
    else if(str.contains("ExtraExpanded", false))
        return FC_WIDTH_EXTRAEXPANDED;
    else if(str.contains("Expanded", false))
        return FC_WIDTH_EXPANDED;
    else
        return FC_WIDTH_NORMAL;
}
#endif

struct FoundryMap
{
    const char     *noticeStr,
                   *foundry;
    unsigned short len;
};

static const FoundryMap map[]=   // These are (mainly) taken from type1inst
{
    { "Bigelow",                            "B&H",         3},
    { "Adobe",                              "Adobe",       5},
    { "Bitstream",                          "Bitstream",   9},
    { "Monotype",                           "Monotype",    8},
    { "Linotype",                           "Linotype",    8},
    { "LINOTYPE-HELL",                      "Linotype",    0},
    { "IBM",                                "IBM",         3},
    { "URW",                                "URW",         3},
    { "International Typeface Corporation", "ITC",         3},
    { "Tiro Typeworks",                     "Tiro",        4},
    { "XFree86",                            "XFree86",     7},
    { "Microsoft",                          "Microsoft",   9},
    { "Omega",                              "Omega",       5},
    { "Font21",                             "Hwan",        4},
    { "HanYang System",                     "Hanyang",     7},
    { "Richard Mitchell",                   "Mitchell",    8},
    { "Doug Miles",                         "Miles",       5},
    { "Hank Gillette",                      "Gillette",    8},
    { "Three Islands Press",                "3ip",         3},
    { "MacroMind",                          "Macromind",   9},
    { "MWSoft",                             "MWSoft",      6},
    { "Digiteyes Multimedia",               "DigitEyes",   9},
    { "ZSoft",                              "ZSoft",       5},
    { "Title Wave",                         "Titlewave",   9},
    { "Southern Software",                  "Southern",    8},
    { "Reasonable Solutions",               "Reasonable", 10},
    { "David Rakowski",                     "Rakowski",    8},
    { "D. Rakowski",                        "Rakowski",    0},
    { "S. G. Moye",                         "Moye",        4},
    { "S.G. Moye",                          "Moye",        0},
    { "Andrew s. Meit",                     "Meit",        4},
    { "A.S.Meit",                           "Meit",        0},
    { "Hershey",                            "Hershey",     7},
    { "FontBank",                           "FontBank",    8},
    { "A. Carr",                            "Carr",        4},
    { "Brendel Informatik",                 "Brendel",     7},
    { "Jonathan Brecher",                   "Brecher",     7},
    { "SoftMaker",                          "Softmaker",   9},
    { "LETRASET",                           "Letraset",    8},
    { "Corel Corp",                         "Corel",       5},
    { "PUBLISHERS PARADISE",                "Paradise",    8},
    { "Publishers Paradise",                "Paradise",    0},
    { "Allied Corporation",                 "Allied",      6},
    { NULL,                                 NULL,          0}
};

static const char * getFoundry(const char *notice)
{
    const FoundryMap *entry;

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

static bool readAfm(const QString &file, QString &full, QString &family, QString &foundry, QString &weight,
#ifndef KFI_FC_NO_WIDTHS
                    QString &width,
#endif
                    QString &spacing, QString &slant)
{
    QFile f(file);
    bool  foundName=false,
          foundFamily=false;
    int   intSpacing=FC_PROPORTIONAL,
#ifndef KFI_FC_NO_WIDTHS
          intWidth=FC_WIDTH_NORMAL,
#endif
          intWeight=FC_WEIGHT_NORMAL,
          intSlant=FC_SLANT_ROMAN,
          intItalic=FC_SLANT_ROMAN;

    if(f.open(IO_ReadOnly))
    {
        QTextStream stream(&f);
        QString     line;
        bool        inMetrics=false;

        while(!stream.atEnd())
        {
            line=stream.readLine();
            line=line.simplifyWhiteSpace();
    
            if(inMetrics)
            {
                if(0==line.find("FullName "))
                {
                    full=line.mid(9);
#ifndef KFI_FC_NO_WIDTHS
                    intWidth=strToWidth(full);
#endif
                    foundName=true;
                }
                else if(0==line.find("FamilyName "))
                {
                    family=line.mid(11);
                    foundFamily=true;
                }
                else if(0==line.find("Weight "))
                    intWeight=strToWeight(line.mid(7));
                else if(0==line.find("ItalicAngle "))
                    intSlant=0.0f==line.mid(12).toFloat() ? FC_SLANT_ROMAN : FC_SLANT_ITALIC;
                else if(0==line.find("IsFixedPitch "))
                    intSpacing=0==line.mid(13).find("false", 0, false) ? FC_PROPORTIONAL : FC_MONO;
                else if(0==line.find("Notice "))  
                    foundry=getFoundry(line.mid(7).latin1());
                else if(0==line.find("StartCharMetrics"))
                    break;
            }
            else
                if(0==line.find("StartFontMetrics"))
                    inMetrics=true;
        };
        f.close();

        if(!foundFamily && foundName)
        {
            family=full;
            foundFamily=true;
        }

        if(foundName && FC_SLANT_ITALIC==intItalic && (-1!=full.find("Oblique") || -1!=full.find("Slanted")))
            intItalic=FC_SLANT_OBLIQUE;
    }

    if(foundName && foundFamily)
    {
        weight=KFI::CFcEngine::weightStr(intWeight, false);
#ifndef KFI_FC_NO_WIDTHS
        width=KFI::CFcEngine::widthStr(intWidth, false);
#endif
        slant=KFI::CFcEngine::slantStr(intSlant, false);
        spacing=KFI::CFcEngine::spacingStr(intSpacing);

        if(foundry.isEmpty())
            foundry=i18n(KFI_UNKNOWN_FOUNDRY);

        return true;
    }
    
    return false;
}   

typedef KGenericFactory<KFI::KFileFontPlugin> KFileFontPluginFactory;
K_EXPORT_COMPONENT_FACTORY(kfile_font, KFileFontPluginFactory("kfontinst"))

namespace KFI
{

KFileFontPlugin::KFileFontPlugin(QObject *parent, const char *name, const QStringList& args)
               : KFilePlugin(parent, name, args)
{
    KGlobal::locale()->insertCatalogue(KFI_CATALOGUE);

    addMimeType("application/x-font-ttf"),
    addMimeType("application/x-font-type1");
    //addMimeType("application/x-font-speedo");
    addMimeType("application/x-font-bdf");
    addMimeType("application/x-font-pcf");
    //addMimeType("application/x-font-snf");
    addMimeType("application/x-font-otf");
    addMimeType("application/x-font-ttc");
    addMimeType("application/x-afm");
}

void KFileFontPlugin::addMimeType(const char *mime)
{
    KFileMimeTypeInfo            *info=addMimeTypeInfo(mime);
    KFileMimeTypeInfo::GroupInfo *group=addGroupInfo(info, "General", i18n("General"));

    addItemInfo(group, "Full", i18n("Full Name"), QVariant::String);
    addItemInfo(group, "Family", i18n("Family"), QVariant::String);
    addItemInfo(group, "Foundry", i18n("Foundry"), QVariant::String);
    addItemInfo(group, "Weight", i18n("Weight"),  QVariant::String);
#ifndef KFI_FC_NO_WIDTHS
    addItemInfo(group, "Width", i18n("Width"), QVariant::String);
#endif
    addItemInfo(group, "Spacing", i18n("Spacing"),  QVariant::String);
    addItemInfo(group, "Slant", i18n("Slant"), QVariant::String);
}

bool KFileFontPlugin::readInfo(KFileMetaInfo& info, uint what)
{
    QString full,
            lastFull,
            family,
            foundry,
            weight,
#ifndef KFI_FC_NO_WIDTHS
            width,
#endif
            spacing,
            slant,
            fullAll,
            familyAll,
            foundryAll,
            weightAll,
#ifndef KFI_FC_NO_WIDTHS
            widthAll,
#endif
            spacingAll,
            slantAll;
    KURL    url(info.url());
    QString fName;
    bool    fontsProt  = KFI_KIO_FONTS_PROTOCOL == url.protocol(),
            fileProt   = "file"             == url.protocol(),
            downloaded = false,
            status     = false;

    what=0;

    if(!fontsProt && !fileProt && KIO::NetAccess::download(url, fName, NULL))
    {
        downloaded=true;
        url=KURL(fName);
    }

    if(downloaded || fontsProt || fileProt)
    {
        if("application/x-afm"==info.mimeType())  // Then fontconfig can't give us the data :-(
            status=readAfm(url.path(), fullAll, familyAll, foundryAll, weightAll,
#ifndef KFI_FC_NO_WIDTHS
                           widthAll,
#endif
                           spacingAll, slantAll);
        else
            for(int face=0; face<10; ++face)  // How to get num faces from fontconfig? don't know - so just try 1st 10...
            {
                if(itsEngine.getInfo(url, face, full, family, foundry, weight,
#ifndef KFI_FC_NO_WIDTHS
                                     width,
#endif
                                     spacing, slant) &&
                   !full.isEmpty() && full!=lastFull)
                {
                    addEntry(face, fullAll, full);
                    lastFull=full;

                    if(KFileMetaInfo::Fastest!=what)
                    {
                        addEntry(face, familyAll, family);
                        if(0==face)
                        {
                            foundryAll=foundry;

                            if(foundryAll.isEmpty())
                                foundryAll=i18n(KFI_UNKNOWN_FOUNDRY);
                            else
                            {
                                // Try to make sure foundry is capitalised, and looks the same as that of
                                // any AFM.
                                foundryAll[0]=foundryAll[0].upper();

                                const FoundryMap *entry;

                                for(entry=map; NULL!=entry->foundry; entry++)
                                    if(foundryAll.length()==entry->len && foundryAll.contains(entry->foundry, false))
                                    {
                                        foundryAll=entry->foundry;
                                        break;
                                    }
                            }
                        }
                        addEntry(face, weightAll, weight);
#ifndef KFI_FC_NO_WIDTHS
                        addEntry(face, widthAll, width);
#endif
                        addEntry(face, spacingAll, spacing);
                        addEntry(face, slantAll, slant);
                    }
                    status=true;
                }
                else
                    break;
            }

        if(status)
        {
            KFileMetaInfoGroup group;

            group=appendGroup(info, "General");
            appendItem(group, "Full", fullAll);

            if(KFileMetaInfo::Fastest!=what)
            {
                appendItem(group, "Family", familyAll);
                appendItem(group, "Foundry", foundryAll);
                appendItem(group, "Weight", weightAll);
#ifndef KFI_FC_NO_WIDTHS
                appendItem(group, "Width", widthAll);
#endif
                appendItem(group, "Spacing", spacingAll);
                appendItem(group, "Slant", slantAll);
            }
        }

        if(downloaded)
            KIO::NetAccess::removeTempFile(fName);
    }

    return status;
}

}
