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.
26 #include <QtGui/QPainter>
27 #include <QtGui/QPixmap>
28 #include <QtCore/QFile>
29 #include <QtCore/QTextStream>
30 #include <QtGui/QX11Info>
32 #include <KDE/KConfig>
33 #include <KDE/KConfigGroup>
34 #include <KDE/KGlobalSettings>
35 #include <KDE/KIO/NetAccess>
36 #include <KDE/KGlobal>
37 #include <KDE/KLocale>
40 #include <X11/Xft/Xft.h>
43 //#define KFI_FC_DEBUG
45 #define KFI_PREVIEW_GROUP "KFontInst Preview Settings"
46 #define KFI_PREVIEW_STRING_KEY "String"
51 bool CFcEngine::theirFcDirty(true);
52 const int CFcEngine::constScalableSizes
[]={8, 10, 12, 24, 36, 48, 64, 72, 96, 0 };
53 const int CFcEngine::constDefaultAlphaSize
=24;
55 QColor
CFcEngine::theirTextCol(Qt::black
);
57 static int fcToQtWeight(int weight
)
63 case FC_WEIGHT_EXTRALIGHT
:
64 return QFont::Light
>>1;
68 case FC_WEIGHT_REGULAR
:
70 case FC_WEIGHT_MEDIUM
:
71 #ifdef KFI_HAVE_MEDIUM_WEIGHT
72 return (QFont::Normal
+QFont::DemiBold
)>>1;
75 case FC_WEIGHT_DEMIBOLD
:
76 return QFont::DemiBold
;
79 case FC_WEIGHT_EXTRABOLD
:
80 return (QFont::Bold
+QFont::Black
)>>1;
86 #ifndef KFI_FC_NO_WIDTHS
87 static int fcToQtWidth(int weight
)
91 case KFI_FC_WIDTH_ULTRACONDENSED
:
92 return QFont::UltraCondensed
;
93 case KFI_FC_WIDTH_EXTRACONDENSED
:
94 return QFont::ExtraCondensed
;
95 case KFI_FC_WIDTH_CONDENSED
:
96 return QFont::Condensed
;
97 case KFI_FC_WIDTH_SEMICONDENSED
:
98 return QFont::SemiCondensed
;
100 case KFI_FC_WIDTH_NORMAL
:
101 return QFont::Unstretched
;
102 case KFI_FC_WIDTH_SEMIEXPANDED
:
103 return QFont::SemiExpanded
;
104 case KFI_FC_WIDTH_EXPANDED
:
105 return QFont::Expanded
;
106 case KFI_FC_WIDTH_EXTRAEXPANDED
:
107 return QFont::ExtraExpanded
;
108 case KFI_FC_WIDTH_ULTRAEXPANDED
:
109 return QFont::UltraExpanded
;
114 static bool fcToQtSlant(int slant
)
116 return FC_SLANT_ROMAN
==slant
? false : true;
119 inline bool equal(double d1
, double d2
)
121 return (fabs(d1
- d2
) < 0.0001);
124 inline bool equalWeight(int a
, int b
)
126 return a
==b
|| FC::weight(a
)==FC::weight(b
);
129 #ifndef KFI_FC_NO_WIDTHS
130 inline bool equalWidth(int a
, int b
)
132 return a
==b
|| FC::width(a
)==FC::width(b
);
136 inline bool equalSlant(int a
, int b
)
138 return a
==b
|| FC::slant(a
)==FC::slant(b
);
141 static bool drawChar32Centre(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, quint32 ch
, int w
, int h
)
143 if(XftCharExists(QX11Info::display(), xftFont
, ch
))
147 XftTextExtents32(QX11Info::display(), xftFont
, &ch
, 1, &extents
);
149 int rx(((w
-extents
.width
)/2)+extents
.x
),
150 ry(((h
-extents
.height
)/2)+(extents
.y
));
152 XftDrawString32(xftDraw
, xftCol
, xftFont
, rx
, ry
, &ch
, 1);
159 static const int constBorder
=2;
161 static bool drawChar32(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, quint32 ch
,
162 int &x
, int &y
, int w
, int h
, int fontHeight
, QRect
&r
)
165 if(XftCharExists(QX11Info::display(), xftFont
, ch
))
169 XftTextExtents32(QX11Info::display(), xftFont
, &ch
, 1, &extents
);
174 if(x
+extents
.width
+constBorder
>w
)
179 y
+=fontHeight
+constBorder
;
184 r
=QRect(x
-extents
.x
, y
-extents
.y
, extents
.width
+constBorder
, extents
.height
);
186 XftDrawString32(xftDraw
, xftCol
, xftFont
, x
, y
, &ch
, 1);
187 x
+=extents
.xOff
+constBorder
;
196 static bool drawString(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, const QString
&text
,
197 int x
, int &y
, int h
)
200 const FcChar16
*str
=(FcChar16
*)(text
.utf16());
202 XftTextExtents16(QX11Info::display(), xftFont
, str
, text
.length(), &extents
);
203 if(y
+extents
.height
<h
)
204 XftDrawString16(xftDraw
, xftCol
, xftFont
, x
, y
+extents
.y
, str
, text
.length());
213 static void drawString(XftDraw
*xftDraw
, XftColor
*xftCol
, const QString
&text
,
214 int x
, int &y
, int h
)
217 QFont
qt(KGlobalSettings::generalFont());
218 XftFont
*xftFont
=XftFontOpen(QX11Info::display(), 0,
219 FC_FAMILY
, FcTypeString
, (const FcChar8
*)(qt
.family().toUtf8().data()),
220 FC_WEIGHT
, FcTypeInteger
, qt
.bold() ? FC_WEIGHT_BOLD
: FC_WEIGHT_REGULAR
,
221 FC_SLANT
, FcTypeInteger
, qt
.italic() ? FC_SLANT_ITALIC
: FC_SLANT_ROMAN
,
222 FC_SIZE
, FcTypeDouble
, (double)qt
.pointSize(),
227 drawString(xftDraw
, xftFont
, xftCol
, text
, x
, y
, h
);
228 XftFontClose(QX11Info::display(), xftFont
);
232 static bool drawGlyph(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, FT_UInt i
,
233 int &x
, int &y
, int w
, int h
, int fontHeight
, bool oneLine
,
238 XftGlyphExtents(QX11Info::display(), xftFont
, &i
, 1, &extents
);
240 if(x
+extents
.width
+2>w
)
251 XftDrawGlyphs(xftDraw
, xftCol
, xftFont
, x
, y
, &i
, 1);
253 r
=QRect(x
-extents
.x
, y
-extents
.y
, extents
.width
+constBorder
, extents
.height
);
260 static bool drawAllGlyphs(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, int fontHeight
,
261 int &x
, int &y
, int w
, int h
, bool oneLine
=false, int max
=-1,
268 FT_Face face
=XftLockFace(xftFont
);
272 int space(fontHeight
/10),
281 for(int i
=1; i
<face
->num_glyphs
&& y
<h
; ++i
)
282 if(drawGlyph(xftDraw
, xftFont
, xftCol
, i
, x
, y
, w
, h
, fontHeight
, oneLine
, r
))
291 *used
=used
->united(r
);
293 if(max
>0 && ++drawn
>=max
)
302 XftUnlockFace(xftFont
);
309 inline int point2Pixel(int point
)
311 return (point
*QX11Info::appDpiX()+36)/72;
314 static bool hasStr(XftFont
*font
, QString
&str
)
316 unsigned int slen
=str
.length(),
319 for(ch
=0; ch
<slen
; ++ch
)
320 if(!FcCharSetHasChar(font
->charset
, str
[ch
].unicode()))
325 static QString
usableStr(XftFont
*font
, QString
&str
)
327 unsigned int slen
=str
.length(),
331 for(ch
=0; ch
<slen
; ++ch
)
332 if(FcCharSetHasChar(font
->charset
, str
[ch
].unicode()))
337 CFcEngine::CFcEngine(bool init
)
340 itsAlphaSizeIndex(-1),
341 itsPreviewString(getDefaultPreviewString())
347 CFcEngine::~CFcEngine()
349 // Clear any fonts that may have been added...
350 FcConfigAppFontClear(FcConfigGetCurrent());
353 void CFcEngine::readConfig(KConfig
&cfg
)
355 cfg
.group(KFI_PREVIEW_GROUP
).readEntry(KFI_PREVIEW_STRING_KEY
, getDefaultPreviewString());
358 void CFcEngine::writeConfig(KConfig
&cfg
)
360 cfg
.group(KFI_PREVIEW_GROUP
).writeEntry(KFI_PREVIEW_STRING_KEY
, itsPreviewString
);
363 const QString
& CFcEngine::getName(const KUrl
&url
, int faceNo
)
365 if(url
!=itsLastUrl
|| faceNo
!=itsIndex
)
366 parseUrl(url
, faceNo
);
368 return itsDescriptiveName
;
371 bool CFcEngine::drawPreview(const QString
&item
, QPixmap
&pix
, const QColor
&col
, int h
, quint32 style
, int face
)
377 static const int constOffset
=2;
378 static const int constInitialWidth
=1536;
382 if(QChar('/')==item
[0]) // Then add to fontconfig's list, so that Xft can display it...
384 KUrl
url("file://"+item
);
386 ok
=parseUrl(url
, face
);
391 parseName(item
, style
);
403 // Calculate size of text...
404 int fSize
=((int)(h
*0.75))-2;
405 bool needResize(false);
407 QColor
bgndCol(Qt::white
);
408 bgndCol
.setAlphaF(0.0);
410 if(!itsScalable
) // Then need to get nearest size...
414 for(int s
=0; s
<itsSizes
.size(); ++s
)
415 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
421 pix
=QPixmap(constInitialWidth
, bSize
+8);
429 pix
=QPixmap(constInitialWidth
, h
);
433 const QX11Info
&x11Info(pix
.x11Info());
434 XRenderColor xrenderCol
;
437 xrenderCol
.red
=col
.red()<<8;
438 xrenderCol
.green
=col
.green()<<8;
439 xrenderCol
.blue
=col
.blue()<<8;
440 xrenderCol
.alpha
=0xffff;
442 XftColorAllocValue(QX11Info::display(), DefaultVisual(QX11Info::display(),
444 DefaultColormap(QX11Info::display(), x11Info
.screen()),
445 &xrenderCol
, &xftCol
);
447 XftDraw
*xftDraw
=XftDrawCreateAlpha(QX11Info::display(), (Pixmap
)(pix
.handle()), 32);
451 XftFont
*xftFont
=NULL
;
452 QString
text(itsPreviewString
);
455 // Calculate size of text...
456 int fSize
=((int)(h
*0.75))-2;
458 if(!itsScalable
) // Then need to get nearest size...
462 for(int s
=0; s
<itsSizes
.size(); ++s
)
463 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
468 xftFont
=getFont(fSize
);
472 if(hasStr(xftFont
, text
) || hasStr(xftFont
, text
=text
.toUpper()) ||
473 hasStr(xftFont
, text
=text
.toLower()))
476 const FcChar16
*str
=(FcChar16
*)(text
.utf16());
478 XftTextExtents16(QX11Info::display(), xftFont
, str
, text
.length(),
481 int y
=((h
-extents
.y
)/2)+extents
.y
;
482 XftDrawString16(xftDraw
, &xftCol
, xftFont
, constOffset
, y
, str
,
485 pix
=pix
.scaledToHeight(h
);
486 pix
= pix
.copy(0, 0, extents
.width
+(2*constOffset
)<constInitialWidth
487 ? extents
.width
+(2*constOffset
)
494 FT_Face face
=XftLockFace(xftFont
);
501 for(FT_UInt i
=1; i
<(unsigned int)face
->num_glyphs
&&
502 i
<(unsigned int)text
.length()+1; ++i
)
506 XftGlyphExtents(QX11Info::display(), xftFont
, &i
, 1,
509 if(x
+extents
.width
+2>constInitialWidth
) // Only want 1 line
512 XftDrawGlyphs(xftDraw
, &xftCol
, xftFont
, x
, y
, &i
, 1);
516 pix
=pix
.scaledToHeight(h
);
517 pix
= pix
.copy(0, 0, x
+constOffset
<constInitialWidth
521 XftUnlockFace(xftFont
);
534 bool CFcEngine::draw(const KUrl
&url
, int w
, int h
, QPixmap
&pix
, int faceNo
, bool thumb
,
535 const QList
<TRange
> &range
, QList
<TChar
> *chars
, const QString
&name
, quint32 style
)
542 if((url
==itsLastUrl
&& faceNo
==itsIndex
) ||
543 (!name
.isEmpty() && parseName(name
, style
, url
)) ||
544 parseUrl(url
, faceNo
))
549 if(!itsInstalled
) // Then add to fontconfig's list, so that Xft can display it...
550 addFontFile(itsFileName
);
553 // We allow kio_thumbnail to cache our thumbs. Normal is 128x128, and large is 256x256
554 // ...if kio_thumbnail asks us for a bigger size, then its probably the file info dialog, in
555 // which case treat it as a normal preview...
556 if(thumb
&& (h
>256 || w
!=h
))
563 pix
=thumb
&& itsScalable
? QPixmap(w
*4, h
*2) : QPixmap(w
, h
);
565 QColor
bgndCol(Qt::white
);
567 bgndCol
.setAlphaF(0.0);
572 const QX11Info
&x11Info(pix
.x11Info());
573 XRenderColor xrenderCol
;
576 xrenderCol
.red
=theirTextCol
.red()<<8;
577 xrenderCol
.green
=theirTextCol
.green()<<8;
578 xrenderCol
.blue
=theirTextCol
.blue()<<8;
579 xrenderCol
.alpha
=0xffff;
580 XftColorAllocValue(QX11Info::display(), DefaultVisual(QX11Info::display(),
582 DefaultColormap(QX11Info::display(), x11Info
.screen()),
583 &xrenderCol
, &xftCol
);
585 XftDraw
*xftDraw
=XftDrawCreateAlpha(QX11Info::display(), (Pixmap
)(pix
.handle()), 32);
589 XftFont
*xftFont
=NULL
;
593 QString
text(itsScalable
594 ? i18nc("First letter of the alphabet (in upper then lower case)", "Aa")
595 : i18nc("All letters of the alphabet (in upper/lower case pairs), followed by numbers",
596 "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"));
599 // Calculate size of text...
602 if(!itsScalable
) // Then need to get nearest size...
606 for(int s
=0; s
<itsSizes
.size(); ++s
)
607 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
612 xftFont
=getFont(fSize
);
616 QString
valid(usableStr(xftFont
, text
));
617 QRect
used(0, 0, 0, 0);
624 if(valid
.length()!=text
.length())
626 text
=getPunctuation().mid(1, 2); // '1' '2'
627 valid
=usableStr(xftFont
, text
);
631 if(valid
.length()<(text
.length()/2))
632 for(int i
=0; i
<3; ++i
)
634 text
=0==i
? getUppercaseLetters() : 1==i
? getLowercaseLetters() : getPunctuation();
635 valid
=usableStr(xftFont
, text
);
637 if(valid
.length()>=(text
.length()/2))
642 ? valid
.length()!=text
.length()
643 : valid
.length()<(text
.length()/2))
644 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fSize
, x
, y
, pix
.width(), pix
.height(), true,
645 itsScalable
? 4 : -1, itsScalable
? &used
: NULL
);
648 QVector
<uint
> ucs4(valid
.toUcs4());
651 for(int ch
=0; ch
<ucs4
.size(); ++ch
) // Display char by char so wraps...
652 if(drawChar32(xftDraw
, xftFont
, &xftCol
, ucs4
[ch
], x
, y
, pix
.width(), pix
.height(), fSize
, r
))
663 if(itsScalable
&& !used
.isEmpty() && used
.width()<pix
.width() && used
.height()<pix
.height())
667 else if(0==range
.count())
669 QString
lowercase(getLowercaseLetters()),
670 uppercase(getUppercaseLetters()),
671 punctuation(getPunctuation());
673 drawName(pix
, xftDraw
, &xftCol
, x
, y
, w
, h
);
675 xftFont
=getFont(alphaSize());
678 bool lc(hasStr(xftFont
, lowercase
)),
679 uc(hasStr(xftFont
, uppercase
)),
680 drawGlyphs
=!lc
&& !uc
;
686 QString
validPunc(usableStr(xftFont
, punctuation
));
687 bool punc(validPunc
.length()>=(punctuation
.length()/2));
690 drawString(xftDraw
, xftFont
, &xftCol
, lowercase
, x
, y
, h
);
692 drawString(xftDraw
, xftFont
, &xftCol
, uppercase
, x
, y
, h
);
694 drawString(xftDraw
, xftFont
, &xftCol
, validPunc
, x
, y
, h
);
695 XftFontClose(QX11Info::display(), xftFont
);
697 drawLine(pix
, 0, y
+2, w
-1, 1);
701 QString
previewString(getPreviewString());
706 previewString
=previewString
.toUpper();
708 previewString
=previewString
.toLower();
711 for(int s
=0; s
<itsSizes
.size(); ++s
)
712 if((xftFont
=getFont(itsSizes
[s
])))
714 int fontHeight
=xftFont
->ascent
+xftFont
->descent
;
718 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fontHeight
, x
, y
, w
, h
,
721 drawString(xftDraw
, xftFont
, &xftCol
, previewString
, x
, y
, h
);
722 XftFontClose(QX11Info::display(), xftFont
);
726 else if(1==range
.count() && (range
.first().null() || 0==range
.first().to
))
728 if(range
.first().null())
730 drawName(pix
, xftDraw
, &xftCol
, x
, y
, w
, h
);
732 if((xftFont
=getFont(alphaSize())))
734 int fontHeight
=xftFont
->ascent
+xftFont
->descent
;
737 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fontHeight
, x
, y
, w
, h
, 0);
739 XftFontClose(QX11Info::display(), xftFont
);
742 else if((xftFont
=getFont(alphaSize()*2)))
745 rv
=drawChar32Centre(xftDraw
, xftFont
, &xftCol
, (*(range
.begin())).from
,
746 pix
.width(), pix
.height());
751 QList
<TRange
>::ConstIterator
it(range
.begin()),
754 if((xftFont
=getFont(alphaSize())))
757 drawName(pix
, xftDraw
, &xftCol
, x
, y
, w
, h
);
761 int fontHeight
=xftFont
->ascent
+xftFont
->descent
, xOrig(x
), yOrig(y
);
764 for(it
=range
.begin(); it
!=end
&& !stop
; ++it
)
765 for(quint32 c
=(*it
).from
; c
<=(*it
).to
&& !stop
; ++c
)
767 if(drawChar32(xftDraw
, xftFont
, &xftCol
, c
, x
, y
, w
, h
, fontHeight
, r
))
769 if(chars
&& !r
.isEmpty())
770 chars
->append(TChar(r
, c
));
776 if(x
==xOrig
&& y
==yOrig
)
778 // No characters found within the selected range...
779 drawString(xftDraw
, &xftCol
, i18n("No characters found."), x
, y
, h
);
784 XftDrawDestroy(xftDraw
);
792 QString
CFcEngine::getDefaultPreviewString()
794 return i18nc("A sentence that uses all of the letters of the alphabet",
795 "The quick brown fox jumps over the lazy dog");
798 QString
CFcEngine::getUppercaseLetters()
800 return i18nc("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
803 QString
CFcEngine::getLowercaseLetters()
805 return i18nc("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz");
808 QString
CFcEngine::getPunctuation()
810 return i18nc("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]"); //krazy:exclude=i18ncheckarg
813 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
815 // Try to get the 'string' that matches the users KDE locale..
816 QString
CFcEngine::getFcLangString(FcPattern
*pat
, const char *val
, const char *valLang
)
819 QStringList kdeLangs
=KGlobal::locale()->languageList(),
821 QStringList::ConstIterator
it(kdeLangs
.begin()),
824 // Create list of langs that this font's 'val' is encoded in...
825 for(int i
=0; true; ++i
)
827 QString lang
=getFcString(pat
, valLang
, i
);
832 fontLangs
.append(lang
);
835 // Now go through the user's KDE locale, and try to find a font match...
838 int index
=fontLangs
.findIndex(*it
);
842 rv
=getFcString(pat
, val
, index
);
850 rv
=getFcString(pat
, val
, 0);
855 bool CFcEngine::getInfo(const KUrl
&url
, int faceNo
, Misc::TFont
&info
)
857 if((url
==itsLastUrl
&& faceNo
==itsIndex
) || parseUrl(url
, faceNo
))
859 if(url
.isLocalFile() || Misc::isHidden(url
))
863 if(-1==(pos
=itsDescriptiveName
.indexOf(", "))) // No style information...
864 info
.family
=itsDescriptiveName
;
866 info
.family
=itsDescriptiveName
.left(pos
);
870 info
.styleInfo
=styleVal();
877 QFont
CFcEngine::getQFont(const QString
&family
, quint32 style
, int size
)
883 FC::decomposeStyleVal(style
, weight
, width
, slant
);
885 QFont
font(family
, size
, fcToQtWeight(weight
), fcToQtSlant(slant
));
887 #ifndef KFI_FC_NO_WIDTHS
888 font
.setStretch(fcToQtWidth(width
));
893 bool CFcEngine::parseUrl(const KUrl
&url
, int faceNo
)
896 kDebug() << url
.prettyUrl() << ' ' << faceNo
;
909 // fonts:/System/times.ttf
910 // file:/home/wibble/hmm.ttf
912 if(KFI_KIO_FONTS_PROTOCOL
==url
.protocol())
914 bool hidden
=Misc::isHidden(url
);
915 KIO::UDSEntry udsEntry
;
917 quint32 style
=KFI_NO_STYLE_INFO
;
919 if(KIO::NetAccess::stat(url
, udsEntry
, NULL
)) // Need to stat the url to get its font name...
921 name
=udsEntry
.stringValue((uint
)KIO::UDSEntry::UDS_NAME
);
922 itsFileName
=udsEntry
.stringValue((uint
)UDS_EXTRA_FILE_NAME
);
923 style
=udsEntry
.numberValue((uint
)UDS_EXTRA_FC_STYLE
);
924 itsIndex
=Misc::getIntQueryVal(KUrl(udsEntry
.stringValue((uint
)KIO::UDSEntry::UDS_URL
)),
927 kDebug() << "Stated fonts:/ url, name:" << name
<< " itsFileName:" << itsFileName
928 << " style:" << style
<< " itsIndex:" << itsIndex
;
932 kDebug() << "isHidden:" << hidden
;
941 if(!parseUrl(KUrl(name
), faceNo
))
946 parseName(name
, style
);
953 else if(url
.isLocalFile() || url
.protocol().isEmpty())
955 // Now lets see if its from the thumbnail job! if so, then file should contain either:
957 // b. FontName followed by style info
958 QFile
file(url
.path());
959 bool isThumbnailUrl
=false;
961 if(file
.size()<2048 && file
.open(QIODevice::ReadOnly
)) // Data should be less than 2k, and fonts usually above!
963 QTextStream
stream(&file
);
964 QString
line1(stream
.readLine()),
965 line2(stream
.readLine());
968 isThumbnailUrl
=(0==line1
.indexOf(KFI_KIO_FONTS_PROTOCOL
) ||
969 0==line1
.indexOf("file:/")) &&
970 parseUrl(KUrl(line1
), faceNo
);
971 else if(0==line1
.indexOf(KFI_PATH_KEY
) && 0==line2
.indexOf(KFI_FACE_KEY
))
973 line1
=line1
.mid(strlen(KFI_PATH_KEY
));
974 line2
=line2
.mid(strlen(KFI_FACE_KEY
));
976 if(!line1
.isEmpty() && !line2
.isEmpty())
979 int face
=line2
.toInt(&ok
);
981 isThumbnailUrl
=ok
&& parseUrl(line1
, face
<0 ? 0 : face
);
984 else if(0==line1
.indexOf(KFI_NAME_KEY
) && 0==line2
.indexOf(KFI_STYLE_KEY
))
986 line1
=line1
.mid(strlen(KFI_NAME_KEY
));
987 line2
=line2
.mid(strlen(KFI_STYLE_KEY
));
989 if(!line1
.isEmpty() && !line2
.isEmpty())
992 quint32 style
=line2
.toULong(&ok
);
994 itsInstalled
=isThumbnailUrl
=ok
&& parseName(line1
, style
);
998 QString
line3(stream
.readLine()),
999 line4(stream
.readLine());
1001 if(0==line3
.indexOf(KFI_PATH_KEY
) && 0==line4
.indexOf(KFI_FACE_KEY
))
1003 line3
=line3
.mid(strlen(KFI_PATH_KEY
));
1004 line4
=line4
.mid(strlen(KFI_FACE_KEY
));
1006 if(!line1
.isEmpty() && !line2
.isEmpty())
1009 int face
=line4
.toInt(&ok
);
1024 if(!isThumbnailUrl
) // Its not a thumbnail, so read the real font file...
1026 itsName
=itsFileName
=url
.path();
1029 FcPattern
*pat
=FcFreeTypeQuery((const FcChar8
*)(QFile::encodeName(itsFileName
).data()),
1030 faceNo
, NULL
, &count
);
1032 itsWeight
=FC_WEIGHT_REGULAR
;
1033 itsWidth
=KFI_FC_WIDTH_NORMAL
;
1034 itsSlant
=FC_SLANT_ROMAN
;
1038 FcPatternGetInteger(pat
, FC_WEIGHT
, 0, &itsWeight
);
1039 FcPatternGetInteger(pat
, FC_SLANT
, 0, &itsSlant
);
1040 #ifndef KFI_FC_NO_WIDTHS
1041 FcPatternGetInteger(pat
, FC_WIDTH
, 0, &itsWidth
);
1043 itsDescriptiveName
=FC::createName(pat
, itsWeight
, itsWidth
, itsSlant
);
1044 FcPatternDestroy(pat
);
1048 itsDescriptiveName
.clear();
1063 bool CFcEngine::parseName(const QString
&name
, quint32 style
, const KUrl
&url
)
1069 itsDescriptiveName
=name
;
1071 if(-1==(pos
=name
.indexOf(", "))) // No style information...
1073 if(KFI_NO_STYLE_INFO
!=style
)
1074 FC::decomposeStyleVal(style
, itsWeight
, itsWidth
, itsSlant
);
1077 itsWeight
=FC_WEIGHT_REGULAR
;
1078 itsWidth
=KFI_FC_WIDTH_NORMAL
;
1079 itsSlant
=FC_SLANT_ROMAN
;
1085 if(KFI_NO_STYLE_INFO
!=style
)
1086 FC::decomposeStyleVal(style
, itsWeight
, itsWidth
, itsSlant
);
1089 QString
style(name
.mid(pos
+2));
1091 itsWeight
=FC::strToWeight(style
, style
);
1092 itsWidth
=FC::strToWidth(style
, style
);
1093 itsSlant
=FC::strToSlant(style
);
1095 itsName
=name
.left(pos
);
1098 itsFileName
.clear();
1100 itsIndex
=0; // Doesn't matter, as we're gonna use font name!
1105 XftFont
* CFcEngine::queryFont()
1107 static const int constQuerySize
=8;
1113 XftFont
*f
=getFont(constQuerySize
);
1115 if(!isCorrect(f
, true))
1117 XftFontClose(QX11Info::display(), f
);
1121 if(itsInstalled
&& !f
)
1123 // Perhaps its a newly installed font? If so try re-initialising fontconfig...
1127 f
=getFont(constQuerySize
);
1129 // This time don't bother checking family - we've re-inited fc anyway, so things should be
1130 // up to date... And for "Symbol" Fc returns "Standard Symbols L", so wont match anyway!
1131 if(f
&& !isCorrect(f
, false))
1133 XftFontClose(QX11Info::display(), f
);
1138 kDebug() << "ret" << (int)f
;
1143 XftFont
* CFcEngine::getFont(int size
)
1148 kDebug() << QString(itsInstalled
? itsName
: itsFileName
) << ' ' << size
;
1153 #ifndef KFI_FC_NO_WIDTHS
1154 if(KFI_NULL_SETTING
!=itsWidth
)
1155 f
=XftFontOpen(QX11Info::display(), 0,
1156 FC_FAMILY
, FcTypeString
, (const FcChar8
*)(itsName
.toUtf8().data()),
1157 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1158 FC_SLANT
, FcTypeInteger
, itsSlant
,
1159 FC_WIDTH
, FcTypeInteger
, itsWidth
,
1160 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1164 f
=XftFontOpen(QX11Info::display(), 0,
1165 FC_FAMILY
, FcTypeString
, (const FcChar8
*)(itsName
.toUtf8().data()),
1166 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1167 FC_SLANT
, FcTypeInteger
, itsSlant
,
1168 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1173 FcPattern
*pattern
= FcPatternBuild(NULL
,
1174 FC_FILE
, FcTypeString
,
1175 QFile::encodeName(itsFileName
).data(),
1176 FC_INDEX
, FcTypeInteger
, itsIndex
<0 ? 0 : itsIndex
,
1177 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1179 f
=XftFontOpenPattern(QX11Info::display(), pattern
);
1183 kDebug() << "ret: " << (int)f
;
1189 bool CFcEngine::isCorrect(XftFont
*f
, bool checkFamily
)
1196 QTextStream
s(&xxx
);
1202 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WEIGHT
, 0, &iv
))
1203 s
<< iv
<< '/' << itsWeight
;
1208 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_SLANT
, 0, &iv
))
1209 s
<< iv
<< '/' << itsSlant
;
1214 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WIDTH
, 0, &iv
))
1215 s
<< iv
<< '/' << itsWidth
;
1221 if(FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FAMILY
, 0, &str
) && str
)
1222 s
<< QString::fromUtf8((char *)str
) << '/' << itsName
;
1229 s
<< "NOT Installed... ";
1233 kDebug() << "isCorrect? " << xxx
;
1239 ? FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WEIGHT
, 0, &iv
) &&
1240 equalWeight(iv
, itsWeight
) &&
1241 FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_SLANT
, 0, &iv
) &&
1242 equalSlant(iv
, itsSlant
) &&
1243 #ifndef KFI_FC_NO_WIDTHS
1244 (KFI_NULL_SETTING
==itsWidth
||
1245 (FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WIDTH
, 0, &iv
) &&
1246 equalWidth(iv
, itsWidth
))) &&
1249 (FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FAMILY
, 0, &str
) && str
&&
1250 QString::fromUtf8((char *)str
)==itsName
))
1251 : (itsIndex
<0 || (FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_INDEX
, 0, &iv
) && itsIndex
==iv
)) &&
1252 FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FILE
, 0, &str
) && str
&&
1253 QString::fromUtf8((char *)str
)==itsFileName
1257 void CFcEngine::getSizes()
1262 XftFont
*f
=queryFont();
1263 int alphaSize(itsSizes
.size()>itsAlphaSizeIndex
&& itsAlphaSizeIndex
>=0 ? itsSizes
[itsAlphaSizeIndex
] : constDefaultAlphaSize
);
1268 itsAlphaSizeIndex
=0;
1272 bool gotSizes
=false;
1277 if(FcResultMatch
!=FcPatternGetBool(f
->pattern
, FC_SCALABLE
, 0, &itsScalable
))
1278 itsScalable
=FcFalse
;
1282 FcPattern
*pat
=NULL
;
1283 FcObjectSet
*os
= FcObjectSetBuild(FC_PIXEL_SIZE
, (void*)0);
1284 #ifndef KFI_FC_NO_WIDTHS
1285 if(KFI_NULL_SETTING
!=itsWidth
)
1286 pat
=FcPatternBuild(NULL
,
1287 FC_FAMILY
, FcTypeString
,
1288 (const FcChar8
*)(itsName
.toUtf8().data()),
1289 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1290 FC_SLANT
, FcTypeInteger
, itsSlant
,
1291 FC_WIDTH
, FcTypeInteger
, itsWidth
,
1295 pat
=FcPatternBuild(NULL
,
1296 FC_FAMILY
, FcTypeString
,
1297 (const FcChar8
*)(itsName
.toUtf8().data()),
1298 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1299 FC_SLANT
, FcTypeInteger
, itsSlant
,
1302 FcFontSet
*set
=FcFontList(0, pat
, os
);
1304 FcPatternDestroy(pat
);
1305 FcObjectSetDestroy(os
);
1311 kDebug() << "got fixed sizes: " << set
->nfont
;
1313 itsSizes
.reserve(set
->nfont
);
1314 for (int i
= 0; i
< set
->nfont
; i
++)
1315 if(FcResultMatch
==FcPatternGetDouble(set
->fonts
[i
], FC_PIXEL_SIZE
, 0, &px
))
1318 itsSizes
.push_back((int)px
);
1321 kDebug() << "got fixed: " << px
;
1324 itsAlphaSizeIndex
=size
;
1327 FcFontSetDestroy(set
);
1333 FT_Face face
=XftLockFace(f
);
1337 itsIndexCount
=face
->num_faces
;
1338 if(!(itsScalable
=FT_IS_SCALABLE(face
)))
1340 int numSizes
=face
->num_fixed_sizes
,
1345 itsSizes
.reserve(numSizes
);
1348 kDebug() << "numSizes fixed: " << numSizes
;
1350 for (size
=0; size
<numSizes
; size
++)
1352 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
1353 double px
=face
->available_sizes
[size
].y_ppem
>>6;
1355 double px
=face
->available_sizes
[size
].width
;
1358 kDebug() << "px: " << px
;
1360 itsSizes
.push_back((int)px
);
1363 itsAlphaSizeIndex
=size
;
1370 XftFontClose(QX11Info::display(), f
);
1375 itsSizes
.reserve(sizeof(constScalableSizes
)/sizeof(int));
1377 for (int i
=0; constScalableSizes
[i
]; ++i
)
1379 int px
=point2Pixel(constScalableSizes
[i
]);
1382 itsAlphaSizeIndex
=i
;
1383 itsSizes
.push_back(px
);
1392 void CFcEngine::drawLine(QPixmap
&pix
, int x
, int y
, int w
, int h
)
1394 QPainter
painter(&pix
);
1395 painter
.setPen(theirTextCol
);
1396 painter
.drawLine(x
, y
, x
+w
-1, y
+h
-1);
1400 void CFcEngine::drawName(QPixmap
&pix
, XftDraw
*xftDraw
, XftColor
*xftCol
, int x
, int &y
, int w
, int h
)
1402 QString
title(itsDescriptiveName
.isEmpty()
1403 ? i18n("ERROR: Could not determine font's name.")
1404 : itsDescriptiveName
);
1406 if(1==itsSizes
.size())
1407 title
=i18np("%2 [1 pixel]", "%2 [%1 pixels]", itsSizes
[0], title
);
1409 drawString(xftDraw
, xftCol
, title
, x
, y
, h
);
1412 drawLine(pix
, x
, y
, w
-1, 1);
1416 void CFcEngine::addFontFile(const QString
&file
)
1418 if(!itsAddedFiles
.contains(file
))
1420 FcInitReinitialize();
1421 FcConfigAppFontAddFile(FcConfigGetCurrent(), (const FcChar8
*)(QFile::encodeName(file
).data()));
1422 itsAddedFiles
.append(file
);
1426 void CFcEngine::reinit()
1430 FcInitLoadConfigAndFonts();
1431 FcInitReinitialize();