#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() : TQPaintDevice::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 TQFont::Light; default: case FC_WEIGHT_NORMAL: return TQFont::Normal; case FC_WEIGHT_MEDIUM: #ifdef KFI_HAVE_MEDIUM_WEIGHT return (TQFont::Normal+TQFont::DemiBold)>>1; #endif return TQFont::Normal; case FC_WEIGHT_SEMIBOLD: return TQFont::DemiBold; case FC_WEIGHT_BOLD: return TQFont::Bold; case FC_WEIGHT_ULTRABOLD: return (TQFont::Bold+TQFont::Black)>>1; case FC_WEIGHT_HEAVY: return TQFont::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(TQPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const TQString &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(TQPixmap &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()); } TQString 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, TQPixmap &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); TQPainter 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) { TQString 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 TQString CFcEngine::getPreviewString() { TDEConfig cfg(KFI_UI_CFG_FILE); cfg.setGroup(KFI_PREVIEW_GROUP); TQString 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 TQString &str) { TDEConfig cfg(KFI_UI_CFG_FILE); cfg.setGroup(KFI_PREVIEW_GROUP); cfg.writeEntry(KFI_PREVIEW_STRING_KEY, str); } TQString CFcEngine::getUppercaseLetters() { return i18n("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); } TQString CFcEngine::getLowercaseLetters() { return i18n("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz"); } TQString CFcEngine::getPunctuation() { return i18n("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]"); } TQString CFcEngine::getFcString(FcPattern *pat, const char *val, int faceNo) { TQString rv; FcChar8 *fcStr; if(FcResultMatch==FcPatternGetString(pat, val, faceNo, &fcStr)) rv=TQString::fromUtf8((char *)fcStr); return rv; } TQString CFcEngine::createName(FcPattern *pat, int faceNo) { //CPD: TODO: the names *need* to match up with tdefontchooser's... TQString 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+=TQString(", ")+str; comma=true; } } if (FcResultMatch==FcPatternGetInteger(pat, FC_SLANT, faceNo, &intVal)) { str=slantStr(intVal); if(!str.isEmpty()) { if(!comma) { name+=TQChar(','); comma=true; } name+=TQChar(' ')+str; } } #ifndef KFI_FC_NO_WIDTHS if (FcResultMatch==FcPatternGetInteger(pat, FC_WIDTH, faceNo, &intVal)) { str=widthStr(intVal); if(!str.isEmpty()) name+=TQChar(' ')+str; } #endif return name; } TQString 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 ? TQString::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 TQString 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 ? TQString::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 TQString 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 ? TQString::null : i18n(KFI_SLANT_ROMAN); } } TQString 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, TQString &full, TQString &family, TQString &foundry, TQString &weight, #ifndef KFI_FC_NO_WIDTHS TQString &width, #endif TQString &spacing, TQString &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; } TQFont CFcEngine::getQFont(const TQString &name, int size) { parseName(name, 0, false); TQFont 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_TDEIO_FONTS_PROTOCOL==url.protocol()) { TDEIO::UDSEntry udsEntry; TQString name; FcInitReinitialize(); if(TDEIO::NetAccess::stat(url, udsEntry, NULL)) // Need to stat the url to get its font name... { TDEIO::UDSEntry::Iterator it(udsEntry.begin()), end(udsEntry.end()); for( ; it != end; ++it) if (TDEIO::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! TQFile file(url.path()); bool isThumbnailUrl=false; if(file.size()<2048 && file.open(IO_ReadOnly)) // Urls should be less than 2k, and fonts usually above! { TQString thumbUrl; TQTextStream stream(&file); thumbUrl=stream.readLine(); isThumbnailUrl=0==thumbUrl.find(KFI_TDEIO_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 *)(TQFile::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=TQString::null; itsInstalled=false; itsIndex=faceNo; } } else return false; itsLastUrl=url; return true; } void CFcEngine::parseName(const TQString &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 { TQString 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, TQPixmap *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, TQFile::encodeName(itsName).data(), FC_INDEX, FcTypeInteger, itsIndex, FC_PIXEL_SIZE, FcTypeDouble, (double)size, NULL); return XftFontOpenPattern(KFI_DISPLAY(pix), pattern); } } void CFcEngine::getSizes(TQPixmap *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 && TQString::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 }