Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / kcontrol / kfontinst / lib / FcEngine.cpp
blobb42f55d0ce1118a91bc16b3363984d16be28ff1b
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 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)
61 switch(weight)
63 case FC_WEIGHT_THIN:
64 return 0;
65 case FC_WEIGHT_EXTRALIGHT:
66 return QFont::Light>>1;
67 case FC_WEIGHT_LIGHT:
68 return QFont::Light;
69 default:
70 case FC_WEIGHT_REGULAR:
71 return QFont::Normal;
72 case FC_WEIGHT_MEDIUM:
73 #ifdef KFI_HAVE_MEDIUM_WEIGHT
74 return (QFont::Normal+QFont::DemiBold)>>1;
75 #endif
76 return QFont::Normal;
77 case FC_WEIGHT_DEMIBOLD:
78 return QFont::DemiBold;
79 case FC_WEIGHT_BOLD:
80 return QFont::Bold;
81 case FC_WEIGHT_EXTRABOLD:
82 return (QFont::Bold+QFont::Black)>>1;
83 case FC_WEIGHT_BLACK:
84 return QFont::Black;
88 #ifndef KFI_FC_NO_WIDTHS
89 static int fcToQtWidth(int weight)
91 switch(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;
101 default:
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;
114 #endif
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)
123 QString s(str);
124 bool addedElipses=false;
126 width-=x*2;
127 while(s.length()>3 && painter.fontMetrics().size(0, s).width()>width)
129 if(!addedElipses)
131 s.remove(s.length()-2, 2);
132 s.append("...");
133 addedElipses=true;
135 else
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);
156 #endif
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))
167 XGlyphInfo extents;
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);
175 return true;
178 return false;
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)
186 r=QRect();
187 if(XftCharExists(QX11Info::display(), xftFont, ch))
189 XGlyphInfo extents;
191 XftTextExtents32(QX11Info::display(), xftFont, &ch, 1, &extents);
193 if(extents.x>0)
194 x+=extents.x;
196 if(x+extents.width+constBorder>w)
198 x=offset;
199 if(extents.x>0)
200 x+=extents.x;
201 y+=fontHeight+constBorder;
204 if(y+offset<h)
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;
210 return true;
212 return false;
215 return true;
218 static bool drawString(XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const QString &text,
219 int x, int &y, int h, int offset)
221 XGlyphInfo extents;
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());
227 if(extents.height>0)
229 y+=extents.height+offset;
230 return true;
232 return false;
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,
237 QRect &r)
239 XGlyphInfo extents;
241 XftGlyphExtents(QX11Info::display(), xftFont, &i, 1, &extents);
243 if(x+extents.width+2>w)
245 if(oneLine)
246 return false;
248 x=offset;
249 y+=fontHeight+2;
252 if(y+offset<h)
254 XftDrawGlyphs(xftDraw, xftCol, xftFont, x, y, &i, 1);
255 x+=extents.width+2;
256 r=QRect(x-extents.x, y-extents.y, extents.width+constBorder, extents.height);
258 return true;
260 return false;
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,
265 QRect *used=NULL)
267 bool rv(false);
269 if(xftFont)
271 FT_Face face=XftLockFace(xftFont);
273 if(face)
275 int space(fontHeight/10),
276 drawn(1);
277 QRect r;
279 if(!space)
280 space=1;
282 rv=true;
283 y+=fontHeight;
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))
287 if(r.height()>0)
289 if(used)
290 if(used->isEmpty())
291 *used=r;
292 else
293 *used=used->united(r);
294 if(max>0 && ++drawn>=max)
295 break;
298 else
299 break;
301 if(oneLine)
302 x=offset;
303 XftUnlockFace(xftFont);
307 return rv;
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()))
322 return false;
323 return true;
326 static QString usableStr(XftFont *font, QString &str)
328 unsigned int slen=str.length(),
330 QString newStr;
332 for(ch=0; ch<slen; ++ch)
333 if(FcCharSetHasChar(font->charset, str[ch].unicode()))
334 newStr+=str[ch];
335 return newStr;
338 CFcEngine * CFcEngine::instance()
340 return theInstance;
343 CFcEngine::CFcEngine()
344 : itsFcDirty(true),
345 itsIndex(-1),
346 itsIndexCount(1),
347 itsPreviewString(getDefaultPreviewString())
349 reinit();
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)
378 bool rv=false;
380 if(!item.isEmpty())
382 static const int constOffset=2;
383 static const int constInitialWidth=1536;
385 bool ok=true;
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);
392 addFontFile(item);
394 else
396 parseName(item, style);
397 itsInstalled=true;
400 if(ok)
402 itsLastUrl=KUrl();
403 getSizes();
405 if(itsSizes.size())
408 // Calculate size of text...
409 int fSize=((int)(h*0.75))-2,
410 offset=0;
411 bool needResize(false);
413 if(!itsScalable) // Then need to get nearest size...
415 int bSize=0;
417 for(int s=0; s<itsSizes.size(); ++s)
418 if (itsSizes[s]<=fSize || 0==bSize)
419 bSize=itsSizes[s];
420 fSize=bSize;
422 if(bSize>h)
424 pix=QPixmap(constInitialWidth, bSize+8);
425 pix.fill(theirBgndCol);
426 needResize=true;
428 else
429 offset=(fSize-bSize)/2;
432 if(!needResize)
434 pix=QPixmap(constInitialWidth, h);
435 pix.fill(theirBgndCol);
438 const QX11Info &x11Info(pix.x11Info());
439 XRenderColor xrenderCol;
440 XftColor xftCol;
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(),
448 x11Info.screen()),
449 DefaultColormap(QX11Info::display(), x11Info.screen()),
450 &xrenderCol, &xftCol);
452 XftDraw *xftDraw=XftDrawCreate(QX11Info::display(), (Pixmap)(pix.handle()),
453 (Visual*)(x11Info.visual()), x11Info.colormap());
455 if(xftDraw)
457 XftFont *xftFont=NULL;
458 QString text(itsPreviewString);
461 // Calculate size of text...
462 int fSize=((int)(h*0.75))-2,
463 offset=0;
465 if(!itsScalable) // Then need to get nearest size...
467 int bSize=0;
469 for(int s=0; s<itsSizes.size(); ++s)
470 if (itsSizes[s]<=fSize || 0==bSize)
471 bSize=itsSizes[s];
472 fSize=bSize;
474 if(bSize>h)
477 #ifdef KFI_FC_DEBUG
478 kDebug() << "use size:" << bSize+8;
479 #endif
480 pix=QPixmap(constInitialWidth, bSize+8);
481 offset=4;
482 pix.fill(theirBgndCol);
483 needResize=true;
485 else
487 offset=(fSize-bSize)/2;
490 xftFont=getFont(fSize);
492 if(xftFont)
494 if(hasStr(xftFont, text) || hasStr(xftFont, text=text.toUpper()) ||
495 hasStr(xftFont, text=text.toLower()))
497 XGlyphInfo extents;
498 const FcChar16 *str=(FcChar16 *)(text.utf16());
500 XftTextExtents16(QX11Info::display(), xftFont, str, text.length(),
501 &extents);
503 int y=((h-extents.y)/2)+extents.y;
504 XftDrawString16(xftDraw, &xftCol, xftFont, constOffset, y, str,
505 text.length());
506 if(needResize)
507 pix=pix.scaledToHeight(h);
508 pix = pix.copy(0, 0, extents.width+(2*constOffset)<constInitialWidth
509 ? extents.width+(2*constOffset)
510 : constInitialWidth,
512 rv=true;
514 else
516 FT_Face face=XftLockFace(xftFont);
518 if(face)
520 int x=constOffset,
521 y=constOffset+fSize;
523 for(FT_UInt i=1; i<(unsigned int)face->num_glyphs &&
524 i<(unsigned int)text.length()+1; ++i)
526 XGlyphInfo extents;
528 XftGlyphExtents(QX11Info::display(), xftFont, &i, 1,
529 &extents);
531 if(x+extents.width+2>constInitialWidth) // Only want 1 line
532 break;
534 XftDrawGlyphs(xftDraw, &xftCol, xftFont, x, y, &i, 1);
535 x+=extents.width+2;
537 if(needResize)
538 pix=pix.scaledToHeight(h);
539 pix = pix.copy(0, 0, x+constOffset<constInitialWidth
540 ? x+constOffset
541 : constInitialWidth,
543 XftUnlockFace(xftFont);
544 rv=true;
553 return rv;
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)
559 bool rv=false;
561 if(chars)
562 chars->clear();
564 if((url==itsLastUrl && faceNo==itsIndex) ||
565 (!name.isEmpty() && parseName(name, style, url)) ||
566 parseUrl(url, faceNo))
568 if(!name.isEmpty())
569 itsInstalled=true;
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))
579 thumb=false;
581 int offset=thumb
582 ? h<=32
585 : 4,
586 x=offset, y=offset;
588 getSizes();
590 pix=thumb && itsScalable ? QPixmap(w*4, h*2) : QPixmap(w, h);
591 pix.fill(theirBgndCol);
593 QPainter painter(&pix);
595 if(itsSizes.size())
597 const QX11Info &x11Info(pix.x11Info());
598 XRenderColor xrenderCol;
599 XftColor xftCol;
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(),
606 x11Info.screen()),
607 DefaultColormap(QX11Info::display(), x11Info.screen()),
608 &xrenderCol, &xftCol);
610 XftDraw *xftDraw=XftDrawCreate(QX11Info::display(), (Pixmap)(pix.handle()),
611 (Visual*)(x11Info.visual()), x11Info.colormap());
613 if(xftDraw)
615 XftFont *xftFont=NULL;
617 if(thumb)
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...
630 int bSize=0;
632 for(int s=0; s<itsSizes.size(); ++s)
633 if (itsSizes[s]<=fSize || 0==bSize)
634 bSize=itsSizes[s];
635 fSize=bSize;
638 xftFont=getFont(fSize);
640 if(xftFont)
642 QString valid(usableStr(xftFont, text));
643 QRect used(0, 0, 0, 0);
645 y=fSize;
646 rv=true;
648 if(itsScalable)
650 if(valid.length()!=text.length())
652 text=getPunctuation().mid(1, 2); // '1' '2'
653 valid=usableStr(xftFont, text);
656 else
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))
664 break;
667 if(itsScalable
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);
672 else
674 QVector<uint> ucs4(valid.toUcs4());
675 QRect r;
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,
679 offset, r))
681 if(used.isEmpty())
682 used=r;
683 else
684 used=used.united(r);
686 else
687 break;
690 painter.end();
692 if(itsScalable && !used.isEmpty())
694 used.adjust(-1*offset, -1*offset, offset, offset);
696 if(used.width()<pix.width() && used.height()<pix.height())
697 pix=pix.copy(used);
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);
710 if(xftFont)
712 bool lc(hasStr(xftFont, lowercase)),
713 uc(hasStr(xftFont, uppercase)),
714 drawGlyphs=!lc && !uc;
716 if(drawGlyphs)
717 y-=8;
718 else
720 QString validPunc(usableStr(xftFont, punctuation));
721 bool punc(validPunc.length()>=(punctuation.length()/2));
723 if(lc)
724 drawString(xftDraw, xftFont, &xftCol, lowercase, x, y, h, offset);
725 if(uc)
726 drawString(xftDraw, xftFont, &xftCol, uppercase, x, y, h, offset);
727 if(punc)
728 drawString(xftDraw, xftFont, &xftCol, validPunc, x, y, h, offset);
729 XftFontClose(QX11Info::display(), xftFont);
730 if(lc || uc || punc)
731 painter.drawLine(offset, y, w-(offset+1), y);
732 y+=8;
735 QString previewString(getPreviewString());
737 if(!drawGlyphs)
739 if(!lc && uc)
740 previewString=previewString.toUpper();
741 if(!uc && lc)
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;
750 rv=true;
751 if(drawGlyphs)
752 drawAllGlyphs(xftDraw, xftFont, &xftCol, fontHeight, x, y, w, h, offset,
753 itsSizes.count()>1);
754 else
755 drawString(xftDraw, xftFont, &xftCol, previewString, x, y, h,
756 offset);
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;
775 y-=8;
776 drawAllGlyphs(xftDraw, xftFont, &xftCol, fontHeight, x, y, w, h, offset);
777 rv=true;
778 XftFontClose(QX11Info::display(), xftFont);
781 else if((xftFont=getFont(itsAlphaSize*2)))
783 QRect r;
784 rv=drawChar32Centre(xftDraw, xftFont, &xftCol, (*(range.begin())).from,
785 pix.width(), pix.height());
788 else
790 QList<TRange>::ConstIterator it(range.begin()),
791 end(range.end());
793 if((xftFont=getFont(itsAlphaSize)))
795 rv=true;
796 drawName(painter, x, y, w, offset);
797 y+=itsAlphaSize;
799 bool stop=false;
800 int fontHeight=xftFont->ascent+xftFont->descent, xOrig(x), yOrig(y);
801 QRect r;
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,
807 offset, r))
809 if(chars && !r.isEmpty())
810 chars->append(TChar(r, c));
812 else
813 stop=true;
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);
831 return rv;
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)
860 QString rv;
861 QStringList kdeLangs=KGlobal::locale()->languageList(),
862 fontLangs;
863 QStringList::ConstIterator it(kdeLangs.begin()),
864 end(kdeLangs.end());
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);
871 if(lang.isEmpty())
872 break;
873 else
874 fontLangs.append(lang);
877 // Now go through the user's KDE locale, and try to find a font match...
878 for(; it!=end; ++it)
880 int index=fontLangs.findIndex(*it);
882 if(-1!=index)
884 rv=getFcString(pat, val, index);
886 if(!rv.isEmpty())
887 break;
891 if(rv.isEmpty())
892 rv=getFcString(pat, val, 0);
893 return rv;
895 #endif
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))
903 int pos;
905 if(-1==(pos=itsDescriptiveName.indexOf(", "))) // No style information...
906 info.family=itsDescriptiveName;
907 else
908 info.family=itsDescriptiveName.left(pos);
910 else
911 info.family=itsName;
912 info.styleInfo=styleVal();
913 return true;
916 return false;
919 QFont CFcEngine::getQFont(const QString &family, quint32 style, int size)
921 int weight,
922 width,
923 slant;
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));
931 #endif
932 return font;
935 bool CFcEngine::parseUrl(const KUrl &url, int faceNo)
937 #ifdef KFI_FC_DEBUG
938 kDebug() << url.prettyUrl() << ' ' << faceNo;
939 #endif
940 if(faceNo<0)
941 faceNo=0;
943 itsFileName.clear();
944 itsIndex=faceNo;
946 reinit();
948 // Possible urls:
950 // fonts:/times.ttf
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;
958 QString name;
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)),
967 KFI_KIO_FACE, 0);
968 #ifdef KFI_FC_DEBUG
969 kDebug() << "Stated fonts:/ url, name:" << name << " itsFileName:" << itsFileName
970 << " style:" << style << " itsIndex:" << itsIndex;
971 #endif
973 #ifdef KFI_FC_DEBUG
974 kDebug() << "isHidden:" << hidden;
975 #endif
976 if(hidden)
977 name=itsFileName;
979 if(!name.isEmpty())
981 if(hidden)
983 if(!parseUrl(KUrl(name), faceNo))
984 return false;
986 else
988 parseName(name, style);
989 itsInstalled=true;
992 else
993 return false;
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:
998 // a. fonts:/ Url
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());
1009 if(line2.isEmpty())
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())
1020 bool ok=false;
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())
1033 bool ok=false;
1034 quint32 style=line2.toULong(&ok);
1036 itsInstalled=isThumbnailUrl=ok && parseName(line1, style);
1038 if(itsInstalled)
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())
1050 ok=false;
1051 int face=line4.toInt(&ok);
1053 if(ok)
1055 itsFileName=line3;
1056 itsIndex=face;
1063 file.close();
1066 if(!isThumbnailUrl) // Its not a thumbnail, so read the real font file...
1068 itsName=itsFileName=url.path();
1070 int count;
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;
1078 if(pat)
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);
1084 #endif
1085 itsDescriptiveName=FC::createName(pat, itsWeight, itsWidth, itsSlant);
1086 FcPatternDestroy(pat);
1088 else
1090 itsDescriptiveName.clear();
1091 return false;
1094 itsInstalled=false;
1095 itsIndex=faceNo;
1098 else
1099 return false;
1101 itsLastUrl=url;
1102 return true;
1105 bool CFcEngine::parseName(const QString &name, quint32 style, const KUrl &url)
1107 int pos;
1109 reinit();
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);
1117 else
1119 itsWeight=FC_WEIGHT_REGULAR;
1120 itsWidth=KFI_FC_WIDTH_NORMAL;
1121 itsSlant=FC_SLANT_ROMAN;
1123 itsName=name;
1125 else
1127 if(KFI_NO_STYLE_INFO!=style)
1128 FC::decomposeStyleVal(style, itsWeight, itsWidth, itsSlant);
1129 else
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!
1143 itsLastUrl=url;
1144 return true;
1147 XftFont * CFcEngine::queryFont()
1149 static const int constQuerySize=8;
1151 #ifdef KFI_FC_DEBUG
1152 kDebug();
1153 #endif
1155 XftFont *f=getFont(constQuerySize);
1157 if(!isCorrect(f, true))
1159 XftFontClose(QX11Info::display(), f);
1160 f=NULL;
1163 if(itsInstalled && !f)
1165 // Perhaps its a newly installed font? If so try re-initialising fontconfig...
1166 itsFcDirty=true;
1167 reinit();
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);
1176 f=NULL;
1179 #ifdef KFI_FC_DEBUG
1180 kDebug() << "ret" << (int)f;
1181 #endif
1182 return f;
1185 XftFont * CFcEngine::getFont(int size)
1187 XftFont *f=NULL;
1189 #ifdef KFI_FC_DEBUG
1190 kDebug() << QString(itsInstalled ? itsName : itsFileName) << ' ' << size;
1191 #endif
1193 if(itsInstalled)
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,
1203 NULL);
1204 else
1205 #endif
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,
1211 NULL);
1213 else
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,
1220 NULL);
1221 f=XftFontOpenPattern(QX11Info::display(), pattern);
1224 #ifdef KFI_FC_DEBUG
1225 kDebug() << "ret: " << (int)f;
1226 #endif
1228 return f;
1231 bool CFcEngine::isCorrect(XftFont *f, bool checkFamily)
1233 int iv;
1234 FcChar8 *str;
1236 #ifdef KFI_FC_DEBUG
1237 QString xxx;
1238 QTextStream s(&xxx);
1239 if(f)
1241 if(itsInstalled)
1243 s << "weight:";
1244 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv))
1245 s << iv << '/' << itsWeight;
1246 else
1247 s << "no";
1249 s << " slant:";
1250 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv))
1251 s << iv << '/' << itsSlant;
1252 else
1253 s << "no";
1255 s << " width:";
1256 if(FcResultMatch==FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv))
1257 s << iv << '/' << itsWidth;
1258 else
1259 s << "no";
1261 s << " fam:";
1262 if(checkFamily)
1263 if(FcResultMatch==FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str)
1264 s << QString::fromUtf8((char *)str) << '/' << itsName;
1265 else
1266 s << "no";
1267 else
1268 s << "ok";
1270 else
1271 s << "NOT Installed... ";
1273 else
1274 s << "No font!!! ";
1275 kDebug() << "isCorrect? " << xxx;
1276 #endif
1278 return
1280 ? itsInstalled
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))) &&
1289 #endif
1290 (!checkFamily ||
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
1296 : false;
1299 void CFcEngine::getSizes()
1301 #ifdef KFI_FC_DEBUG
1302 kDebug();
1303 #endif
1305 XftFont *f=queryFont();
1307 itsScalable=FcTrue;
1309 itsSizes.clear();
1310 itsAlphaSize=0;
1312 if(f)
1314 bool gotSizes=false;
1315 double px(0.0);
1317 if(itsInstalled)
1319 if(FcResultMatch!=FcPatternGetBool(f->pattern, FC_SCALABLE, 0, &itsScalable))
1320 itsScalable=FcFalse;
1322 if(!itsScalable)
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,
1334 NULL);
1335 else
1336 #endif
1337 pat=FcPatternBuild(NULL,
1338 FC_FAMILY, FcTypeString,
1339 (const FcChar8 *)(itsName.toUtf8().data()),
1340 FC_WEIGHT, FcTypeInteger, itsWeight,
1341 FC_SLANT, FcTypeInteger, itsSlant,
1342 NULL);
1344 FcFontSet *set=FcFontList(0, pat, os);
1346 FcPatternDestroy(pat);
1347 FcObjectSetDestroy(os);
1349 if (set)
1351 #ifdef KFI_FC_DEBUG
1352 kDebug() << "got fixed sizes: " << set->nfont;
1353 #endif
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))
1358 gotSizes=true;
1359 itsSizes.push_back((int)px);
1361 #ifdef KFI_FC_DEBUG
1362 kDebug() << "got fixed: " << px;
1363 #endif
1364 if (px<=constDefaultAlphaSize)
1365 itsAlphaSize=(int)px;
1367 FcFontSetDestroy(set);
1371 else
1373 FT_Face face=XftLockFace(f);
1375 if(face)
1377 itsIndexCount=face->num_faces;
1378 if(!(itsScalable=FT_IS_SCALABLE(face)))
1380 int numSizes=face->num_fixed_sizes,
1381 size;
1383 gotSizes=true;
1385 itsSizes.reserve(numSizes);
1387 #ifdef KFI_FC_DEBUG
1388 kDebug() << "numSizes fixed: " << numSizes;
1389 #endif
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;
1394 #else
1395 double px=face->available_sizes[size].width;
1396 #endif
1397 #ifdef KFI_FC_DEBUG
1398 kDebug() << "px: " << px;
1399 #endif
1400 itsSizes.push_back((int)px);
1402 if (px<=constDefaultAlphaSize)
1403 itsAlphaSize=(int)px;
1406 XftUnlockFace(f);
1410 XftFontClose(QX11Info::display(), f);
1413 if(itsScalable)
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];
1424 #ifdef KFI_FC_DEBUG
1425 kDebug() << "end";
1426 #endif
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);
1442 y+=4;
1443 painter.drawLine(offset, y, w-(offset+1), y);
1444 y+=8;
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()
1459 if(itsFcDirty)
1461 FcInitLoadConfigAndFonts();
1462 FcInitReinitialize();
1463 itsFcDirty=false;