You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdebase/kcontrol/tdefontinst/tdefontinst/Fontmap.cpp

597 lines
17 KiB

////////////////////////////////////////////////////////////////////////////////
//
// Namespae : KFI::Fontmap
// Author : Craig Drummond
// Project : K Font Installer
// Creation Date : 06/06/2003
// Version : $Revision$ $Date$
//
////////////////////////////////////////////////////////////////////////////////
//
// 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 "Fontmap.h"
#include "FontEngine.h"
#include "XConfig.h"
#include "FcEngine.h"
#include "KfiConstants.h"
#include <ksavefile.h>
#include <tqtextstream.h>
#include <tqdir.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <tqregexp.h>
#include <fstream>
#include <unistd.h>
using namespace std;
static const char * findSpace(const char *str)
{
while(str && *str!=' ' && *str!='\t')
str++;
return str;
}
static bool parseLine(const char *line, TQString &ps, TQString &fname, bool &isAlias)
{
static const int constMaxLen = 127;
static const int constFileMaxLen = 1023;
//
// Format:
// "/<psname> (<filename>) ; "
// "/<psname> /real ; "
char a[constMaxLen+1],
b[constFileMaxLen+1];
char *slash1=const_cast<char*>(strchr(line, '/')),
*space1=slash1 ? (char*)findSpace(slash1) : NULL, //strchr(slash1, ' ') : NULL,
*ob=slash1 ? strchr(slash1, '(') : NULL,
*cb=ob ? strchr(ob, ')') : NULL,
*slash2=space1 && !ob && !cb ? strchr(space1, '/') : NULL,
*space2=slash2 ? (char*)findSpace(slash2) : NULL, // strchr(slash2, ' ') : NULL,
*semic=cb || space2 ? strchr(cb ? cb : space2, ';') : NULL;
if(semic && space1-slash1<constMaxLen)
{
slash1++;
memcpy(a, slash1, space1-slash1);
a[space1-slash1]='\0';
if(cb && cb-ob<constFileMaxLen) // Then found a file entry...
{
ob++;
memcpy(b, ob, cb-ob);
b[cb-ob]='\0';
ps=a;
fname=b;
isAlias=false;
return true;
}
else if(space2 && space2-slash2<constMaxLen) // Then found an alias...
{
slash2++;
memcpy(b, slash2, space2-slash2);
b[space2-slash2]='\0';
ps=a;
fname=b;
isAlias=true;
return true;
}
}
return false;
}
//
// Returns a PS name from an X family name...
// e.g. "Times New Roman" -> "TimesNewRoman"
static TQString createX11PsName(const TQString &font)
{
TQString newName(font);
unsigned int ch;
bool newWord=true;
newName.replace(TQRegExp("\\-"), "_");
for(ch=0; ch<newName.length(); ++ch)
{
if(newName[ch].isSpace())
newWord=true;
else
{
if(newName[ch]==newName[ch].upper())
{
if(!newWord)
newName[ch]=newName[ch].lower();
}
else
if(newName[ch]==newName[ch].lower())
{
if(newWord)
newName[ch]=newName[ch].upper();
}
newWord=false;
}
}
newName.replace(" ", TQString());
return newName;
}
static const char * getItalicStr(KFI::CFontEngine::EItalic it)
{
switch(it)
{
default:
case KFI::CFontEngine::ITALIC_NONE:
return NULL;
case KFI::CFontEngine::ITALIC_ITALIC:
return "Italic";
case KFI::CFontEngine::ITALIC_OBLIQUE:
return "Oblique";
}
}
//
// Create a full Ps name
static TQString createName(const TQString &family, const TQString &weight, const char *italic)
{
TQString name;
TQTextOStream str(&name);
str << family;
if(!weight.isEmpty() || NULL!=italic)
{
str << '-';
if(!weight.isEmpty())
str << weight;
if(NULL!=italic)
str << italic;
}
return name;
}
static TQString getEntry(TQStringList &list, const TQString &name)
{
TQStringList::Iterator it(list.begin()),
end(list.end());
for( ; it!=end; ++it)
if(0==(*it).find('/'+name+' '))
return *it;
return TQString::null;
}
inline bool isAlias(const TQString &entry)
{
return -1==entry.findRev(TQRegExp(")\\s*;\\s*$"));
}
static void addEntry(TQStringList &list, const TQString &name, const TQString &file, const TQString &fmapDir)
{
TQString existing(getEntry(list, name));
bool insert=true;
if(!existing.isEmpty())
if(isAlias(existing))
list.remove(existing);
else
insert=false;
if(insert)
{
TQString entry;
TQTextOStream str(&entry);
str << '/' << name << " (";
if(0==file.find(fmapDir))
str << file.mid(fmapDir.length());
else
str << file;
str << ") ;";
list.append(entry);
}
}
static void addAliasEntry(TQStringList &list, const TQString &x11Name, const TQString &psName)
{
if(x11Name!=psName)
{
TQString existing(getEntry(list, x11Name));
if(existing.isEmpty())
{
TQString entry;
TQTextOStream str(&entry);
str << '/' << x11Name << " /" << psName << " ;";
list.append(entry);
}
}
}
static TQString locateFile(const char *dir, const char *file, int level=0)
{
if(level<5)
{
TQDir d(dir);
if(d.isReadable())
{
const TQFileInfoList *fList=d.entryInfoList();
if(fList)
{
TQFileInfoListIterator it(*fList);
TQFileInfo *fInfo;
TQString str;
for(; NULL!=(fInfo=it.current()); ++it)
if("."!=fInfo->fileName() && ".."!=fInfo->fileName())
if(fInfo->isDir())
{
if(!(str=locateFile(TQFile::encodeName(fInfo->filePath()+"/"), file, level+1)).isEmpty())
return str;
}
else
if(fInfo->fileName()==file)
return fInfo->filePath();
}
}
}
return TQString();
}
static TQString locateFile(const char *file, const char **dirs)
{
int d;
TQString str;
for(d=0; dirs[d]; ++d)
if(!(str=locateFile(dirs[d], file)).isEmpty())
return str;
return TQString::null;
}
#define FONTMAP "Fontmap"
namespace KFI
{
namespace Fontmap
{
bool create(const TQString &dir, CFontEngine &fe)
{
bool root(Misc::root()),
added=false;
TQString fmapDir(Misc::dirSyntax(root ? KFI_ROOT_CFG_DIR : dir));
CFile old(fmapDir);
TQStringList entries;
int i;
FcPattern *pat = FcPatternCreate();
FcObjectSet *os = FcObjectSetBuild(FC_FILE, FC_SCALABLE, (void*)0);
FcFontSet *fs = FcFontList(0, pat, os);
FcPatternDestroy(pat);
FcObjectSetDestroy(os);
for (i = 0; i<fs->nfont; i++)
{
TQString fName(Misc::fileSyntax(CFcEngine::getFcString(fs->fonts[i], FC_FILE)));
FcBool scalable=FcFalse;
if(!fName.isEmpty() && (root || dir.isEmpty() || 0==fName.find(dir)) &&
FcResultMatch==FcPatternGetBool(fs->fonts[i], FC_SCALABLE, 0, &scalable) && scalable)
{
const TQStringList *existing=old.getEntries(fName);
if(existing && existing->count())
entries+=(*existing);
else
{
int face=0,
numFaces=0;
do
{
if(fe.openFont(fName, face))
{
if(fe.hasPsInfo())
{
if(0==numFaces)
numFaces=fe.getNumFaces(); // Only really for TTC files...
//
// Add real
addEntry(entries, fe.getPsName(), fName, fmapDir);
added=true;
//
// Add fake entries for X11 generated names
switch(fe.getWeight())
{
case CFontEngine::WEIGHT_MEDIUM:
case CFontEngine::WEIGHT_REGULAR:
{
TQString x11Ps(createX11PsName(fe.getFamilyName()));
if(CFontEngine::ITALIC_ITALIC!=fe.getItalic() &&
CFontEngine::ITALIC_OBLIQUE!=fe.getItalic())
addAliasEntry(entries,
createName(x11Ps, "Roman",
getItalicStr(fe.getItalic())),
fe.getPsName());
addAliasEntry(entries,
createName(x11Ps, NULL, getItalicStr(fe.getItalic())),
fe.getPsName());
break;
}
case CFontEngine::WEIGHT_UNKNOWN:
break;
default:
addAliasEntry(entries,
createName(createX11PsName(fe.getFamilyName()),
CFontEngine::weightStr(fe.getWeight()),
getItalicStr(fe.getItalic())),
fe.getPsName());
}
}
fe.closeFont();
}
}
while(++face<numFaces);
}
}
}
bool status=true;
if(added || entries.count()!=old.getLineCount())
{
KSaveFile out(fmapDir+FONTMAP);
TQTextStream *stream=out.textStream();
if(stream)
{
TQStringList::Iterator it;
for(it=entries.begin(); it!=entries.end(); ++it)
*stream << *it << endl;
}
else
status=false;
}
//
// Ensure GS's main Fontmap references our file...
if(root && status)
{
static const char * constGhostscriptDirs[]=
{
"/usr/share/ghostscript/",
"/usr/local/share/ghostscript/",
"/usr/share/gs-esp/",
NULL
};
TQString gsFile=locateFile(FONTMAP, constGhostscriptDirs);
if(!gsFile.isEmpty())
{
const int constMaxLineLen=1024;
const char *constRLF=".runlibfile";
char line[constMaxLineLen];
ifstream in(TQFile::encodeName(gsFile));
if(in)
{
TQCString fmap(TQFile::encodeName(fmapDir+FONTMAP));
int lineNum=0,
kfiLine=-1,
gsLine=-1,
ncLine=-1;
do
{
in.getline(line, constMaxLineLen);
if(in.good())
{
line[constMaxLineLen-1]='\0';
if(strstr(line, fmap.data())!=NULL && strstr(line, constRLF)!=NULL)
kfiLine=lineNum;
else if(strstr(line, FONTMAP".GS")!=NULL && strstr(line, constRLF)!=NULL)
gsLine=lineNum;
if(-1==ncLine && '%'!=line[0])
ncLine=lineNum;
lineNum++;
}
}
while(!in.eof() && (-1==kfiLine || -1==gsLine));
//
// If the file doesn't already say to use our Fontmap file, then tell it to!
// Also, ensure ours is .runlibfile'd before the main GS one - else problems can occur
if(-1==kfiLine || kfiLine>gsLine)
{
in.clear();
in.seekg(0, ios::end);
int size= (streamoff) in.tellg();
in.seekg(0, ios::beg);
char *buffer=new char[size+strlen(fmap)+strlen(constRLF)+5];
if(buffer)
{
bool added=false;
buffer[0]='\0';
lineNum=0;
do
{
in.getline(line, constMaxLineLen);
if(in.good())
{
line[constMaxLineLen-1]='\0';
if(lineNum>=ncLine && !added)
{
strcat(buffer, "(");
strcat(buffer, fmap);
strcat(buffer, ") ");
strcat(buffer, constRLF);
strcat(buffer, "\n");
added=true;
}
if(lineNum!=kfiLine)
{
strcat(buffer, line);
strcat(buffer, "\n");
}
lineNum++;
}
}
while(!in.eof());
in.close();
if(added) // Don't re-write GS's Fontmap unless we've actually added something...
{
KSaveFile out(gsFile);
TQTextStream *stream=out.textStream();
if(stream)
*stream << buffer;
}
delete [] buffer;
}
}
}
}
}
return status;
}
CFile::CFile(const TQString &dir)
: itsDir(dir),
itsLineCount(0)
{
ifstream f(TQFile::encodeName(dir+FONTMAP));
itsEntries.setAutoDelete(true);
if(f)
{
static const int constMaxLine=512;
char line[constMaxLine+1];
TEntry *current=NULL;
while(!f.eof())
{
f.getline(line, constMaxLine);
if(!f.eof())
{
TQString ps,
fname;
bool isAlias;
if(parseLine(line, ps, fname, isAlias))
{
itsLineCount++;
TEntry *entry=getEntry(&current, fname, isAlias);
if(!isAlias && entry && entry->psName.isEmpty())
entry->psName=ps;
if(entry)
entry->entries.append(line);
}
}
}
f.close();
}
}
const TQStringList * CFile::getEntries(const TQString &fname)
{
TEntry *entry=findEntry(0==fname.find(itsDir) ? fname.mid(itsDir.length()) : fname, false);
return entry ? &entry->entries : NULL;
}
CFile::TEntry * CFile::findEntry(const TQString &fname, bool isAlias)
{
TEntry *entry=NULL;
for(entry=itsEntries.first(); entry; entry=itsEntries.next())
if(isAlias ? entry->psName==fname : entry->filename==fname)
break;
return entry;
}
CFile::TEntry * CFile::getEntry(TEntry **current, const TQString &fname, bool isAlias)
{
//
// See if its the current one...
if(*current && (isAlias ? (*current)->psName==fname : (*current)->filename==fname))
return *current;
//
// See if its already known...
TEntry *entry=findEntry(fname, isAlias);
//
// If not found, then create a new entry
if(!entry)
{
entry=new TEntry(fname);
itsEntries.append(entry);
}
*current=entry;
return entry;
}
}
}