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 K_GLOBAL_STATIC(CFcEngine
, theInstance
)
53 const int CFcEngine::constScalableSizes
[]={8, 10, 12, 24, 36, 48, 64, 72, 96, 0 };
54 const int CFcEngine::constDefaultAlphaSize
=24;
56 QColor
CFcEngine::theirBgndCol(Qt::white
);
57 QColor
CFcEngine::theirTextCol(Qt::black
);
59 static int fcToQtWeight(int weight
)
65 case FC_WEIGHT_EXTRALIGHT
:
66 return QFont::Light
>>1;
70 case FC_WEIGHT_REGULAR
:
72 case FC_WEIGHT_MEDIUM
:
73 #ifdef KFI_HAVE_MEDIUM_WEIGHT
74 return (QFont::Normal
+QFont::DemiBold
)>>1;
77 case FC_WEIGHT_DEMIBOLD
:
78 return QFont::DemiBold
;
81 case FC_WEIGHT_EXTRABOLD
:
82 return (QFont::Bold
+QFont::Black
)>>1;
88 #ifndef KFI_FC_NO_WIDTHS
89 static int fcToQtWidth(int weight
)
93 case KFI_FC_WIDTH_ULTRACONDENSED
:
94 return QFont::UltraCondensed
;
95 case KFI_FC_WIDTH_EXTRACONDENSED
:
96 return QFont::ExtraCondensed
;
97 case KFI_FC_WIDTH_CONDENSED
:
98 return QFont::Condensed
;
99 case KFI_FC_WIDTH_SEMICONDENSED
:
100 return QFont::SemiCondensed
;
102 case KFI_FC_WIDTH_NORMAL
:
103 return QFont::Unstretched
;
104 case KFI_FC_WIDTH_SEMIEXPANDED
:
105 return QFont::SemiExpanded
;
106 case KFI_FC_WIDTH_EXPANDED
:
107 return QFont::Expanded
;
108 case KFI_FC_WIDTH_EXTRAEXPANDED
:
109 return QFont::ExtraExpanded
;
110 case KFI_FC_WIDTH_ULTRAEXPANDED
:
111 return QFont::UltraExpanded
;
116 static bool fcToQtSlant(int slant
)
118 return FC_SLANT_ROMAN
==slant
? false : true;
121 static void drawText(QPainter
&painter
, int x
, int y
, int width
, const QString
&str
)
124 bool addedElipses
=false;
127 while(s
.length()>3 && painter
.fontMetrics().size(0, s
).width()>width
)
131 s
.remove(s
.length()-2, 2);
136 s
.remove(s
.length()-4, 1);
138 painter
.drawText(x
, y
, s
);
141 inline bool equal(double d1
, double d2
)
143 return (fabs(d1
- d2
) < 0.0001);
146 inline bool equalWeight(int a
, int b
)
148 return a
==b
|| FC::weight(a
)==FC::weight(b
);
151 #ifndef KFI_FC_NO_WIDTHS
152 inline bool equalWidth(int a
, int b
)
154 return a
==b
|| FC::width(a
)==FC::width(b
);
158 inline bool equalSlant(int a
, int b
)
160 return a
==b
|| FC::slant(a
)==FC::slant(b
);
163 static bool drawChar32Centre(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, quint32 ch
, int w
, int h
)
165 if(XftCharExists(QX11Info::display(), xftFont
, ch
))
169 XftTextExtents32(QX11Info::display(), xftFont
, &ch
, 1, &extents
);
171 int rx(((w
-extents
.width
)/2)+extents
.x
),
172 ry(((h
-extents
.height
)/2)+(extents
.y
));
174 XftDrawString32(xftDraw
, xftCol
, xftFont
, rx
, ry
, &ch
, 1);
181 static const int constBorder
=2;
183 static bool drawChar32(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, quint32 ch
,
184 int &x
, int &y
, int w
, int h
, int fontHeight
, int offset
, QRect
&r
)
187 if(XftCharExists(QX11Info::display(), xftFont
, ch
))
191 XftTextExtents32(QX11Info::display(), xftFont
, &ch
, 1, &extents
);
196 if(x
+extents
.width
+constBorder
>w
)
201 y
+=fontHeight
+constBorder
;
206 r
=QRect(x
-extents
.x
, y
-extents
.y
, extents
.width
+constBorder
, extents
.height
);
208 XftDrawString32(xftDraw
, xftCol
, xftFont
, x
, y
, &ch
, 1);
209 x
+=extents
.xOff
+constBorder
;
218 static bool drawString(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, const QString
&text
,
219 int x
, int &y
, int h
, int offset
)
222 const FcChar16
*str
=(FcChar16
*)(text
.utf16());
224 XftTextExtents16(QX11Info::display(), xftFont
, str
, text
.length(), &extents
);
225 if(y
+extents
.height
<h
)
226 XftDrawString16(xftDraw
, xftCol
, xftFont
, x
, y
+extents
.y
, str
, text
.length());
229 y
+=extents
.height
+offset
;
235 static bool drawGlyph(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, FT_UInt i
,
236 int &x
, int &y
, int w
, int h
, int fontHeight
, int offset
, bool oneLine
,
241 XftGlyphExtents(QX11Info::display(), xftFont
, &i
, 1, &extents
);
243 if(x
+extents
.width
+2>w
)
254 XftDrawGlyphs(xftDraw
, xftCol
, xftFont
, x
, y
, &i
, 1);
256 r
=QRect(x
-extents
.x
, y
-extents
.y
, extents
.width
+constBorder
, extents
.height
);
263 static bool drawAllGlyphs(XftDraw
*xftDraw
, XftFont
*xftFont
, XftColor
*xftCol
, int fontHeight
,
264 int &x
, int &y
, int w
, int h
, int offset
, bool oneLine
=false, int max
=-1,
271 FT_Face face
=XftLockFace(xftFont
);
275 int space(fontHeight
/10),
284 for(int i
=1; i
<face
->num_glyphs
&& y
<h
; ++i
)
285 if(drawGlyph(xftDraw
, xftFont
, xftCol
, i
, x
, y
, w
, h
, fontHeight
, offset
, oneLine
, r
))
293 *used
=used
->united(r
);
294 if(max
>0 && ++drawn
>=max
)
303 XftUnlockFace(xftFont
);
310 inline int point2Pixel(int point
)
312 return (point
*QX11Info::appDpiX()+36)/72;
315 static bool hasStr(XftFont
*font
, QString
&str
)
317 unsigned int slen
=str
.length(),
320 for(ch
=0; ch
<slen
; ++ch
)
321 if(!FcCharSetHasChar(font
->charset
, str
[ch
].unicode()))
326 static QString
usableStr(XftFont
*font
, QString
&str
)
328 unsigned int slen
=str
.length(),
332 for(ch
=0; ch
<slen
; ++ch
)
333 if(FcCharSetHasChar(font
->charset
, str
[ch
].unicode()))
338 CFcEngine
* CFcEngine::instance()
343 CFcEngine::CFcEngine()
347 itsPreviewString(getDefaultPreviewString())
352 CFcEngine::~CFcEngine()
354 // Clear any fonts that may have been added...
355 FcConfigAppFontClear(FcConfigGetCurrent());
358 void CFcEngine::readConfig(KConfig
&cfg
)
360 cfg
.group(KFI_PREVIEW_GROUP
).readEntry(KFI_PREVIEW_STRING_KEY
, getDefaultPreviewString());
363 void CFcEngine::writeConfig(KConfig
&cfg
)
365 cfg
.group(KFI_PREVIEW_GROUP
).writeEntry(KFI_PREVIEW_STRING_KEY
, itsPreviewString
);
368 const QString
& CFcEngine::getName(const KUrl
&url
, int faceNo
)
370 if(url
!=itsLastUrl
|| faceNo
!=itsIndex
)
371 parseUrl(url
, faceNo
);
373 return itsDescriptiveName
;
376 bool CFcEngine::drawPreview(const QString
&item
, QPixmap
&pix
, int h
, quint32 style
, int face
)
382 static const int constOffset
=2;
383 static const int constInitialWidth
=1536;
387 if(QChar('/')==item
[0]) // Then add to fontconfig's list, so that Xft can display it...
389 KUrl
url("file://"+item
);
391 ok
=parseUrl(url
, face
);
396 parseName(item
, style
);
408 // Calculate size of text...
409 int fSize
=((int)(h
*0.75))-2,
411 bool needResize(false);
413 if(!itsScalable
) // Then need to get nearest size...
417 for(int s
=0; s
<itsSizes
.size(); ++s
)
418 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
424 pix
=QPixmap(constInitialWidth
, bSize
+8);
425 pix
.fill(theirBgndCol
);
429 offset
=(fSize
-bSize
)/2;
434 pix
=QPixmap(constInitialWidth
, h
);
435 pix
.fill(theirBgndCol
);
438 const QX11Info
&x11Info(pix
.x11Info());
439 XRenderColor xrenderCol
;
442 xrenderCol
.red
=theirTextCol
.red()<<8;
443 xrenderCol
.green
=theirTextCol
.green()<<8;
444 xrenderCol
.blue
=theirTextCol
.blue()<<8;
445 xrenderCol
.alpha
=0xffff;
447 XftColorAllocValue(QX11Info::display(), DefaultVisual(QX11Info::display(),
449 DefaultColormap(QX11Info::display(), x11Info
.screen()),
450 &xrenderCol
, &xftCol
);
452 XftDraw
*xftDraw
=XftDrawCreate(QX11Info::display(), (Pixmap
)(pix
.handle()),
453 (Visual
*)(x11Info
.visual()), x11Info
.colormap());
457 XftFont
*xftFont
=NULL
;
458 QString
text(itsPreviewString
);
461 // Calculate size of text...
462 int fSize
=((int)(h
*0.75))-2,
465 if(!itsScalable
) // Then need to get nearest size...
469 for(int s
=0; s
<itsSizes
.size(); ++s
)
470 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
478 kDebug() << "use size:" << bSize+8;
480 pix=QPixmap(constInitialWidth, bSize+8);
482 pix.fill(theirBgndCol);
487 offset
=(fSize
-bSize
)/2;
490 xftFont
=getFont(fSize
);
494 if(hasStr(xftFont
, text
) || hasStr(xftFont
, text
=text
.toUpper()) ||
495 hasStr(xftFont
, text
=text
.toLower()))
498 const FcChar16
*str
=(FcChar16
*)(text
.utf16());
500 XftTextExtents16(QX11Info::display(), xftFont
, str
, text
.length(),
503 int y
=((h
-extents
.y
)/2)+extents
.y
;
504 XftDrawString16(xftDraw
, &xftCol
, xftFont
, constOffset
, y
, str
,
507 pix
=pix
.scaledToHeight(h
);
508 pix
= pix
.copy(0, 0, extents
.width
+(2*constOffset
)<constInitialWidth
509 ? extents
.width
+(2*constOffset
)
516 FT_Face face
=XftLockFace(xftFont
);
523 for(FT_UInt i
=1; i
<(unsigned int)face
->num_glyphs
&&
524 i
<(unsigned int)text
.length()+1; ++i
)
528 XftGlyphExtents(QX11Info::display(), xftFont
, &i
, 1,
531 if(x
+extents
.width
+2>constInitialWidth
) // Only want 1 line
534 XftDrawGlyphs(xftDraw
, &xftCol
, xftFont
, x
, y
, &i
, 1);
538 pix
=pix
.scaledToHeight(h
);
539 pix
= pix
.copy(0, 0, x
+constOffset
<constInitialWidth
543 XftUnlockFace(xftFont
);
556 bool CFcEngine::draw(const KUrl
&url
, int w
, int h
, QPixmap
&pix
, int faceNo
, bool thumb
,
557 const QList
<TRange
> &range
, QList
<TChar
> *chars
, const QString
&name
, quint32 style
)
564 if((url
==itsLastUrl
&& faceNo
==itsIndex
) ||
565 (!name
.isEmpty() && parseName(name
, style
, url
)) ||
566 parseUrl(url
, faceNo
))
571 if(!itsInstalled
) // Then add to fontconfig's list, so that Xft can display it...
572 addFontFile(itsFileName
);
575 // We allow kio_thumbnail to cache our thumbs. Normal is 128x128, and large is 256x256
576 // ...if kio_thumbnail asks us for a bigger size, then its probably the file info dialog, in
577 // which case treat it as a normal preview...
578 if(thumb
&& (h
>256 || w
!=h
))
590 pix
=thumb
&& itsScalable
? QPixmap(w
*4, h
*2) : QPixmap(w
, h
);
591 pix
.fill(theirBgndCol
);
593 QPainter
painter(&pix
);
597 const QX11Info
&x11Info(pix
.x11Info());
598 XRenderColor xrenderCol
;
601 xrenderCol
.red
=theirTextCol
.red()<<8;
602 xrenderCol
.green
=theirTextCol
.green()<<8;
603 xrenderCol
.blue
=theirTextCol
.blue()<<8;
604 xrenderCol
.alpha
=0xffff;
605 XftColorAllocValue(QX11Info::display(), DefaultVisual(QX11Info::display(),
607 DefaultColormap(QX11Info::display(), x11Info
.screen()),
608 &xrenderCol
, &xftCol
);
610 XftDraw
*xftDraw
=XftDrawCreate(QX11Info::display(), (Pixmap
)(pix
.handle()),
611 (Visual
*)(x11Info
.visual()), x11Info
.colormap());
615 XftFont
*xftFont
=NULL
;
619 QString
text(itsScalable
620 ? i18nc("First letter of the alphabet (in upper then lower case)", "Aa")
621 : i18nc("All letters of the alphabet (in upper/lower case pairs), followed by numbers",
622 "AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789"));
625 // Calculate size of text...
626 int fSize
= h
-(offset
*2);
628 if(!itsScalable
) // Then need to get nearest size...
632 for(int s
=0; s
<itsSizes
.size(); ++s
)
633 if (itsSizes
[s
]<=fSize
|| 0==bSize
)
638 xftFont
=getFont(fSize
);
642 QString
valid(usableStr(xftFont
, text
));
643 QRect
used(0, 0, 0, 0);
650 if(valid
.length()!=text
.length())
652 text
=getPunctuation().mid(1, 2); // '1' '2'
653 valid
=usableStr(xftFont
, text
);
657 if(valid
.length()<(text
.length()/2))
658 for(int i
=0; i
<3; ++i
)
660 text
=0==i
? getUppercaseLetters() : 1==i
? getLowercaseLetters() : getPunctuation();
661 valid
=usableStr(xftFont
, text
);
663 if(valid
.length()>=(text
.length()/2))
668 ? valid
.length()!=text
.length()
669 : valid
.length()<(text
.length()/2))
670 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fSize
, x
, y
, pix
.width(), pix
.height(), offset
, true,
671 itsScalable
? 4 : -1, itsScalable
? &used
: NULL
);
674 QVector
<uint
> ucs4(valid
.toUcs4());
677 for(int ch
=0; ch
<ucs4
.size(); ++ch
) // Display char by char so wraps...
678 if(drawChar32(xftDraw
, xftFont
, &xftCol
, ucs4
[ch
], x
, y
, pix
.width(), pix
.height(), fSize
,
692 if(itsScalable
&& !used
.isEmpty())
694 used
.adjust(-1*offset
, -1*offset
, offset
, offset
);
696 if(used
.width()<pix
.width() && used
.height()<pix
.height())
701 else if(0==range
.count())
703 QString
lowercase(getLowercaseLetters()),
704 uppercase(getUppercaseLetters()),
705 punctuation(getPunctuation());
707 drawName(painter
, x
, y
, w
, offset
);
709 xftFont
=getFont(itsAlphaSize
);
712 bool lc(hasStr(xftFont
, lowercase
)),
713 uc(hasStr(xftFont
, uppercase
)),
714 drawGlyphs
=!lc
&& !uc
;
720 QString
validPunc(usableStr(xftFont
, punctuation
));
721 bool punc(validPunc
.length()>=(punctuation
.length()/2));
724 drawString(xftDraw
, xftFont
, &xftCol
, lowercase
, x
, y
, h
, offset
);
726 drawString(xftDraw
, xftFont
, &xftCol
, uppercase
, x
, y
, h
, offset
);
728 drawString(xftDraw
, xftFont
, &xftCol
, validPunc
, x
, y
, h
, offset
);
729 XftFontClose(QX11Info::display(), xftFont
);
731 painter
.drawLine(offset
, y
, w
-(offset
+1), y
);
735 QString
previewString(getPreviewString());
740 previewString
=previewString
.toUpper();
742 previewString
=previewString
.toLower();
745 for(int s
=0; s
<itsSizes
.size(); ++s
)
746 if((xftFont
=getFont(itsSizes
[s
])))
748 int fontHeight
=xftFont
->ascent
+xftFont
->descent
;
752 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fontHeight
, x
, y
, w
, h
, offset
,
755 drawString(xftDraw
, xftFont
, &xftCol
, previewString
, x
, y
, h
,
757 XftFontClose(QX11Info::display(), xftFont
);
761 painter
.setPen(theirBgndCol
);
762 for(int l
=0; l
<offset
; ++l
)
763 painter
.drawLine((w
-1)-l
, 0, (w
-1)-l
, h
);
765 else if(1==range
.count() && (range
.first().null() || 0==range
.first().to
))
767 if(range
.first().null())
769 drawName(painter
, x
, y
, w
, offset
);
771 if((xftFont
=getFont(itsAlphaSize
)))
773 int fontHeight
=xftFont
->ascent
+xftFont
->descent
;
776 drawAllGlyphs(xftDraw
, xftFont
, &xftCol
, fontHeight
, x
, y
, w
, h
, offset
);
778 XftFontClose(QX11Info::display(), xftFont
);
781 else if((xftFont
=getFont(itsAlphaSize
*2)))
784 rv
=drawChar32Centre(xftDraw
, xftFont
, &xftCol
, (*(range
.begin())).from
,
785 pix
.width(), pix
.height());
790 QList
<TRange
>::ConstIterator
it(range
.begin()),
793 if((xftFont
=getFont(itsAlphaSize
)))
796 drawName(painter
, x
, y
, w
, offset
);
800 int fontHeight
=xftFont
->ascent
+xftFont
->descent
, xOrig(x
), yOrig(y
);
803 for(it
=range
.begin(); it
!=end
&& !stop
; ++it
)
804 for(quint32 c
=(*it
).from
; c
<=(*it
).to
&& !stop
; ++c
)
806 if(drawChar32(xftDraw
, xftFont
, &xftCol
, c
, x
, y
, w
, h
, fontHeight
,
809 if(chars
&& !r
.isEmpty())
810 chars
->append(TChar(r
, c
));
816 if(x
==xOrig
&& y
==yOrig
)
818 // No characters found within the selected range...
819 painter
.setFont(KGlobalSettings::generalFont());
820 painter
.setPen(theirTextCol
);
821 drawText(painter
, x
, y
, w
-offset
, i18n("No characters found."));
826 XftDrawDestroy(xftDraw
);
834 QString
CFcEngine::getDefaultPreviewString()
836 return i18nc("A sentence that uses all of the letters of the alphabet",
837 "The quick brown fox jumps over the lazy dog");
840 QString
CFcEngine::getUppercaseLetters()
842 return i18nc("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
845 QString
CFcEngine::getLowercaseLetters()
847 return i18nc("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz");
850 QString
CFcEngine::getPunctuation()
852 return i18nc("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]"); //krazy:exclude=i18ncheckarg
855 #ifdef KFI_USE_TRANSLATED_FAMILY_NAME
857 // Try to get the 'string' that matches the users KDE locale..
858 QString
CFcEngine::getFcLangString(FcPattern
*pat
, const char *val
, const char *valLang
)
861 QStringList kdeLangs
=KGlobal::locale()->languageList(),
863 QStringList::ConstIterator
it(kdeLangs
.begin()),
866 // Create list of langs that this font's 'val' is encoded in...
867 for(int i
=0; true; ++i
)
869 QString lang
=getFcString(pat
, valLang
, i
);
874 fontLangs
.append(lang
);
877 // Now go through the user's KDE locale, and try to find a font match...
880 int index
=fontLangs
.findIndex(*it
);
884 rv
=getFcString(pat
, val
, index
);
892 rv
=getFcString(pat
, val
, 0);
897 bool CFcEngine::getInfo(const KUrl
&url
, int faceNo
, Misc::TFont
&info
)
899 if((url
==itsLastUrl
&& faceNo
==itsIndex
) || parseUrl(url
, faceNo
))
901 if(url
.isLocalFile() || Misc::isHidden(url
))
905 if(-1==(pos
=itsDescriptiveName
.indexOf(", "))) // No style information...
906 info
.family
=itsDescriptiveName
;
908 info
.family
=itsDescriptiveName
.left(pos
);
912 info
.styleInfo
=styleVal();
919 QFont
CFcEngine::getQFont(const QString
&family
, quint32 style
, int size
)
925 FC::decomposeStyleVal(style
, weight
, width
, slant
);
927 QFont
font(family
, size
, fcToQtWeight(weight
), fcToQtSlant(slant
));
929 #ifndef KFI_FC_NO_WIDTHS
930 font
.setStretch(fcToQtWidth(width
));
935 bool CFcEngine::parseUrl(const KUrl
&url
, int faceNo
)
938 kDebug() << url
.prettyUrl() << ' ' << faceNo
;
951 // fonts:/System/times.ttf
952 // file:/home/wibble/hmm.ttf
954 if(KFI_KIO_FONTS_PROTOCOL
==url
.protocol())
956 bool hidden
=Misc::isHidden(url
);
957 KIO::UDSEntry udsEntry
;
959 quint32 style
=KFI_NO_STYLE_INFO
;
961 if(KIO::NetAccess::stat(url
, udsEntry
, NULL
)) // Need to stat the url to get its font name...
963 name
=udsEntry
.stringValue((uint
)KIO::UDSEntry::UDS_NAME
);
964 itsFileName
=udsEntry
.stringValue((uint
)UDS_EXTRA_FILE_NAME
);
965 style
=udsEntry
.numberValue((uint
)UDS_EXTRA_FC_STYLE
);
966 itsIndex
=Misc::getIntQueryVal(KUrl(udsEntry
.stringValue((uint
)KIO::UDSEntry::UDS_URL
)),
969 kDebug() << "Stated fonts:/ url, name:" << name
<< " itsFileName:" << itsFileName
970 << " style:" << style
<< " itsIndex:" << itsIndex
;
974 kDebug() << "isHidden:" << hidden
;
983 if(!parseUrl(KUrl(name
), faceNo
))
988 parseName(name
, style
);
995 else if(url
.isLocalFile() || url
.protocol().isEmpty())
997 // Now lets see if its from the thumbnail job! if so, then file should contain either:
999 // b. FontName followed by style info
1000 QFile
file(url
.path());
1001 bool isThumbnailUrl
=false;
1003 if(file
.size()<2048 && file
.open(QIODevice::ReadOnly
)) // Data should be less than 2k, and fonts usually above!
1005 QTextStream
stream(&file
);
1006 QString
line1(stream
.readLine()),
1007 line2(stream
.readLine());
1010 isThumbnailUrl
=(0==line1
.indexOf(KFI_KIO_FONTS_PROTOCOL
) ||
1011 0==line1
.indexOf("file:/")) &&
1012 parseUrl(KUrl(line1
), faceNo
);
1013 else if(0==line1
.indexOf(KFI_PATH_KEY
) && 0==line2
.indexOf(KFI_FACE_KEY
))
1015 line1
=line1
.mid(strlen(KFI_PATH_KEY
));
1016 line2
=line2
.mid(strlen(KFI_FACE_KEY
));
1018 if(!line1
.isEmpty() && !line2
.isEmpty())
1021 int face
=line2
.toInt(&ok
);
1023 isThumbnailUrl
=ok
&& parseUrl(line1
, face
<0 ? 0 : face
);
1026 else if(0==line1
.indexOf(KFI_NAME_KEY
) && 0==line2
.indexOf(KFI_STYLE_KEY
))
1028 line1
=line1
.mid(strlen(KFI_NAME_KEY
));
1029 line2
=line2
.mid(strlen(KFI_STYLE_KEY
));
1031 if(!line1
.isEmpty() && !line2
.isEmpty())
1034 quint32 style
=line2
.toULong(&ok
);
1036 itsInstalled
=isThumbnailUrl
=ok
&& parseName(line1
, style
);
1040 QString
line3(stream
.readLine()),
1041 line4(stream
.readLine());
1043 if(0==line3
.indexOf(KFI_PATH_KEY
) && 0==line4
.indexOf(KFI_FACE_KEY
))
1045 line3
=line3
.mid(strlen(KFI_PATH_KEY
));
1046 line4
=line4
.mid(strlen(KFI_FACE_KEY
));
1048 if(!line1
.isEmpty() && !line2
.isEmpty())
1051 int face
=line4
.toInt(&ok
);
1066 if(!isThumbnailUrl
) // Its not a thumbnail, so read the real font file...
1068 itsName
=itsFileName
=url
.path();
1071 FcPattern
*pat
=FcFreeTypeQuery((const FcChar8
*)(QFile::encodeName(itsFileName
).data()),
1072 faceNo
, NULL
, &count
);
1074 itsWeight
=FC_WEIGHT_REGULAR
;
1075 itsWidth
=KFI_FC_WIDTH_NORMAL
;
1076 itsSlant
=FC_SLANT_ROMAN
;
1080 FcPatternGetInteger(pat
, FC_WEIGHT
, 0, &itsWeight
);
1081 FcPatternGetInteger(pat
, FC_SLANT
, 0, &itsSlant
);
1082 #ifndef KFI_FC_NO_WIDTHS
1083 FcPatternGetInteger(pat
, FC_WIDTH
, 0, &itsWidth
);
1085 itsDescriptiveName
=FC::createName(pat
, itsWeight
, itsWidth
, itsSlant
);
1086 FcPatternDestroy(pat
);
1090 itsDescriptiveName
.clear();
1105 bool CFcEngine::parseName(const QString
&name
, quint32 style
, const KUrl
&url
)
1111 itsDescriptiveName
=name
;
1113 if(-1==(pos
=name
.indexOf(", "))) // No style information...
1115 if(KFI_NO_STYLE_INFO
!=style
)
1116 FC::decomposeStyleVal(style
, itsWeight
, itsWidth
, itsSlant
);
1119 itsWeight
=FC_WEIGHT_REGULAR
;
1120 itsWidth
=KFI_FC_WIDTH_NORMAL
;
1121 itsSlant
=FC_SLANT_ROMAN
;
1127 if(KFI_NO_STYLE_INFO
!=style
)
1128 FC::decomposeStyleVal(style
, itsWeight
, itsWidth
, itsSlant
);
1131 QString
style(name
.mid(pos
+2));
1133 itsWeight
=FC::strToWeight(style
, style
);
1134 itsWidth
=FC::strToWidth(style
, style
);
1135 itsSlant
=FC::strToSlant(style
);
1137 itsName
=name
.left(pos
);
1140 itsFileName
.clear();
1142 itsIndex
=0; // Doesn't matter, as we're gonna use font name!
1147 XftFont
* CFcEngine::queryFont()
1149 static const int constQuerySize
=8;
1155 XftFont
*f
=getFont(constQuerySize
);
1157 if(!isCorrect(f
, true))
1159 XftFontClose(QX11Info::display(), f
);
1163 if(itsInstalled
&& !f
)
1165 // Perhaps its a newly installed font? If so try re-initialising fontconfig...
1169 f
=getFont(constQuerySize
);
1171 // This time don't bother checking family - we've re-inited fc anyway, so things should be
1172 // up to date... And for "Symbol" Fc returns "Standard Symbols L", so wont match anyway!
1173 if(f
&& !isCorrect(f
, false))
1175 XftFontClose(QX11Info::display(), f
);
1180 kDebug() << "ret" << (int)f
;
1185 XftFont
* CFcEngine::getFont(int size
)
1190 kDebug() << QString(itsInstalled
? itsName
: itsFileName
) << ' ' << size
;
1195 #ifndef KFI_FC_NO_WIDTHS
1196 if(KFI_NULL_SETTING
!=itsWidth
)
1197 f
=XftFontOpen(QX11Info::display(), 0,
1198 FC_FAMILY
, FcTypeString
, (const FcChar8
*)(itsName
.toUtf8().data()),
1199 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1200 FC_SLANT
, FcTypeInteger
, itsSlant
,
1201 FC_WIDTH
, FcTypeInteger
, itsWidth
,
1202 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1206 f
=XftFontOpen(QX11Info::display(), 0,
1207 FC_FAMILY
, FcTypeString
, (const FcChar8
*)(itsName
.toUtf8().data()),
1208 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1209 FC_SLANT
, FcTypeInteger
, itsSlant
,
1210 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1215 FcPattern
*pattern
= FcPatternBuild(NULL
,
1216 FC_FILE
, FcTypeString
,
1217 QFile::encodeName(itsFileName
).data(),
1218 FC_INDEX
, FcTypeInteger
, itsIndex
<0 ? 0 : itsIndex
,
1219 FC_PIXEL_SIZE
, FcTypeDouble
, (double)size
,
1221 f
=XftFontOpenPattern(QX11Info::display(), pattern
);
1225 kDebug() << "ret: " << (int)f
;
1231 bool CFcEngine::isCorrect(XftFont
*f
, bool checkFamily
)
1238 QTextStream
s(&xxx
);
1244 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WEIGHT
, 0, &iv
))
1245 s
<< iv
<< '/' << itsWeight
;
1250 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_SLANT
, 0, &iv
))
1251 s
<< iv
<< '/' << itsSlant
;
1256 if(FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WIDTH
, 0, &iv
))
1257 s
<< iv
<< '/' << itsWidth
;
1263 if(FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FAMILY
, 0, &str
) && str
)
1264 s
<< QString::fromUtf8((char *)str
) << '/' << itsName
;
1271 s
<< "NOT Installed... ";
1275 kDebug() << "isCorrect? " << xxx
;
1281 ? FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WEIGHT
, 0, &iv
) &&
1282 equalWeight(iv
, itsWeight
) &&
1283 FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_SLANT
, 0, &iv
) &&
1284 equalSlant(iv
, itsSlant
) &&
1285 #ifndef KFI_FC_NO_WIDTHS
1286 (KFI_NULL_SETTING
==itsWidth
||
1287 (FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_WIDTH
, 0, &iv
) &&
1288 equalWidth(iv
, itsWidth
))) &&
1291 (FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FAMILY
, 0, &str
) && str
&&
1292 QString::fromUtf8((char *)str
)==itsName
))
1293 : (itsIndex
<0 || (FcResultMatch
==FcPatternGetInteger(f
->pattern
, FC_INDEX
, 0, &iv
) && itsIndex
==iv
)) &&
1294 FcResultMatch
==FcPatternGetString(f
->pattern
, FC_FILE
, 0, &str
) && str
&&
1295 QString::fromUtf8((char *)str
)==itsFileName
1299 void CFcEngine::getSizes()
1305 XftFont
*f
=queryFont();
1314 bool gotSizes
=false;
1319 if(FcResultMatch
!=FcPatternGetBool(f
->pattern
, FC_SCALABLE
, 0, &itsScalable
))
1320 itsScalable
=FcFalse
;
1324 FcPattern
*pat
=NULL
;
1325 FcObjectSet
*os
= FcObjectSetBuild(FC_PIXEL_SIZE
, (void*)0);
1326 #ifndef KFI_FC_NO_WIDTHS
1327 if(KFI_NULL_SETTING
!=itsWidth
)
1328 pat
=FcPatternBuild(NULL
,
1329 FC_FAMILY
, FcTypeString
,
1330 (const FcChar8
*)(itsName
.toUtf8().data()),
1331 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1332 FC_SLANT
, FcTypeInteger
, itsSlant
,
1333 FC_WIDTH
, FcTypeInteger
, itsWidth
,
1337 pat
=FcPatternBuild(NULL
,
1338 FC_FAMILY
, FcTypeString
,
1339 (const FcChar8
*)(itsName
.toUtf8().data()),
1340 FC_WEIGHT
, FcTypeInteger
, itsWeight
,
1341 FC_SLANT
, FcTypeInteger
, itsSlant
,
1344 FcFontSet
*set
=FcFontList(0, pat
, os
);
1346 FcPatternDestroy(pat
);
1347 FcObjectSetDestroy(os
);
1352 kDebug() << "got fixed sizes: " << set
->nfont
;
1354 itsSizes
.reserve(set
->nfont
);
1355 for (int i
= 0; i
< set
->nfont
; i
++)
1356 if(FcResultMatch
==FcPatternGetDouble(set
->fonts
[i
], FC_PIXEL_SIZE
, 0, &px
))
1359 itsSizes
.push_back((int)px
);
1362 kDebug() << "got fixed: " << px
;
1364 if (px
<=constDefaultAlphaSize
)
1365 itsAlphaSize
=(int)px
;
1367 FcFontSetDestroy(set
);
1373 FT_Face face
=XftLockFace(f
);
1377 itsIndexCount
=face
->num_faces
;
1378 if(!(itsScalable
=FT_IS_SCALABLE(face
)))
1380 int numSizes
=face
->num_fixed_sizes
,
1385 itsSizes
.reserve(numSizes
);
1388 kDebug() << "numSizes fixed: " << numSizes
;
1390 for (size
=0; size
<numSizes
; size
++)
1392 #if (FREETYPE_MAJOR*10000+FREETYPE_MINOR*100+FREETYPE_PATCH) >= 20105
1393 double px
=face
->available_sizes
[size
].y_ppem
>>6;
1395 double px
=face
->available_sizes
[size
].width
;
1398 kDebug() << "px: " << px
;
1400 itsSizes
.push_back((int)px
);
1402 if (px
<=constDefaultAlphaSize
)
1403 itsAlphaSize
=(int)px
;
1410 XftFontClose(QX11Info::display(), f
);
1415 itsSizes
.reserve(sizeof(constScalableSizes
)/sizeof(int));
1417 for (int i
=0; constScalableSizes
[i
]; ++i
)
1418 itsSizes
.push_back(point2Pixel(constScalableSizes
[i
]));
1419 itsAlphaSize
=constDefaultAlphaSize
;
1422 if(0==itsAlphaSize
&& itsSizes
.count())
1423 itsAlphaSize
=itsSizes
[0];
1429 void CFcEngine::drawName(QPainter
&painter
, int x
, int &y
, int w
, int offset
)
1431 QString
title(itsDescriptiveName
.isEmpty()
1432 ? i18n("ERROR: Could not determine font's name.")
1433 : itsDescriptiveName
);
1435 if(1==itsSizes
.size())
1436 title
=i18np("%2 [1 pixel]", "%2 [%1 pixels]", itsSizes
[0], title
);
1438 painter
.setFont(KGlobalSettings::generalFont());
1439 painter
.setPen(theirTextCol
);
1440 y
=painter
.fontMetrics().height();
1441 drawText(painter
, x
, y
, w
-offset
, title
);
1443 painter
.drawLine(offset
, y
, w
-(offset
+1), y
);
1447 void CFcEngine::addFontFile(const QString
&file
)
1449 if(!itsAddedFiles
.contains(file
))
1451 FcInitReinitialize();
1452 FcConfigAppFontAddFile(FcConfigGetCurrent(), (const FcChar8
*)(QFile::encodeName(file
).data()));
1453 itsAddedFiles
.append(file
);
1457 void CFcEngine::reinit()
1461 FcInitLoadConfigAndFonts();
1462 FcInitReinitialize();