add more spacing
[personal-kdebase.git] / workspace / kcontrol / kfontinst / lib / FcEngine.cpp
blobf6eca1f1e24a06e1ea50d9619099dea1d07c82ff
1 /*
2 * KFontInst - KDE Font Installer
4 * Copyright 2003-2007 Craig Drummond <craig@kde.org>
6 * ----
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 "FcEngine.h"
26 #include <QtGui/QPainter>
27 #include <QtGui/QPixmap>
28 #include <QtCore/QFile>
29 #include <QtCore/QTextStream>
30 #include <QtGui/QX11Info>
31 #include <KDE/KUrl>
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>
38 #include <math.h>
39 #include <X11/Xlib.h>
40 #include <X11/Xft/Xft.h>
41 #include <fixx11h.h>
43 //#define KFI_FC_DEBUG
45 #define KFI_PREVIEW_GROUP "KFontInst Preview Settings"
46 #define KFI_PREVIEW_STRING_KEY "String"
48 namespace KFI
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)
59 switch(weight)
61 case FC_WEIGHT_THIN:
62 return 0;
63 case FC_WEIGHT_EXTRALIGHT:
64 return QFont::Light>>1;
65 case FC_WEIGHT_LIGHT:
66 return QFont::Light;
67 default:
68 case FC_WEIGHT_REGULAR:
69 return QFont::Normal;
70 case FC_WEIGHT_MEDIUM:
71 #ifdef KFI_HAVE_MEDIUM_WEIGHT
72 return (QFont::Normal+QFont::DemiBold)>>1;
73 #endif
74 return QFont::Normal;
75 case FC_WEIGHT_DEMIBOLD:
76 return QFont::DemiBold;
77 case FC_WEIGHT_BOLD:
78 return QFont::Bold;
79 case FC_WEIGHT_EXTRABOLD:
80 return (QFont::Bold+QFont::Black)>>1;
81 case FC_WEIGHT_BLACK:
82 return QFont::Black;
86 #ifndef KFI_FC_NO_WIDTHS
87 static int fcToQtWidth(int weight)
89 switch(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;
99 default:
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;
112 #endif
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);
134 #endif
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))
145 XGlyphInfo extents;
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);
153 return true;
156 return false;
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)
164 r=QRect();
165 if(XftCharExists(QX11Info::display(), xftFont, ch))
167 XGlyphInfo extents;
169 XftTextExtents32(QX11Info::display(), xftFont, &ch, 1, &extents);
171 if(extents.x>0)
172 x+=extents.x;
174 if(x+extents.width+constBorder>w)
176 x=0;
177 if(extents.x>0)
178 x+=extents.x;
179 y+=fontHeight+constBorder;
182 if(y<h)
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;
188 return true;
190 return false;
193 return true;
196 static bool drawString(XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const QString &text,
197 int x, int &y, int h)
199 XGlyphInfo extents;
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());
205 if(extents.height>0)
207 y+=extents.height;
208 return true;
210 return false;
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(),
223 NULL);
225 if(xftFont)
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,
234 QRect &r)
236 XGlyphInfo extents;
238 XftGlyphExtents(QX11Info::display(), xftFont, &i, 1, &extents);
240 if(x+extents.width+2>w)
242 if(oneLine)
243 return false;
245 x=0;
246 y+=fontHeight+2;
249 if(y<h)
251 XftDrawGlyphs(xftDraw, xftCol, xftFont, x, y, &i, 1);
252 x+=extents.width+2;
253 r=QRect(x-extents.x, y-extents.y, extents.width+constBorder, extents.height);
255 return true;
257 return false;
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,
262 QRect *used=NULL)
264 bool rv(false);
266 if(xftFont)
268 FT_Face face=XftLockFace(xftFont);
270 if(face)
272 int space(fontHeight/10),
273 drawn(1);
274 QRect r;
276 if(!space)
277 space=1;
279 rv=true;
280 y+=fontHeight;
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))
284 if(r.height()>0)
286 if(used)
288 if(used->isEmpty())
289 *used=r;
290 else
291 *used=used->united(r);
293 if(max>0 && ++drawn>=max)
294 break;
297 else
298 break;
300 if(oneLine)
301 x=0;
302 XftUnlockFace(xftFont);
306 return rv;
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()))
321 return false;
322 return true;
325 static QString usableStr(XftFont *font, QString &str)
327 unsigned int slen=str.length(),
329 QString newStr;
331 for(ch=0; ch<slen; ++ch)
332 if(FcCharSetHasChar(font->charset, str[ch].unicode()))
333 newStr+=str[ch];
334 return newStr;
337 CFcEngine::CFcEngine(bool init)
338 : itsIndex(-1),
339 itsIndexCount(1),
340 itsAlphaSizeIndex(-1),
341 itsPreviewString(getDefaultPreviewString())
343 if(init)
344 reinit();
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)
373 bool rv=false;
375 if(!item.isEmpty())
377 static const int constOffset=2;
378 static const int constInitialWidth=1536;
380 bool ok=true;
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);
387 addFontFile(item);
389 else
391 parseName(item, style);
392 itsInstalled=true;
395 if(ok)
397 itsLastUrl=KUrl();
398 getSizes();
400 if(itsSizes.size())
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...
412 int bSize=0;
414 for(int s=0; s<itsSizes.size(); ++s)
415 if (itsSizes[s]<=fSize || 0==bSize)
416 bSize=itsSizes[s];
417 fSize=bSize;
419 if(bSize>h)
421 pix=QPixmap(constInitialWidth, bSize+8);
422 pix.fill(bgndCol);
423 needResize=true;
427 if(!needResize)
429 pix=QPixmap(constInitialWidth, h);
430 pix.fill(bgndCol);
433 const QX11Info &x11Info(pix.x11Info());
434 XRenderColor xrenderCol;
435 XftColor xftCol;
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(),
443 x11Info.screen()),
444 DefaultColormap(QX11Info::display(), x11Info.screen()),
445 &xrenderCol, &xftCol);
447 XftDraw *xftDraw=XftDrawCreateAlpha(QX11Info::display(), (Pixmap)(pix.handle()), 32);
449 if(xftDraw)
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...
460 int bSize=0;
462 for(int s=0; s<itsSizes.size(); ++s)
463 if (itsSizes[s]<=fSize || 0==bSize)
464 bSize=itsSizes[s];
465 fSize=bSize;
468 xftFont=getFont(fSize);
470 if(xftFont)
472 if(hasStr(xftFont, text) || hasStr(xftFont, text=text.toUpper()) ||
473 hasStr(xftFont, text=text.toLower()))
475 XGlyphInfo extents;
476 const FcChar16 *str=(FcChar16 *)(text.utf16());
478 XftTextExtents16(QX11Info::display(), xftFont, str, text.length(),
479 &extents);
481 int y=((h-extents.y)/2)+extents.y;
482 XftDrawString16(xftDraw, &xftCol, xftFont, constOffset, y, str,
483 text.length());
484 if(needResize)
485 pix=pix.scaledToHeight(h);
486 pix = pix.copy(0, 0, extents.width+(2*constOffset)<constInitialWidth
487 ? extents.width+(2*constOffset)
488 : constInitialWidth,
490 rv=true;
492 else
494 FT_Face face=XftLockFace(xftFont);
496 if(face)
498 int x=constOffset,
499 y=constOffset+fSize;
501 for(FT_UInt i=1; i<(unsigned int)face->num_glyphs &&
502 i<(unsigned int)text.length()+1; ++i)
504 XGlyphInfo extents;
506 XftGlyphExtents(QX11Info::display(), xftFont, &i, 1,
507 &extents);
509 if(x+extents.width+2>constInitialWidth) // Only want 1 line
510 break;
512 XftDrawGlyphs(xftDraw, &xftCol, xftFont, x, y, &i, 1);
513 x+=extents.width+2;
515 if(needResize)
516 pix=pix.scaledToHeight(h);
517 pix = pix.copy(0, 0, x+constOffset<constInitialWidth
518 ? x+constOffset
519 : constInitialWidth,
521 XftUnlockFace(xftFont);
522 rv=true;
531 return rv;
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)
537 bool rv=false;
539 if(chars)
540 chars->clear();
542 if((url==itsLastUrl && faceNo==itsIndex) ||
543 (!name.isEmpty() && parseName(name, style, url)) ||
544 parseUrl(url, faceNo))
546 if(!name.isEmpty())
547 itsInstalled=true;
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))
557 thumb=false;
559 int x=0, y=0;
561 getSizes();
563 pix=thumb && itsScalable ? QPixmap(w*4, h*2) : QPixmap(w, h);
565 QColor bgndCol(Qt::white);
567 bgndCol.setAlphaF(0.0);
568 pix.fill(bgndCol);
570 if(itsSizes.size())
572 const QX11Info &x11Info(pix.x11Info());
573 XRenderColor xrenderCol;
574 XftColor xftCol;
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(),
581 x11Info.screen()),
582 DefaultColormap(QX11Info::display(), x11Info.screen()),
583 &xrenderCol, &xftCol);
585 XftDraw *xftDraw=XftDrawCreateAlpha(QX11Info::display(), (Pixmap)(pix.handle()), 32);
587 if(xftDraw)
589 XftFont *xftFont=NULL;
591 if(thumb)
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...
600 int fSize=h;
602 if(!itsScalable) // Then need to get nearest size...
604 int bSize=0;
606 for(int s=0; s<itsSizes.size(); ++s)
607 if (itsSizes[s]<=fSize || 0==bSize)
608 bSize=itsSizes[s];
609 fSize=bSize;
612 xftFont=getFont(fSize);
614 if(xftFont)
616 QString valid(usableStr(xftFont, text));
617 QRect used(0, 0, 0, 0);
619 y=fSize;
620 rv=true;
622 if(itsScalable)
624 if(valid.length()!=text.length())
626 text=getPunctuation().mid(1, 2); // '1' '2'
627 valid=usableStr(xftFont, text);
630 else
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))
638 break;
641 if(itsScalable
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);
646 else
648 QVector<uint> ucs4(valid.toUcs4());
649 QRect r;
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))
654 if(used.isEmpty())
655 used=r;
656 else
657 used=used.united(r);
659 else
660 break;
663 if(itsScalable && !used.isEmpty() && used.width()<pix.width() && used.height()<pix.height())
664 pix=pix.copy(used);
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());
676 if(xftFont)
678 bool lc(hasStr(xftFont, lowercase)),
679 uc(hasStr(xftFont, uppercase)),
680 drawGlyphs=!lc && !uc;
682 if(drawGlyphs)
683 y-=8;
684 else
686 QString validPunc(usableStr(xftFont, punctuation));
687 bool punc(validPunc.length()>=(punctuation.length()/2));
689 if(lc)
690 drawString(xftDraw, xftFont, &xftCol, lowercase, x, y, h);
691 if(uc)
692 drawString(xftDraw, xftFont, &xftCol, uppercase, x, y, h);
693 if(punc)
694 drawString(xftDraw, xftFont, &xftCol, validPunc, x, y, h);
695 XftFontClose(QX11Info::display(), xftFont);
696 if(lc || uc || punc)
697 drawLine(pix, 0, y+2, w-1, 1);
698 y+=8;
701 QString previewString(getPreviewString());
703 if(!drawGlyphs)
705 if(!lc && uc)
706 previewString=previewString.toUpper();
707 if(!uc && lc)
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;
716 rv=true;
717 if(drawGlyphs)
718 drawAllGlyphs(xftDraw, xftFont, &xftCol, fontHeight, x, y, w, h,
719 itsSizes.count()>1);
720 else
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;
736 y-=8;
737 drawAllGlyphs(xftDraw, xftFont, &xftCol, fontHeight, x, y, w, h, 0);
738 rv=true;
739 XftFontClose(QX11Info::display(), xftFont);
742 else if((xftFont=getFont(alphaSize()*2)))
744 QRect r;
745 rv=drawChar32Centre(xftDraw, xftFont, &xftCol, (*(range.begin())).from,
746 pix.width(), pix.height());
749 else
751 QList<TRange>::ConstIterator it(range.begin()),
752 end(range.end());
754 if((xftFont=getFont(alphaSize())))
756 rv=true;
757 drawName(pix, xftDraw, &xftCol, x, y, w, h);
758 y+=alphaSize();
760 bool stop=false;
761 int fontHeight=xftFont->ascent+xftFont->descent, xOrig(x), yOrig(y);
762 QRect r;
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));
772 else
773 stop=true;
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);
789 return rv;
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)
818 QString rv;
819 QStringList kdeLangs=KGlobal::locale()->languageList(),
820 fontLangs;
821 QStringList::ConstIterator it(kdeLangs.begin()),
822 end(kdeLangs.end());
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);
829 if(lang.isEmpty())
830 break;
831 else
832 fontLangs.append(lang);
835 // Now go through the user's KDE locale, and try to find a font match...
836 for(; it!=end; ++it)
838 int index=fontLangs.findIndex(*it);
840 if(-1!=index)
842 rv=getFcString(pat, val, index);
844 if(!rv.isEmpty())
845 break;
849 if(rv.isEmpty())
850 rv=getFcString(pat, val, 0);
851 return rv;
853 #endif
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))
861 int pos;
863 if(-1==(pos=itsDescriptiveName.indexOf(", "))) // No style information...
864 info.family=itsDescriptiveName;
865 else
866 info.family=itsDescriptiveName.left(pos);
868 else
869 info.family=itsName;
870 info.styleInfo=styleVal();
871 return true;
874 return false;
877 QFont CFcEngine::getQFont(const QString &family, quint32 style, int size)
879 int weight,
880 width,
881 slant;
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));
889 #endif
890 return font;
893 bool CFcEngine::parseUrl(const KUrl &url, int faceNo)
895 #ifdef KFI_FC_DEBUG
896 kDebug() << url.prettyUrl() << ' ' << faceNo;
897 #endif
898 if(faceNo<0)
899 faceNo=0;
901 itsFileName.clear();
902 itsIndex=faceNo;
904 reinit();
906 // Possible urls:
908 // fonts:/times.ttf
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;
916 QString name;
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)),
925 KFI_KIO_FACE, 0);
926 #ifdef KFI_FC_DEBUG
927 kDebug() << "Stated fonts:/ url, name:" << name << " itsFileName:" << itsFileName
928 << " style:" << style << " itsIndex:" << itsIndex;
929 #endif
931 #ifdef KFI_FC_DEBUG
932 kDebug() << "isHidden:" << hidden;
933 #endif
934 if(hidden)
935 name=itsFileName;
937 if(!name.isEmpty())
939 if(hidden)
941 if(!parseUrl(KUrl(name), faceNo))
942 return false;
944 else
946 parseName(name, style);
947 itsInstalled=true;
950 else
951 return false;
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:
956 // a. fonts:/ Url
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());
967 if(line2.isEmpty())
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())
978 bool ok=false;
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())
991 bool ok=false;
992 quint32 style=line2.toULong(&ok);
994 itsInstalled=isThumbnailUrl=ok && parseName(line1, style);
996 if(itsInstalled)
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())
1008 ok=false;
1009 int face=line4.toInt(&ok);
1011 if(ok)
1013 itsFileName=line3;
1014 itsIndex=face;
1021 file.close();
1024 if(!isThumbnailUrl) // Its not a thumbnail, so read the real font file...
1026 itsName=itsFileName=url.path();
1028 int count;
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;
1036 if(pat)
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);
1042 #endif
1043 itsDescriptiveName=FC::createName(pat, itsWeight, itsWidth, itsSlant);
1044 FcPatternDestroy(pat);
1046 else
1048 itsDescriptiveName.clear();
1049 return false;
1052 itsInstalled=false;
1053 itsIndex=faceNo;
1056 else
1057 return false;
1059 itsLastUrl=url;
1060 return true;
1063 bool CFcEngine::parseName(const QString &name, quint32 style, const KUrl &url)
1065 int pos;
1067 reinit();
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);
1075 else
1077 itsWeight=FC_WEIGHT_REGULAR;
1078 itsWidth=KFI_FC_WIDTH_NORMAL;
1079 itsSlant=FC_SLANT_ROMAN;
1081 itsName=name;
1083 else
1085 if(KFI_NO_STYLE_INFO!=style)
1086 FC::decomposeStyleVal(style, itsWeight, itsWidth, itsSlant);
1087 else
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!
1101 itsLastUrl=url;
1102 return true;
1105 XftFont * CFcEngine::queryFont()
1107 static const int constQuerySize=8;
1109 #ifdef KFI_FC_DEBUG
1110 kDebug();
1111 #endif
1113 XftFont *f=getFont(constQuerySize);
1115 if(!isCorrect(f, true))
1117 XftFontClose(QX11Info::display(), f);
1118 f=NULL;
1121 if(itsInstalled && !f)
1123 // Perhaps its a newly installed font? If so try re-initialising fontconfig...
1124 theirFcDirty=true;
1125 reinit();
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);
1134 f=NULL;
1137 #ifdef KFI_FC_DEBUG
1138 kDebug() << "ret" << (int)f;
1139 #endif
1140 return f;
1143 XftFont * CFcEngine::getFont(int size)
1145 XftFont *f=NULL;
1147 #ifdef KFI_FC_DEBUG
1148 kDebug() << QString(itsInstalled ? itsName : itsFileName) << ' ' << size;
1149 #endif
1151 if(itsInstalled)
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,
1161 NULL);
1162 else
1163 #endif
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,
1169 NULL);
1171 else
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,
1178 NULL);
1179 f=XftFontOpenPattern(QX11Info::display(), pattern);
1182 #ifdef KFI_FC_DEBUG
1183 kDebug() << "ret: " << (int)f;
1184 #endif
1186 return f;
1189 bool CFcEngine::isCorrect(XftFont *f, bool checkFamily)
1191 int iv;
1192 FcChar8 *str;
1194 #ifdef KFI_FC_DEBUG
1195 QString xxx;
1196 QTextStream s(&xxx);
1197 if(f)
1199 if(itsInstalled)
1201 s << "weight:";
1202 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv))
1203 s << iv << '/' << itsWeight;
1204 else
1205 s << "no";
1207 s << " slant:";
1208 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv))
1209 s << iv << '/' << itsSlant;
1210 else
1211 s << "no";
1213 s << " width:";
1214 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv))
1215 s << iv << '/' << itsWidth;
1216 else
1217 s << "no";
1219 s << " fam:";
1220 if(checkFamily)
1221 if(FcResultMatch==FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str)
1222 s << QString::fromUtf8((char *)str) << '/' << itsName;
1223 else
1224 s << "no";
1225 else
1226 s << "ok";
1228 else
1229 s << "NOT Installed... ";
1231 else
1232 s << "No font!!! ";
1233 kDebug() << "isCorrect? " << xxx;
1234 #endif
1236 return
1238 ? itsInstalled
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))) &&
1247 #endif
1248 (!checkFamily ||
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
1254 : false;
1257 void CFcEngine::getSizes()
1259 #ifdef KFI_FC_DEBUG
1260 kDebug();
1261 #endif
1262 XftFont *f=queryFont();
1263 int alphaSize(itsSizes.size()>itsAlphaSizeIndex && itsAlphaSizeIndex>=0 ? itsSizes[itsAlphaSizeIndex] : constDefaultAlphaSize);
1265 itsScalable=FcTrue;
1267 itsSizes.clear();
1268 itsAlphaSizeIndex=0;
1270 if(f)
1272 bool gotSizes=false;
1273 double px(0.0);
1275 if(itsInstalled)
1277 if(FcResultMatch!=FcPatternGetBool(f->pattern, FC_SCALABLE, 0, &itsScalable))
1278 itsScalable=FcFalse;
1280 if(!itsScalable)
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,
1292 NULL);
1293 else
1294 #endif
1295 pat=FcPatternBuild(NULL,
1296 FC_FAMILY, FcTypeString,
1297 (const FcChar8 *)(itsName.toUtf8().data()),
1298 FC_WEIGHT, FcTypeInteger, itsWeight,
1299 FC_SLANT, FcTypeInteger, itsSlant,
1300 NULL);
1302 FcFontSet *set=FcFontList(0, pat, os);
1304 FcPatternDestroy(pat);
1305 FcObjectSetDestroy(os);
1307 if (set)
1309 int size(0);
1310 #ifdef KFI_FC_DEBUG
1311 kDebug() << "got fixed sizes: " << set->nfont;
1312 #endif
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))
1317 gotSizes=true;
1318 itsSizes.push_back((int)px);
1320 #ifdef KFI_FC_DEBUG
1321 kDebug() << "got fixed: " << px;
1322 #endif
1323 if (px<=alphaSize)
1324 itsAlphaSizeIndex=size;
1325 size++;
1327 FcFontSetDestroy(set);
1331 else
1333 FT_Face face=XftLockFace(f);
1335 if(face)
1337 itsIndexCount=face->num_faces;
1338 if(!(itsScalable=FT_IS_SCALABLE(face)))
1340 int numSizes=face->num_fixed_sizes,
1341 size;
1343 gotSizes=true;
1345 itsSizes.reserve(numSizes);
1347 #ifdef KFI_FC_DEBUG
1348 kDebug() << "numSizes fixed: " << numSizes;
1349 #endif
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;
1354 #else
1355 double px=face->available_sizes[size].width;
1356 #endif
1357 #ifdef KFI_FC_DEBUG
1358 kDebug() << "px: " << px;
1359 #endif
1360 itsSizes.push_back((int)px);
1362 if (px<=alphaSize)
1363 itsAlphaSizeIndex=size;
1366 XftUnlockFace(f);
1370 XftFontClose(QX11Info::display(), f);
1373 if(itsScalable)
1375 itsSizes.reserve(sizeof(constScalableSizes)/sizeof(int));
1377 for (int i=0; constScalableSizes[i]; ++i)
1379 int px=point2Pixel(constScalableSizes[i]);
1381 if (px<=alphaSize)
1382 itsAlphaSizeIndex=i;
1383 itsSizes.push_back(px);
1387 #ifdef KFI_FC_DEBUG
1388 kDebug() << "end";
1389 #endif
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);
1397 painter.end();
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);
1411 y+=4;
1412 drawLine(pix, x, y, w-1, 1);
1413 y+=8;
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()
1428 if(theirFcDirty)
1430 FcInitLoadConfigAndFonts();
1431 FcInitReinitialize();
1432 theirFcDirty=false;