2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; see the file COPYING. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
24 #include "DisabledFonts.h"
27 #include "KfiConstants.h"
28 #include <QtCore/QDir>
29 #include <QtXml/QDomDocument>
30 #include <QtXml/QDomElement>
31 #include <QtXml/QDomNode>
32 #include <QtCore/QFile>
33 #include <QtCore/QTextStream>
34 #include <QtCore/QTemporaryFile>
35 #include <KDE/KLocale>
36 #include <KDE/KStandardDirs>
37 #include <fontconfig/fontconfig.h>
40 #include <sys/types.h>
49 #define FILE_NAME "disabledfonts"
50 #define DISABLED_DOC "disabledfonts"
51 #define FONT_TAG "font"
52 #define FILE_TAG "file"
53 #define PATH_ATTR "path"
54 #define FOUNDRY_ATTR "foundry"
55 #define FAMILY_ATTR "family"
56 #define WEIGHT_ATTR "weight"
57 #define WIDTH_ATTR "width"
58 #define SLANT_ATTR "slant"
59 #define FACE_ATTR "face"
60 #define LANGS_ATTR "langs"
63 // Simple lock file class...
68 CLockFile(const QString
&fileName
)
69 : itsName(QFile::encodeName(fileName
+".lock")),
79 ::unlink(itsName
.constData());
87 itsFd
=open(itsName
.constData(), O_WRONLY
|O_CREAT
|O_EXCL
, 0644);
89 if(itsFd
<0 && EEXIST
==errno
)
91 // Open file read only to ascertain pid of process that currently is locking the file...
92 int fd(open(itsName
.constData(), O_RDONLY
));
96 read(fd
, &pid
, sizeof(int));
99 if(0==kill(pid
, 0)) // Process is still alive, give it 5 seconds to release the lock...
101 else // Process is not alive, remove file...
102 ::unlink(itsName
.constData());
104 // else could not open file - perhaps it was removed in between the 2 open calls?
106 // Try to lock again...
107 itsFd
=open(itsName
.constData(), O_WRONLY
|O_CREAT
|O_EXCL
, 0644);
114 write(itsFd
, &pid
, sizeof(int));
124 static QString
changeName(const QString
&f
, bool enable
)
126 QString
file(Misc::getFile(f
)),
129 if(enable
&& Misc::isHidden(file
))
130 dest
=Misc::getDir(f
)+file
.mid(1);
131 else if (!enable
&& !Misc::isHidden(file
))
132 dest
=Misc::getDir(f
)+QChar('.')+file
;
138 static bool changeFileStatus(const QString
&f
, bool enable
)
140 QString
dest(changeName(f
, enable
));
142 if(dest
==f
) // File is already enabled/disabled
145 if(Misc::fExists(dest
) && !Misc::fExists(f
)) // File is already enabled/disabled
148 if(0==::rename(QFile::encodeName(f
).data(), QFile::encodeName(dest
).data()))
152 Misc::getAssociatedFiles(f
, files
);
156 QStringList::const_iterator fIt
,
157 fEnd
=files
.constEnd();
159 for(fIt
=files
.constBegin(); fIt
!=fEnd
; ++fIt
)
160 ::rename(QFile::encodeName(*fIt
).data(),
161 QFile::encodeName(changeName(*fIt
, enable
)).data());
168 static bool changeStatus(const CDisabledFonts::TFileList
&files
, bool enable
)
171 CDisabledFonts::TFileList::ConstIterator
it(files
.begin()),
175 if(changeFileStatus((*it
).path
, enable
))
176 mods
.append((*it
).path
);
180 if(mods
.count()!=files
.count())
183 // Failed to enable/disable a file - so need to revert any
184 // previous changes...
185 QStringList::ConstIterator
sit(mods
.begin()),
187 for(; sit
!=send
; ++sit
)
188 changeFileStatus(*sit
, !enable
);
194 CDisabledFonts::LangWritingSystemMap
CDisabledFonts::theirLanguageForWritingSystem
[]=
196 { QFontDatabase::Latin
, (const FcChar8
*)"en" },
197 { QFontDatabase::Greek
, (const FcChar8
*)"el" },
198 { QFontDatabase::Cyrillic
, (const FcChar8
*)"ru" },
199 { QFontDatabase::Armenian
, (const FcChar8
*)"hy" },
200 { QFontDatabase::Hebrew
, (const FcChar8
*)"he" },
201 { QFontDatabase::Arabic
, (const FcChar8
*)"ar" },
202 { QFontDatabase::Syriac
, (const FcChar8
*)"syr" },
203 { QFontDatabase::Thaana
, (const FcChar8
*)"div" },
204 { QFontDatabase::Devanagari
, (const FcChar8
*)"hi" },
205 { QFontDatabase::Bengali
, (const FcChar8
*)"bn" },
206 { QFontDatabase::Gurmukhi
, (const FcChar8
*)"pa" },
207 { QFontDatabase::Gujarati
, (const FcChar8
*)"gu" },
208 { QFontDatabase::Oriya
, (const FcChar8
*)"or" },
209 { QFontDatabase::Tamil
, (const FcChar8
*)"ta" },
210 { QFontDatabase::Telugu
, (const FcChar8
*)"te" },
211 { QFontDatabase::Kannada
, (const FcChar8
*)"kn" },
212 { QFontDatabase::Malayalam
, (const FcChar8
*)"ml" },
213 { QFontDatabase::Sinhala
, (const FcChar8
*)"si" },
214 { QFontDatabase::Thai
, (const FcChar8
*)"th" },
215 { QFontDatabase::Lao
, (const FcChar8
*)"lo" },
216 { QFontDatabase::Tibetan
, (const FcChar8
*)"bo" },
217 { QFontDatabase::Myanmar
, (const FcChar8
*)"my" },
218 { QFontDatabase::Georgian
, (const FcChar8
*)"ka" },
219 { QFontDatabase::Khmer
, (const FcChar8
*)"km" },
220 { QFontDatabase::SimplifiedChinese
, (const FcChar8
*)"zh-cn" },
221 { QFontDatabase::TraditionalChinese
, (const FcChar8
*)"zh-tw" },
222 { QFontDatabase::Japanese
, (const FcChar8
*)"ja" },
223 { QFontDatabase::Korean
, (const FcChar8
*)"ko" },
224 { QFontDatabase::Vietnamese
, (const FcChar8
*)"vi" },
225 { QFontDatabase::Other
, NULL
},
227 // The following is only used to save writing system data for disabled fonts...
228 { QFontDatabase::Telugu
, (const FcChar8
*)"Qt-Telugu" },
229 { QFontDatabase::Kannada
, (const FcChar8
*)"Qt-Kannada" },
230 { QFontDatabase::Malayalam
, (const FcChar8
*)"Qt-Malayalam" },
231 { QFontDatabase::Sinhala
, (const FcChar8
*)"Qt-Sinhala" },
232 { QFontDatabase::Myanmar
, (const FcChar8
*)"Qt-Myanmar" },
233 { QFontDatabase::Ogham
, (const FcChar8
*)"Qt-Ogham" },
234 { QFontDatabase::Runic
, (const FcChar8
*)"Qt-Runic" },
236 { QFontDatabase::Any
, NULL
}
239 // Cache qstring->ws value
241 static QMap
<QString
, qulonglong
> constWritingSystemMap
;
243 void CDisabledFonts::createWritingSystemMap()
245 // check if we have created the cache yet...
246 if(constWritingSystemMap
.isEmpty())
247 for(int i
=0; QFontDatabase::Any
!=theirLanguageForWritingSystem
[i
].ws
; ++i
)
248 if(theirLanguageForWritingSystem
[i
].lang
)
249 constWritingSystemMap
[(const char *)theirLanguageForWritingSystem
[i
].lang
]=
250 ((qulonglong
)1)<<theirLanguageForWritingSystem
[i
].ws
;
253 CDisabledFonts::CDisabledFonts(bool sys
)
260 createWritingSystemMap();
262 if(Misc::root() || sys
)
263 path
=KFI_ROOT_CFG_DIR
;
266 path
=KGlobal::dirs()->localxdgconfdir();
268 if(!Misc::dExists(path
))
269 Misc::createDir(path
);
272 itsFileName
=path
+'/'+FILE_NAME
".xml";
274 itsModifiable
=Misc::fWritable(itsFileName
) ||
275 (!Misc::fExists(itsFileName
) && Misc::dWritable(Misc::getDir(itsFileName
)));
282 void CDisabledFonts::reload()
287 save(); // This will only do a second save, if the 'load' set the modfified flag...
290 bool CDisabledFonts::refresh()
292 time_t ts
=Misc::getTimeStamp(itsFileName
);
294 if(!ts
|| ts
!=itsTimeStamp
)
304 // Do not always lock during a load, as we may be trying to read global file (but not as root),
305 // or this load might be being called within the save() - so cant lock as is already!
306 void CDisabledFonts::load(bool lock
)
308 CLockFile
lf(itsFileName
);
310 lock
=lock
&& itsModifiable
;
312 if(!lock
|| lf
.lock())
314 time_t ts
=Misc::getTimeStamp(itsFileName
);
316 if(!ts
|| ts
!=itsTimeStamp
)
320 QFile
f(itsFileName
);
322 if(f
.open(QIODevice::ReadOnly
))
329 if(doc
.setContent(&f
))
330 for(QDomNode n
=doc
.documentElement().firstChild(); !n
.isNull(); n
=n
.nextSibling())
332 QDomElement e
=n
.toElement();
334 if(FONT_TAG
==e
.tagName())
338 if(font
.load(e
, itsModified
))
351 bool CDisabledFonts::save()
357 CLockFile
lf(itsFileName
);
361 time_t ts
=Misc::getTimeStamp(itsFileName
);
363 if(Misc::fExists(itsFileName
) && ts
!=itsTimeStamp
)
365 // Timestamps differ, so possibly file was modified by another process...
366 merge(CDisabledFonts(*this));
369 QTemporaryFile
temp(itsFileName
);
371 temp
.setAutoRemove(false);
376 QFile
file(temp
.fileName());
378 if(!file
.open(QIODevice::WriteOnly
))
380 temp
.setAutoRemove(true);
384 QTextStream
str(&file
);
386 str
<< "<"DISABLED_DOC
">" << endl
;
388 TFontList::Iterator
it(itsFonts
.begin()),
393 str
<< "</"DISABLED_DOC
">" << endl
;
395 file
.setPermissions(QFile::ReadOwner
|QFile::WriteOwner
|
396 QFile::ReadGroup
|QFile::ReadOther
);
398 rv
=::rename(QFile::encodeName(file
.fileName()), QFile::encodeName(itsFileName
));
401 itsTimeStamp
=Misc::getTimeStamp(itsFileName
);
410 static QString
expandHome(const QString
&path
)
412 QString mpath
= path
;
413 return !mpath
.isEmpty() && '~'==mpath
[0]
414 ? 1==mpath
.length() ? QDir::homePath() : mpath
.replace(0, 1, QDir::homePath())
418 bool CDisabledFonts::TFile::load(QDomElement
&elem
)
420 if(elem
.hasAttribute(PATH_ATTR
))
424 path
=expandHome(elem
.attribute(PATH_ATTR
));
425 foundry
=elem
.attribute(FOUNDRY_ATTR
);
427 if(elem
.hasAttribute(FACE_ATTR
))
428 face
=elem
.attribute(FACE_ATTR
).toInt(&ok
);
432 return Misc::fExists(path
);
438 QString
CDisabledFonts::TFileList::toString(bool skipFirst
) const
441 QTextStream
str(&s
, QIODevice::WriteOnly
);
442 ConstIterator
it(begin()),
445 if(skipFirst
&& it
!=e
)
449 str
<< (*it
).path
<< endl
450 << (*it
).foundry
<< endl
451 << (*it
).face
<< endl
;
456 void CDisabledFonts::TFileList::fromString(QString
&s
)
459 QTextStream
str(&s
, QIODevice::ReadOnly
);
463 QString
path(str
.readLine()),
464 foundry(str
.readLine()),
465 face(str
.readLine());
470 append(TFile(path
, face
.toInt(), foundry
));
474 bool CDisabledFonts::TFont::load(QDomElement
&elem
, bool &modified
)
476 if(elem
.hasAttribute(FAMILY_ATTR
))
479 int weight(KFI_NULL_SETTING
), width(KFI_NULL_SETTING
), slant(KFI_NULL_SETTING
),
480 tmp(KFI_NULL_SETTING
);
482 family
=elem
.attribute(FAMILY_ATTR
);
484 if(elem
.hasAttribute(WEIGHT_ATTR
))
486 tmp
=elem
.attribute(WEIGHT_ATTR
).toInt(&ok
);
490 if(elem
.hasAttribute(WIDTH_ATTR
))
492 tmp
=elem
.attribute(WIDTH_ATTR
).toInt(&ok
);
497 if(elem
.hasAttribute(SLANT_ATTR
))
499 tmp
=elem
.attribute(SLANT_ATTR
).toInt(&ok
);
504 styleInfo
=FC::createStyleVal(weight
, width
, slant
);
506 if(elem
.hasAttribute(LANGS_ATTR
))
508 QStringList
langs(elem
.attribute(LANGS_ATTR
).split(LANG_SEP
, QString::SkipEmptyParts
));
510 QStringList::ConstIterator
it(langs
.begin()),
514 writingSystems
|=constWritingSystemMap
[*it
];
517 if(elem
.hasAttribute(PATH_ATTR
))
527 for(QDomNode n
=elem
.firstChild(); !n
.isNull(); n
=n
.nextSibling())
529 QDomElement ent
=n
.toElement();
531 if(FILE_TAG
==ent
.tagName())
542 return files
.count()>0;
548 const QString
& CDisabledFonts::TFont::getName() const
551 name
=FC::createName(family
, styleInfo
);
555 CDisabledFonts::TFontList::Iterator
CDisabledFonts::TFontList::locate(const TFont
&t
)
560 CDisabledFonts::TFontList::Iterator
CDisabledFonts::TFontList::locate(const Misc::TFont
&t
)
562 return locate((TFont
&)t
);
565 void CDisabledFonts::TFontList::add(const TFont
&t
) const
567 (const_cast<TFontList
*>(this))->insert(t
);
570 bool CDisabledFonts::disable(const TFont
&font
)
572 static const int constMaxMods
=100;
574 TFontList::Iterator it
=itsFonts
.locate(font
);
576 if(it
==itsFonts
.end())
578 TFont
newFont(font
.family
, font
.styleInfo
, font
.writingSystems
);
580 if(changeStatus(font
.files
, false))
582 TFileList::ConstIterator
it(font
.files
.begin()),
583 end(font
.files
.end());
586 newFont
.files
.add(TFile(changeName((*it
).path
, false), (*it
).face
, (*it
).foundry
));
588 itsFonts
.add(newFont
);
591 if(++itsMods
>constMaxMods
)
597 TFileList::ConstIterator
it(font
.files
.begin()),
598 end(font
.files
.end());
602 QString
modName(changeName((*it
).path
, false));
603 if(Misc::fExists(modName
))
604 newFont
.files
.add(TFile(modName
, (*it
).face
, (*it
).foundry
));
609 if(newFont
.files
.count()==font
.files
.count())
611 itsFonts
.add(newFont
);
613 if(++itsMods
>constMaxMods
)
620 return true; // Already disabled...
625 bool CDisabledFonts::enable(TFontList::Iterator font
)
627 if(font
!=itsFonts
.end())
629 if(changeStatus((*font
).files
, true))
631 itsFonts
.erase(font
);
638 TFileList::ConstIterator
fit((*font
).files
.begin()),
639 fend((*font
).files
.end());
641 for(; fit
!=fend
; ++fit
)
643 QString
modName(changeName((*fit
).path
, true));
644 if(Misc::fExists(modName
))
645 mod
.append((*fit
).path
);
650 if(mod
.count()==(*font
).files
.count())
652 itsFonts
.erase(font
);
659 return true; // Already enabled...
664 CDisabledFonts::TFontList::Iterator
CDisabledFonts::find(const QString
&name
, int face
)
666 TFontList::Iterator
it(itsFonts
.begin()),
668 QString
fontName(name
);
671 fontName
=fontName
.mid(1);
674 if((*it
).getName()==fontName
)
677 if(it
==end
&& '.'==name
[0])
678 for(it
=itsFonts
.begin(); it
!=end
; ++it
)
680 TFileList::ConstIterator
fit((*it
).files
.begin()),
681 fend((*it
).files
.end());
683 for(; fit
!=fend
; ++fit
)
684 if(Misc::getFile((*fit
).path
)==name
&& (*fit
).face
==face
)
691 // This constrcutor is only used internally, and is called in ::save() when it has been
692 // detected that the file has been modified by another process...
693 CDisabledFonts::CDisabledFonts(const CDisabledFonts
&o
)
694 : itsFileName(o
.itsFileName
),
695 itsTimeStamp(o
.itsTimeStamp
),
702 void CDisabledFonts::merge(const CDisabledFonts
&other
)
704 TFontList::ConstIterator
it(other
.itsFonts
.begin()),
705 end(other
.itsFonts
.end());
709 TFontList::Iterator
existing(itsFonts
.locate(*it
));
711 if(existing
!=itsFonts
.end())
713 TFileList::ConstIterator
fit((*it
).files
.begin()),
714 fend((*it
).files
.end());
716 for(; fit
!=fend
; ++fit
)
717 if(!(*existing
).files
.contains(*fit
))
719 (*existing
).files
.add(*fit
);
733 QTextStream
& operator<<(QTextStream
&s
, const KFI::CDisabledFonts::TFile
&f
)
735 s
<< PATH_ATTR
"=\"" << KFI::Misc::encodeText(KFI::Misc::contractHome(f.path), s) << "\" "
736 << FOUNDRY_ATTR"=\"" << KFI::Misc::encodeText(f.foundry, s) << "\" ";
739 s << FACE_ATTR"=\"" << f.face << "\" ";
744 QTextStream & operator<<(QTextStream &s, const KFI::CDisabledFonts::TFont &f)
746 int weight, width, slant;
748 KFI::FC::decomposeStyleVal(f.styleInfo, weight, width, slant);
750 s << " <"FONT_TAG" "FAMILY_ATTR"=\"" << KFI::Misc::encodeText(f.family, s) << "\" ";
752 if(KFI_NULL_SETTING!=weight)
753 s << WEIGHT_ATTR"=\"" << weight << "\" ";
754 if(KFI_NULL_SETTING!=width)
755 s << WIDTH_ATTR"=\"" << width << "\" ";
756 if(KFI_NULL_SETTING!=slant)
757 s << SLANT_ATTR"=\"" << slant << "\" ";
760 QMap<QString, qulonglong>::ConstIterator wit(KFI::constWritingSystemMap.begin()),
761 wend(KFI::constWritingSystemMap.end());
763 for(; wit!=wend; ++wit)
764 if(f.writingSystems&wit.value())
768 s << LANGS_ATTR"=\"" << ws.join(LANG_SEP) << "\" ";
770 if(1==f.files.count())
771 s << *(f.files.begin()) << "/>" << endl;
774 KFI::CDisabledFonts::TFileList::ConstIterator it(f.files.begin()),
779 s << " <"FILE_TAG" " << *it << "/>" << endl;
780 s << " </"FONT_TAG">" << endl;