From 4aed2c8219774f5d797760606b8489a92ddc5163 Mon Sep 17 00:00:00 2001 From: toma Date: Wed, 25 Nov 2009 17:56:58 +0000 Subject: Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features. BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da --- kcontrol/kfontinst/lib/FcEngine.cpp | 1179 +++++++++++++++++++++++++++++++++++ 1 file changed, 1179 insertions(+) create mode 100644 kcontrol/kfontinst/lib/FcEngine.cpp (limited to 'kcontrol/kfontinst/lib/FcEngine.cpp') diff --git a/kcontrol/kfontinst/lib/FcEngine.cpp b/kcontrol/kfontinst/lib/FcEngine.cpp new file mode 100644 index 000000000..0b3e51767 --- /dev/null +++ b/kcontrol/kfontinst/lib/FcEngine.cpp @@ -0,0 +1,1179 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "FcEngine.h" +#include "KfiConstants.h" +#ifdef HAVE_XFT +#include +#include +#include +#endif + +#define KFI_HAVE_OBLIQUE // Do we differentiate between Italic and Oblique? +#define KFI_HAVE_MEDIUM_WEIGHT // Do we differentiate between Medium and Normal weights? + +#define KFI_PREVIEW_GROUP "Preview Settings" +#define KFI_PREVIEW_STRING_KEY "String" + +#ifdef HAVE_XFT +#define KFI_DISPLAY(pix) (pix ? pix->x11Display() : QPaintDevice::x11AppDisplay()) +#endif + +namespace KFI +{ + +const int CFcEngine::constScalableSizes[]={8, 10, 12, 24, 36, 48, 64, 72, 96, 0 }; +const int CFcEngine::constDefaultAlphaSize=24; + +static int fcWeight(int weight) +{ + if(weight>1; + case FC_WEIGHT_LIGHT: + return QFont::Light; + default: + case FC_WEIGHT_NORMAL: + return QFont::Normal; + case FC_WEIGHT_MEDIUM: +#ifdef KFI_HAVE_MEDIUM_WEIGHT + return (QFont::Normal+QFont::DemiBold)>>1; +#endif + return QFont::Normal; + case FC_WEIGHT_SEMIBOLD: + return QFont::DemiBold; + case FC_WEIGHT_BOLD: + return QFont::Bold; + case FC_WEIGHT_ULTRABOLD: + return (QFont::Bold+QFont::Black)>>1; + case FC_WEIGHT_HEAVY: + return QFont::Black; + } +} + +#ifndef KFI_FC_NO_WIDTHS +static int fcWidth(int width) +{ + if(width3 && painter.fontMetrics().size(0, s).width()>width) + { + if(!addedElipses) + { + s.remove(s.length()-2, 2); + s.append("..."); + addedElipses=true; + } + else + s.remove(s.length()-4, 1); + } + painter.drawText(x, y, s); +} + +inline bool equal(double d1, double d2) +{ + return (fabs(d1 - d2) < 0.0001); +} + +inline bool equalWeight(int a, int b) +{ + return a==b || fcWeight(a)==fcWeight(b); +} + +#ifndef KFI_FC_NO_WIDTHS +inline bool equalWidth(int a, int b) +{ + return a==b || fcWidth(a)==fcWidth(b); +} +#endif + +inline bool equalSlant(int a, int b) +{ + return a==b || fcSlant(a)==fcSlant(b); +} + +#ifdef HAVE_XFT +static bool drawChar(QPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const QString &text, int pos, + int &x, int &y, int w, int h, int fSize, int offset) +{ + XGlyphInfo extents; + const FcChar16 *str=(FcChar16 *)(&(text.ucs2()[pos])); + + XftTextExtents16(pix.x11Display(), xftFont, str, 1, &extents); + + if(x+extents.width+2>w) + { + x=offset; + y+=fSize; + } + + if(y+offset0) + { + y+=extents.height+offset; + return true; + } + return false; +} + +static bool drawGlyph(QPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, FT_UInt i, + int &x, int &y, int &w, int &h, int fSize, int offset) +{ + XGlyphInfo extents; + + XftGlyphExtents(pix.x11Display(), xftFont, &i, 1, &extents); + + if(x+extents.width+2>w) + { + x=offset; + y+=fSize; + } + + if(y+offsetcharset, str[ch].unicode())) + return false; + return true; +} +#endif + +CFcEngine::CFcEngine() + : itsIndex(-1), + itsIndexCount(1) +{ +} + +CFcEngine::~CFcEngine() +{ + // Clear any fonts that may have been added... + FcConfigAppFontClear(FcConfigGetCurrent()); +} + +QString CFcEngine::getName(const KURL &url, int faceNo) +{ + if(url!=itsLastUrl || faceNo!=itsIndex) + parseUrl(url, faceNo); + + return itsDescriptiveName; +} + +#ifdef HAVE_XFT +bool CFcEngine::draw(const KURL &url, int w, int h, QPixmap &pix, int faceNo, bool thumb) +{ + bool rv=false; + + if((url==itsLastUrl && faceNo==itsIndex) || parseUrl(url, faceNo)) + { + rv=true; + + if(!itsInstalled) // Then add to fontconfig's list, so that Xft can display it... + { + FcInitReinitialize(); + FcConfigAppFontAddFile(FcConfigGetCurrent(), (const FcChar8 *)(itsName.utf8().data())); + } + + if(thumb && (w!=h || h>128)) + thumb=false; + + int offset=thumb + ? h<=32 + ? 2 + : 3 + : 4, + x=offset, y=offset; + + pix.resize(w, h); + pix.fill(Qt::white); + + QPainter painter(&pix); + + getSizes(&pix); + + if(itsSizes.size()) + { + XRenderColor xrenderCol; + XftColor xftCol; + + xrenderCol.red=xrenderCol.green=xrenderCol.blue=0; + xrenderCol.alpha=0xffff; + XftColorAllocValue(pix.x11Display(), DefaultVisual(pix.x11Display(), + pix.x11Screen()), + DefaultColormap(pix.x11Display(), pix.x11Screen()), + &xrenderCol, &xftCol); + + XftDraw *xftDraw=XftDrawCreate(pix.x11Display(), (Pixmap)(pix.handle()), + (Visual*)(pix.x11Visual()), pix.x11Colormap()); + + if(xftDraw) + { + XftFont *xftFont=NULL; + bool drawGlyphs=false; + + if(thumb) + { + QString text(i18n("AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789")); + + // + // Calculate size of text... + int fSize= h <= 32 + ? h-(offset*2) // 1 line of chars... + : h <= 64 + ? (h-(offset*3))/2 // 2 lines... + : (h-(offset*4))/3; // 3 lines or more + + if(!itsScalable) // Then need to get nearest size... + { + int bSize=fSize; + + for(unsigned int s=0; snum_glyphs && ynum_glyphs && yh) + stop=true; + else + { + if(x+extents.width0) + x+=extents.width+space; + } + if(x>=w || i==face->num_glyphs-1) + { + y+=itsSizes[s]+offset; + x=offset; + break; + } + } + + XftUnlockFace(xftFont); + } + } + else + drawString(pix, xftDraw, xftFont, &xftCol, previewString, x, y, h, offset); + XftFontClose(pix.x11Display(), xftFont); + } + } + } + } + + XftDrawDestroy(xftDraw); + } + } + } + + return rv; +} +#endif + +QString CFcEngine::getPreviewString() +{ + KConfig cfg(KFI_UI_CFG_FILE); + + cfg.setGroup(KFI_PREVIEW_GROUP); + + QString str(cfg.readEntry(KFI_PREVIEW_STRING_KEY)); + + return str.isEmpty() ? i18n("A sentence that uses all of the letters of the alphabet", + "The quick brown fox jumps over the lazy dog") + : str; +} + +void CFcEngine::setPreviewString(const QString &str) +{ + KConfig cfg(KFI_UI_CFG_FILE); + + cfg.setGroup(KFI_PREVIEW_GROUP); + cfg.writeEntry(KFI_PREVIEW_STRING_KEY, str); +} + +QString CFcEngine::getUppercaseLetters() +{ + return i18n("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); +} + +QString CFcEngine::getLowercaseLetters() +{ + return i18n("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz"); +} + +QString CFcEngine::getPunctuation() +{ + return i18n("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]"); +} + +QString CFcEngine::getFcString(FcPattern *pat, const char *val, int faceNo) +{ + QString rv; + FcChar8 *fcStr; + + if(FcResultMatch==FcPatternGetString(pat, val, faceNo, &fcStr)) + rv=QString::fromUtf8((char *)fcStr); + + return rv; +} + +QString CFcEngine::createName(FcPattern *pat, int faceNo) +{ +//CPD: TODO: the names *need* to match up with kfontchooser's... + QString name(getFcString(pat, FC_FAMILY, faceNo)), + str; + int intVal; + bool comma=false; + + if (FcResultMatch==FcPatternGetInteger(pat, FC_WEIGHT, faceNo, &intVal)) + { + str=weightStr(intVal); + if(!str.isEmpty()) + { + name+=QString(", ")+str; + comma=true; + } + } + + if (FcResultMatch==FcPatternGetInteger(pat, FC_SLANT, faceNo, &intVal)) + { + str=slantStr(intVal); + if(!str.isEmpty()) + { + if(!comma) + { + name+=QChar(','); + comma=true; + } + name+=QChar(' ')+str; + } + } + +#ifndef KFI_FC_NO_WIDTHS + if (FcResultMatch==FcPatternGetInteger(pat, FC_WIDTH, faceNo, &intVal)) + { + str=widthStr(intVal); + if(!str.isEmpty()) + name+=QChar(' ')+str; + } +#endif + + return name; +} + +QString CFcEngine::weightStr(int weight, bool emptyNormal) +{ + switch(fcWeight(weight)) + { + case FC_WEIGHT_THIN: + return i18n(KFI_WEIGHT_THIN); + case FC_WEIGHT_ULTRALIGHT: + return i18n(KFI_WEIGHT_ULTRALIGHT); + case FC_WEIGHT_LIGHT: + return i18n(KFI_WEIGHT_LIGHT); + case FC_WEIGHT_NORMAL: + return emptyNormal ? QString::null : i18n(KFI_WEIGHT_NORMAL); + case FC_WEIGHT_MEDIUM: + return i18n(KFI_WEIGHT_MEDIUM); + case FC_WEIGHT_DEMIBOLD: + return i18n(KFI_WEIGHT_SEMIBOLD); + case FC_WEIGHT_BOLD: + return i18n(KFI_WEIGHT_BOLD); + case FC_WEIGHT_ULTRABOLD: + return i18n(KFI_WEIGHT_ULTRABOLD); + default: + return i18n(KFI_WEIGHT_HEAVY); + } +} + +#ifndef KFI_FC_NO_WIDTHS +QString CFcEngine::widthStr(int width, bool emptyNormal) +{ + switch(fcWidth(width)) + { + case FC_WIDTH_ULTRACONDENSED: + return i18n(KFI_WIDTH_ULTRACONDENSED); + case FC_WIDTH_EXTRACONDENSED: + return i18n(KFI_WIDTH_EXTRACONDENSED); + case FC_WIDTH_CONDENSED: + return i18n(KFI_WIDTH_CONDENSED); + case FC_WIDTH_SEMICONDENSED: + return i18n(KFI_WIDTH_SEMICONDENSED); + case FC_WIDTH_NORMAL: + return emptyNormal ? QString::null : i18n(KFI_WIDTH_NORMAL); + case FC_WIDTH_SEMIEXPANDED: + return i18n(KFI_WIDTH_SEMIEXPANDED); + case FC_WIDTH_EXPANDED: + return i18n(KFI_WIDTH_EXPANDED); + case FC_WIDTH_EXTRAEXPANDED: + return i18n(KFI_WIDTH_EXTRAEXPANDED); + default: + return i18n(KFI_WIDTH_ULTRAEXPANDED); + } +} +#endif + +QString CFcEngine::slantStr(int slant, bool emptyNormal) +{ + switch(fcSlant(slant)) + { + case FC_SLANT_OBLIQUE: + return i18n(KFI_SLANT_OBLIQUE); + case FC_SLANT_ITALIC: + return i18n(KFI_SLANT_ITALIC); + default: + return emptyNormal ? QString::null : i18n(KFI_SLANT_ROMAN); + } +} + +QString CFcEngine::spacingStr(int spacing) +{ + switch(fcSpacing(spacing)) + { + case FC_MONO: + return i18n(KFI_SPACING_MONO); + case FC_CHARCELL: + return i18n(KFI_SPACING_CHARCELL); + default: + return i18n(KFI_SPACING_PROPORTIONAL); + } +} + +bool CFcEngine::getInfo(const KURL &url, int faceNo, QString &full, QString &family, QString &foundry, QString &weight, +#ifndef KFI_FC_NO_WIDTHS + QString &width, +#endif + QString &spacing, QString &slant) +{ + if(parseUrl(url, faceNo, true)) + { + full=itsDescriptiveName; + if(url.isLocalFile()) + { + int pos; + + if(-1==(pos=itsDescriptiveName.find(", "))) // No style information... + family=itsDescriptiveName; + else + family=itsDescriptiveName.left(pos); + } + else + family=itsName; + weight=weightStr(itsWeight, false); +#ifndef KFI_FC_NO_WIDTHS + width=widthStr(itsWidth, false); +#endif + slant=slantStr(itsSlant, false); + spacing=spacingStr(itsSpacing); + foundry=itsFoundry; + return true; + } + + return false; +} + +QFont CFcEngine::getQFont(const QString &name, int size) +{ + parseName(name, 0, false); + + QFont font(itsName, size, fcToQtWeight(itsWeight), fcToQtSlant(itsSlant)); + +#ifndef KFI_FC_NO_WIDTHS + font.setStretch(fcToQtWidth(itsWidth)); +#endif + return font; +} + +bool CFcEngine::parseUrl(const KURL &url, int faceNo, bool all) +{ + FcInitLoadConfigAndFonts(); + + // Possible urls: + // + // fonts:/times.ttf + // fonts:/System/times.ttf + // file:/home/wibble/hmm.ttf + // + if(KFI_KIO_FONTS_PROTOCOL==url.protocol()) + { + KIO::UDSEntry udsEntry; + QString name; + + FcInitReinitialize(); + if(KIO::NetAccess::stat(url, udsEntry, NULL)) // Need to stat the url to get its font name... + { + KIO::UDSEntry::Iterator it(udsEntry.begin()), + end(udsEntry.end()); + + for( ; it != end; ++it) + if (KIO::UDS_NAME==(*it).m_uds) + { + name=(*it).m_str; + break; + } + } + + if(!name.isEmpty()) + { + parseName(name, faceNo, all); + itsInstalled=true; + } + else + return false; + } + else if(url.isLocalFile()) + { + // Now lets see if its from the thumbnail job! if so, then file will just contain the URL! + QFile file(url.path()); + bool isThumbnailUrl=false; + + if(file.size()<2048 && file.open(IO_ReadOnly)) // Urls should be less than 2k, and fonts usually above! + { + QString thumbUrl; + QTextStream stream(&file); + + thumbUrl=stream.readLine(); + isThumbnailUrl=0==thumbUrl.find(KFI_KIO_FONTS_PROTOCOL) && parseUrl(KURL(thumbUrl), faceNo, all); + file.close(); + } + + if(!isThumbnailUrl) // Its not a thumbnail, so read the real font file... + { + itsName=url.path(); + + int count; + FcPattern *pat=FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(itsName).data()), 0, NULL, &count); + + itsWeight=FC_WEIGHT_NORMAL; +#ifndef KFI_FC_NO_WIDTHS + itsWidth=FC_WIDTH_NORMAL; +#endif + itsSlant=FC_SLANT_ROMAN; + itsSpacing=FC_PROPORTIONAL; + + if(pat) + { + itsDescriptiveName=createName(pat, faceNo); + + if(all) + { + FcPatternGetInteger(pat, FC_WEIGHT, faceNo, &itsWeight); + FcPatternGetInteger(pat, FC_SLANT, faceNo, &itsSlant); +#ifndef KFI_FC_NO_WIDTHS + FcPatternGetInteger(pat, FC_WIDTH, faceNo, &itsWidth); +#endif + FcPatternGetInteger(pat, FC_SPACING, faceNo, &itsSpacing); + itsFoundry=getFcString(pat, FC_FOUNDRY, faceNo); + } + + FcPatternDestroy(pat); + } + else + itsDescriptiveName=QString::null; + + itsInstalled=false; + itsIndex=faceNo; + } + } + else + return false; + + itsLastUrl=url; + return true; +} + +void CFcEngine::parseName(const QString &name, int faceNo, bool all) +{ + int pos; + + itsDescriptiveName=name; + itsSpacing=FC_PROPORTIONAL; + if(-1==(pos=name.find(", "))) // No style information... + { + itsWeight=FC_WEIGHT_NORMAL; +#ifndef KFI_FC_NO_WIDTHS + itsWidth=FC_WIDTH_NORMAL; +#endif + itsSlant=FC_SLANT_ROMAN; + itsName=name; + } + else + { + QString style(name.mid(pos+2)); + + itsWeight=strToWeight(style, style); +#ifndef KFI_FC_NO_WIDTHS + itsWidth=strToWidth(style, style); +#endif + itsSlant=strToSlant(style); + itsName=name.left(pos); + } + + if(all) + { + FcObjectSet *os = FcObjectSetBuild(FC_SPACING, FC_FOUNDRY, (void *)0); + FcPattern *pat = FcPatternBuild(NULL, + FC_FAMILY, FcTypeString, (const FcChar8 *)(itsName.utf8().data()), + FC_WEIGHT, FcTypeInteger, itsWeight, + FC_SLANT, FcTypeInteger, itsSlant, +#ifndef KFI_FC_NO_WIDTHS + FC_WIDTH, FcTypeInteger, itsWidth, +#endif + NULL); + FcFontSet *set = FcFontList(0, pat, os); + + FcPatternDestroy(pat); + FcObjectSetDestroy(os); + + if(set && set->nfont) + { + FcPatternGetInteger(set->fonts[0], FC_SPACING, faceNo, &itsSpacing); + itsFoundry=getFcString(set->fonts[0], FC_FOUNDRY, faceNo); + } + } + + itsIndex=0; // Doesn't matter, as we're gonna use font name! + itsLastUrl=KURL(); +} + +#ifdef HAVE_XFT +XftFont * CFcEngine::getFont(int size, QPixmap *pix) +{ + if(itsInstalled) + return XftFontOpen(KFI_DISPLAY(pix), 0, + FC_FAMILY, FcTypeString, (const FcChar8 *)(itsName.utf8().data()), + FC_WEIGHT, FcTypeInteger, itsWeight, + FC_SLANT, FcTypeInteger, itsSlant, +#ifndef KFI_FC_NO_WIDTHS + FC_WIDTH, FcTypeInteger, itsWidth, +#endif + FC_PIXEL_SIZE, FcTypeDouble, (double)size, + NULL); + else + { + FcPattern *pattern = FcPatternBuild(NULL, + FC_FILE, FcTypeString, QFile::encodeName(itsName).data(), + FC_INDEX, FcTypeInteger, itsIndex, + FC_PIXEL_SIZE, FcTypeDouble, (double)size, + NULL); + return XftFontOpenPattern(KFI_DISPLAY(pix), pattern); + } +} + +void CFcEngine::getSizes(QPixmap *pix) +{ + static const int constNumSizes=11; + static const int constNumSizeRanges=2; + static const int constSizes[constNumSizeRanges][constNumSizes]= { {8, 10, 12, 14, 16, 18, 24, 36, 48, 72, 96}, + {7, 9, 11, 13, 15, 17, 23, 35, 47, 71, 95} }; + XftFont *f=getFont(8, pix); + + itsScalable=FcTrue; + + itsSizes.clear(); + itsAlphaSize=0; + + if(f) + { + bool gotSizes=false; + + if(itsInstalled) + { + if(FcResultMatch!=FcPatternGetBool(f->pattern, FC_SCALABLE, 0, &itsScalable)) + itsScalable=FcFalse; + } + else + { + FT_Face face=XftLockFace(f); + + if(face) + { + itsIndexCount=face->num_faces; + if(!(itsScalable=FT_IS_SCALABLE(face))) + { + int numSizes=face->num_fixed_sizes, + size; + + gotSizes=true; + + itsSizes.reserve(numSizes); + + for (size=0; sizeavailable_sizes[size].height); + if (face->available_sizes[size].height<=constDefaultAlphaSize) + itsAlphaSize=face->available_sizes[size].height; + } + } + XftUnlockFace(f); + } + } + + XftFontClose(KFI_DISPLAY(pix), f); + + // + // Hmm... its not a scalable font, and its installed. So to get list of sizes, iterate through a list of standard + // sizes, and ask fontconfig for a font of that sizes. Then check the retured size, family, etc is what was asked + // for! + if(!itsScalable && !gotSizes) + { + itsSizes.reserve(constNumSizes); + + for(int l=0; lpattern, FC_PIXEL_SIZE, 0, &px) && equal(constSizes[l][i], px) && + FcResultMatch==FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv) && equalWeight(iv,itsWeight) && + FcResultMatch==FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv) && equalSlant(iv, itsSlant) && +#ifndef KFI_FC_NO_WIDTHS + FcResultMatch==FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv) && equalWidth(iv, itsWidth) && +#endif + FcResultMatch==FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str && + QString::fromUtf8((char *)str)==itsName) + { + itsSizes.push_back(constSizes[l][i]); + gotSizes=true; + if(constSizes[l][i]<=constDefaultAlphaSize) + itsAlphaSize=constSizes[l][i]; + } + XftFontClose(KFI_DISPLAY(pix), f); + } + } + } + } + + if(itsScalable) + { + itsSizes.reserve(constNumSizes); + + for (int i=0; constScalableSizes[i]; ++i) + itsSizes.push_back(point2Pixel(constScalableSizes[i])); + itsAlphaSize=constDefaultAlphaSize; + } +} +#endif + +} -- cgit v1.2.3