#include <tqpainter.h> #include <tqpixmap.h> #include <tqfontmetrics.h> #include <tqfile.h> #include <tqtextstream.h> #include <kurl.h> #include <kconfig.h> #include <kglobalsettings.h> #include <kio/netaccess.h> #include <math.h> #include "FcEngine.h" #include "KfiConstants.h" #ifdef HAVE_XFT #include <X11/Xlib.h> #include <X11/Xft/Xft.h> #include <fixx11h.h> #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<FC_WEIGHT_ULTRALIGHT) return FC_WEIGHT_THIN; if(weight<(FC_WEIGHT_ULTRALIGHT+FC_WEIGHT_LIGHT)/2) return FC_WEIGHT_ULTRALIGHT; if(weight<(FC_WEIGHT_LIGHT+FC_WEIGHT_NORMAL)/2) return FC_WEIGHT_LIGHT; #ifdef KFI_HAVE_MEDIUM_WEIGHT if(weight<(FC_WEIGHT_NORMAL+FC_WEIGHT_MEDIUM)/2) return FC_WEIGHT_NORMAL; if(weight<(FC_WEIGHT_MEDIUM+FC_WEIGHT_SEMIBOLD)/2) return FC_WEIGHT_MEDIUM; #else if(weight<(FC_WEIGHT_NORMAL+FC_WEIGHT_SEMIBOLD)/2) return FC_WEIGHT_NORMAL; #endif if(weight<(FC_WEIGHT_SEMIBOLD+FC_WEIGHT_BOLD)/2) return FC_WEIGHT_SEMIBOLD; if(weight<(FC_WEIGHT_BOLD+FC_WEIGHT_ULTRABOLD)/2) return FC_WEIGHT_BOLD; if(weight<(FC_WEIGHT_ULTRABOLD+FC_WEIGHT_HEAVY)/2) return FC_WEIGHT_ULTRABOLD; return FC_WEIGHT_HEAVY; } static int fcToQtWeight(int weight) { switch(weight) { case FC_WEIGHT_THIN: return 0; case FC_WEIGHT_ULTRALIGHT: return TQFont::Light>>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(width<FC_WIDTH_EXTRACONDENSED) return FC_WIDTH_ULTRACONDENSED; if(width<(FC_WIDTH_EXTRACONDENSED+FC_WIDTH_CONDENSED)/2) return FC_WIDTH_EXTRACONDENSED; if(width<(FC_WIDTH_CONDENSED+FC_WIDTH_SEMICONDENSED)/2) return FC_WIDTH_CONDENSED; if(width<(FC_WIDTH_SEMICONDENSED+FC_WIDTH_NORMAL)/2) return FC_WIDTH_SEMICONDENSED; if(width<(FC_WIDTH_NORMAL+FC_WIDTH_SEMIEXPANDED)/2) return FC_WIDTH_NORMAL; if(width<(FC_WIDTH_SEMIEXPANDED+FC_WIDTH_EXPANDED)/2) return FC_WIDTH_SEMIEXPANDED; if(width<(FC_WIDTH_EXPANDED+FC_WIDTH_EXTRAEXPANDED)/2) return FC_WIDTH_EXPANDED; if(width<(FC_WIDTH_EXTRAEXPANDED+FC_WIDTH_ULTRAEXPANDED)/2) return FC_WIDTH_EXTRAEXPANDED; return FC_WIDTH_ULTRAEXPANDED; } static int fcToQtWidth(int weight) { switch(weight) { case FC_WIDTH_ULTRACONDENSED: return TQFont::UltraCondensed; case FC_WIDTH_EXTRACONDENSED: return TQFont::ExtraCondensed; case FC_WIDTH_CONDENSED: return TQFont::Condensed; case FC_WIDTH_SEMICONDENSED: return TQFont::SemiCondensed; default: case FC_WIDTH_NORMAL: return TQFont::Unstretched; case FC_WIDTH_SEMIEXPANDED: return TQFont::SemiExpanded; case FC_WIDTH_EXPANDED: return TQFont::Expanded; case FC_WIDTH_EXTRAEXPANDED: return TQFont::ExtraExpanded; case FC_WIDTH_ULTRAEXPANDED: return TQFont::UltraExpanded; } } #endif static int fcSlant(int slant) { if(slant<FC_SLANT_ITALIC) return FC_SLANT_ROMAN; #ifdef KFI_HAVE_OBLIQUE if(slant<(FC_SLANT_ITALIC+FC_SLANT_OBLIQUE)/2) return FC_SLANT_ITALIC; return FC_SLANT_OBLIQUE; #else return FC_SLANT_ITALIC; #endif } static bool fcToQtSlant(int slant) { return FC_SLANT_ROMAN==slant ? false : true; } static int fcSpacing(int spacing) { if(spacing<FC_MONO) return FC_PROPORTIONAL; if(spacing<(FC_MONO+FC_CHARCELL)/2) return FC_MONO; return FC_CHARCELL; } static int strToWeight(const TQString &str, TQString &newStr) { if(0==str.find(i18n(KFI_WEIGHT_THIN), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_THIN).length()); return FC_WEIGHT_THIN; } if(0==str.find(i18n(KFI_WEIGHT_EXTRALIGHT), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_EXTRALIGHT).length()); return FC_WEIGHT_EXTRALIGHT; } if(0==str.find(i18n(KFI_WEIGHT_ULTRALIGHT), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_ULTRALIGHT).length()); return FC_WEIGHT_ULTRALIGHT; } if(0==str.find(i18n(KFI_WEIGHT_LIGHT), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_LIGHT).length()); return FC_WEIGHT_LIGHT; } if(0==str.find(i18n(KFI_WEIGHT_REGULAR), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_REGULAR).length()); return FC_WEIGHT_REGULAR; } if(0==str.find(i18n(KFI_WEIGHT_NORMAL), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_NORMAL).length()); return FC_WEIGHT_NORMAL; } if(0==str.find(i18n(KFI_WEIGHT_MEDIUM), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_MEDIUM).length()); return FC_WEIGHT_MEDIUM; } if(0==str.find(i18n(KFI_WEIGHT_DEMIBOLD), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_DEMIBOLD).length()); return FC_WEIGHT_SEMIBOLD; } if(0==str.find(i18n(KFI_WEIGHT_SEMIBOLD), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_SEMIBOLD).length()); return FC_WEIGHT_SEMIBOLD; } if(0==str.find(i18n(KFI_WEIGHT_BOLD), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_BOLD).length()); return FC_WEIGHT_BOLD; } if(0==str.find(i18n(KFI_WEIGHT_EXTRABOLD), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_EXTRABOLD).length()); return FC_WEIGHT_EXTRABOLD; } if(0==str.find(i18n(KFI_WEIGHT_ULTRABOLD), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_ULTRABOLD).length()); return FC_WEIGHT_ULTRABOLD; } if(0==str.find(i18n(KFI_WEIGHT_BLACK), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_BLACK).length()); return FC_WEIGHT_BLACK; } if(0==str.find(i18n(KFI_WEIGHT_HEAVY), 0, false)) { newStr=str.mid(i18n(KFI_WEIGHT_HEAVY).length()); return FC_WEIGHT_HEAVY; } newStr=str; return FC_WEIGHT_REGULAR; } #ifndef KFI_FC_NO_WIDTHS static int strToWidth(const TQString &str, TQString &newStr) { if(0==str.find(i18n(KFI_WIDTH_ULTRACONDENSED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_ULTRACONDENSED).length()); return FC_WIDTH_ULTRACONDENSED; } if(0==str.find(i18n(KFI_WIDTH_EXTRACONDENSED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_EXTRACONDENSED).length()); return FC_WIDTH_EXTRACONDENSED; } if(0==str.find(i18n(KFI_WIDTH_CONDENSED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_CONDENSED).length()); return FC_WIDTH_CONDENSED; } if(0==str.find(i18n(KFI_WIDTH_SEMICONDENSED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_SEMICONDENSED).length()); return FC_WIDTH_SEMICONDENSED; } if(0==str.find(i18n(KFI_WIDTH_NORMAL), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_NORMAL).length()); return FC_WIDTH_NORMAL; } if(0==str.find(i18n(KFI_WIDTH_SEMIEXPANDED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_SEMIEXPANDED).length()); return FC_WIDTH_SEMIEXPANDED; } if(0==str.find(i18n(KFI_WIDTH_EXPANDED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_EXPANDED).length()); return FC_WIDTH_EXPANDED; } if(0==str.find(i18n(KFI_WIDTH_EXTRAEXPANDED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_EXTRAEXPANDED).length()); return FC_WIDTH_EXTRAEXPANDED; } if(0==str.find(i18n(KFI_WIDTH_ULTRAEXPANDED), 0, false)) { newStr=str.mid(i18n(KFI_WIDTH_ULTRAEXPANDED).length()); return FC_WIDTH_ULTRAEXPANDED; } newStr=str; return FC_WIDTH_NORMAL; } #endif static int strToSlant(const TQString &str) { if(-1!=str.find(i18n(KFI_SLANT_ITALIC))) return FC_SLANT_ITALIC; if(-1!=str.find(i18n(KFI_SLANT_OBLIQUE))) return FC_SLANT_OBLIQUE; return FC_SLANT_ROMAN; } static void drawText(TQPainter &painter, int x, int y, int width, const TQString &str) { TQString s(str); bool addedElipses=false; width-=x*2; while(s.length()>3 && 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+offset<h) { XftDrawString16(xftDraw, xftCol, xftFont, x, y, str, 1); x+=extents.width+2; return true; } return false; } static bool drawString(TQPixmap &pix, XftDraw *xftDraw, XftFont *xftFont, XftColor *xftCol, const TQString &text, int x, int &y, int h, int offset) { XGlyphInfo extents; const FcChar16 *str=(FcChar16 *)(text.ucs2()); XftTextExtents16(pix.x11Display(), xftFont, str, text.length(), &extents); if(y+extents.height<h) XftDrawString16(xftDraw, xftCol, xftFont, x, y+extents.y, str, text.length()); if(extents.height>0) { 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+offset<h) { XftDrawGlyphs(xftDraw, xftCol, xftFont, x, y, &i, 1); x+=extents.width+2; return true; } return false; } inline int point2Pixel(int point) { return (point*TQPaintDevice::x11AppDpiX()+36)/72; } static bool hasStr(XftFont *font, TQString &str) { unsigned int slen=str.length(), ch; for(ch=0; ch<slen; ++ch) if(!FcCharSetHasChar(font->charset, 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; s<itsSizes.size(); ++s) if (itsSizes[s]<=fSize) bSize=itsSizes[s]; fSize=bSize; } unsigned int ch; xftFont=getFont(fSize, &pix); y=fSize; if(xftFont) { drawGlyphs=!hasStr(xftFont, text); if(!drawGlyphs) for(ch=0; ch<text.length(); ++ch) // Display char by char so that it wraps... if(!drawChar(pix, xftDraw, xftFont, &xftCol, text, ch, x, y, w, h, fSize, offset)) break; if(drawGlyphs) { FT_Face face=XftLockFace(xftFont); if(face) { for(int i=1; i<face->num_glyphs && y<w; ++i) // Glyph 0 is the NULL glyph if(!drawGlyph(pix, xftDraw, xftFont, &xftCol, i, x, y, w, h, fSize, offset)) break; XftUnlockFace(xftFont); } } } } else { TQString lowercase(getLowercaseLetters()), uppercase(getUppercaseLetters()), punctuation(getPunctuation()), title(itsDescriptiveName.isEmpty() ? i18n("ERROR: Could not determine font's name.") : itsDescriptiveName); if(1==itsSizes.size()) title=i18n("%1 [1 pixel]", "%1 [%n pixels]", itsSizes[0]).arg(title); painter.setFont(KGlobalSettings::generalFont()); painter.setPen(Qt::black); y=painter.fontMetrics().height(); drawText(painter, x, y, w-offset, title); y+=4; painter.drawLine(offset, y, w-(offset+1), y); y+=8; bool lc=true, uc=true, punc=true; xftFont=getFont(itsAlphaSize, &pix); if(xftFont) { lc=hasStr(xftFont, lowercase); uc=hasStr(xftFont, uppercase); punc=hasStr(xftFont, punctuation); drawGlyphs=!lc && !uc; if(!drawGlyphs) { if(lc) drawString(pix, xftDraw, xftFont, &xftCol, lowercase, x, y, h, offset); if(uc) drawString(pix, xftDraw, xftFont, &xftCol, uppercase, x, y, h, offset); if(punc) drawString(pix, xftDraw, xftFont, &xftCol, punctuation, x, y, h, offset); XftFontClose(pix.x11Display(), xftFont); if(lc || uc || punc) painter.drawLine(offset, y, w-(offset+1), y); y+=8; } TQString previewString(getPreviewString()); bool stop=false; if(!drawGlyphs) { if(!lc && uc) previewString=previewString.upper(); if(!uc && lc) previewString=previewString.lower(); } for(unsigned int s=0; s<itsSizes.size(); ++s) { xftFont=getFont(itsSizes[s], &pix); if(xftFont) { if(drawGlyphs) { FT_Face face=XftLockFace(xftFont); if(face) { int space=itsSizes[s]/10; XGlyphInfo extents; if(!space) space=1; for(int i=1; i<face->num_glyphs && y<w && !stop; ++i) { XftGlyphExtents(pix.x11Display(), xftFont, (const FT_UInt *)&i, 1, &extents); if(y+extents.height>h) stop=true; else { if(x+extents.width<w) XftDrawGlyphs(xftDraw, &xftCol, xftFont, x, y+extents.y, (const FT_UInt *)&i, 1); if(extents.width>0) 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() { KConfig 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) { KConfig 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 kfontchooser'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_KIO_FONTS_PROTOCOL==url.protocol()) { KIO::UDSEntry udsEntry; TQString 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! 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_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 *)(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; size<numSizes; size++) { itsSizes.push_back(face->available_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; l<constNumSizeRanges && !gotSizes; ++l) for(int i=0; i<constNumSizes; ++i) { double px; int iv; FcChar8 *str; f=getFont(constSizes[l][i], pix); if(f) { if(FcResultMatch==FcPatternGetDouble(f->pattern, 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 }