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.
597 lines
17 KiB
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(¤t, 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;
|
|
}
|
|
|
|
}
|
|
|
|
}
|