You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1503 lines
48 KiB
1503 lines
48 KiB
/* |
|
SPDX-FileCopyrightText: 2003-2007 Craig Drummond <craig@kde.org> |
|
SPDX-License-Identifier: GPL-2.0-or-later |
|
*/ |
|
|
|
#include "FcEngine.h" |
|
|
|
#include "File.h" |
|
#include <KConfig> |
|
#include <KConfigGroup> |
|
#include <QApplication> |
|
#include <QFile> |
|
#include <QFontDatabase> |
|
#include <QPainter> |
|
#include <QTextStream> |
|
#include <QX11Info> |
|
#include <X11/Xft/Xft.h> |
|
#include <X11/Xlib.h> |
|
#include <X11/extensions/Xrender.h> |
|
#include <math.h> |
|
#include <xcb/xcb_image.h> |
|
//#define KFI_FC_DEBUG |
|
|
|
#define KFI_PREVIEW_GROUP "KFontInst Preview Settings" |
|
#define KFI_PREVIEW_STRING_KEY "String" |
|
|
|
namespace KFI |
|
{ |
|
bool CFcEngine::theirFcDirty(true); |
|
const int CFcEngine::constScalableSizes[] = {8, 10, 12, 24, 36, 48, 64, 72, 96, 0}; |
|
const int CFcEngine::constDefaultAlphaSize = 24; |
|
|
|
static int fcToQtWeight(int weight) |
|
{ |
|
switch (weight) { |
|
case FC_WEIGHT_THIN: |
|
return 0; |
|
case FC_WEIGHT_EXTRALIGHT: |
|
return QFont::Light >> 1; |
|
case FC_WEIGHT_LIGHT: |
|
return QFont::Light; |
|
default: |
|
case FC_WEIGHT_REGULAR: |
|
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_DEMIBOLD: |
|
return QFont::DemiBold; |
|
case FC_WEIGHT_BOLD: |
|
return QFont::Bold; |
|
case FC_WEIGHT_EXTRABOLD: |
|
return (QFont::Bold + QFont::Black) >> 1; |
|
case FC_WEIGHT_BLACK: |
|
return QFont::Black; |
|
} |
|
} |
|
|
|
#ifndef KFI_FC_NO_WIDTHS |
|
static int fcToQtWidth(int weight) |
|
{ |
|
switch (weight) { |
|
case KFI_FC_WIDTH_ULTRACONDENSED: |
|
return QFont::UltraCondensed; |
|
case KFI_FC_WIDTH_EXTRACONDENSED: |
|
return QFont::ExtraCondensed; |
|
case KFI_FC_WIDTH_CONDENSED: |
|
return QFont::Condensed; |
|
case KFI_FC_WIDTH_SEMICONDENSED: |
|
return QFont::SemiCondensed; |
|
default: |
|
case KFI_FC_WIDTH_NORMAL: |
|
return QFont::Unstretched; |
|
case KFI_FC_WIDTH_SEMIEXPANDED: |
|
return QFont::SemiExpanded; |
|
case KFI_FC_WIDTH_EXPANDED: |
|
return QFont::Expanded; |
|
case KFI_FC_WIDTH_EXTRAEXPANDED: |
|
return QFont::ExtraExpanded; |
|
case KFI_FC_WIDTH_ULTRAEXPANDED: |
|
return QFont::UltraExpanded; |
|
} |
|
} |
|
#endif |
|
|
|
static bool fcToQtSlant(int slant) |
|
{ |
|
return FC_SLANT_ROMAN == slant ? false : true; |
|
} |
|
|
|
inline bool equal(double d1, double d2) |
|
{ |
|
return (fabs(d1 - d2) < 0.0001); |
|
} |
|
|
|
inline bool equalWeight(int a, int b) |
|
{ |
|
return a == b || FC::weight(a) == FC::weight(b); |
|
} |
|
|
|
#ifndef KFI_FC_NO_WIDTHS |
|
inline bool equalWidth(int a, int b) |
|
{ |
|
return a == b || FC::width(a) == FC::width(b); |
|
} |
|
#endif |
|
|
|
inline bool equalSlant(int a, int b) |
|
{ |
|
return a == b || FC::slant(a) == FC::slant(b); |
|
} |
|
|
|
static void closeFont(XftFont *&font) |
|
{ |
|
if (font) { |
|
XftFontClose(QX11Info::display(), font); |
|
} |
|
font = nullptr; |
|
} |
|
|
|
class CFcEngine::Xft |
|
{ |
|
public: |
|
struct Pix { |
|
Pix() |
|
: currentW(0) |
|
, currentH(0) |
|
, allocatedW(0) |
|
, allocatedH(0) |
|
{ |
|
} |
|
|
|
static int getSize(int s) |
|
{ |
|
static const int constBlockSize = 64; |
|
|
|
return ((s / constBlockSize) + (s % constBlockSize ? 1 : 0)) * constBlockSize; |
|
} |
|
|
|
bool allocate(int w, int h) |
|
{ |
|
int requiredW = getSize(w), requiredH = getSize(h); |
|
|
|
currentW = w; |
|
currentH = h; |
|
if (requiredW != allocatedW || requiredH != allocatedH) { |
|
free(); |
|
|
|
if (w && h) { |
|
allocatedW = requiredW; |
|
allocatedH = requiredH; |
|
x11 = XCreatePixmap(QX11Info::display(), RootWindow(QX11Info::display(), 0), allocatedW, allocatedH, DefaultDepth(QX11Info::display(), 0)); |
|
return true; |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
void free() |
|
{ |
|
if (allocatedW && allocatedH) { |
|
XFreePixmap(QX11Info::display(), x11); |
|
allocatedW = allocatedH = 0; |
|
} |
|
} |
|
|
|
int currentW, currentH, allocatedW, allocatedH; |
|
Pixmap x11; |
|
}; |
|
|
|
Xft(); |
|
~Xft(); |
|
|
|
bool init(const QColor &txt, const QColor &bgnd, int w, int h); |
|
void freeColors(); |
|
bool drawChar32Centre(XftFont *xftFont, quint32 ch, int w, int h) const; |
|
bool drawChar32(XftFont *xftFont, quint32 ch, int &x, int &y, int w, int h, int fontHeight, QRect &r) const; |
|
bool drawString(XftFont *xftFont, const QString &text, int x, int &y, int h) const; |
|
void drawString(const QString &text, int x, int &y, int h) const; |
|
bool drawGlyph(XftFont *xftFont, FT_UInt i, int &x, int &y, int w, int h, int fontHeight, bool oneLine, QRect &r) const; |
|
bool drawAllGlyphs(XftFont *xftFont, int fontHeight, int &x, int &y, int w, int h, bool oneLine = false, int max = -1, QRect *used = nullptr) const; |
|
bool drawAllChars(XftFont *xftFont, int fontHeight, int &x, int &y, int w, int h, bool oneLine = false, int max = -1, QRect *used = nullptr) const; |
|
QImage toImage(int w, int h) const; |
|
|
|
private: |
|
XftDraw *m_draw; |
|
XftColor m_txtColor, m_bgndColor; |
|
Pix m_pix; |
|
QImage::Format imageFormat; |
|
}; |
|
|
|
CFcEngine::Xft::Xft() |
|
{ |
|
m_draw = nullptr; |
|
m_txtColor.color.alpha = 0x0000; |
|
init(Qt::black, Qt::white, 64, 64); |
|
} |
|
|
|
CFcEngine::Xft::~Xft() |
|
{ |
|
freeColors(); |
|
if (m_draw) { |
|
XftDrawDestroy(m_draw); |
|
m_draw = nullptr; |
|
} |
|
} |
|
|
|
bool CFcEngine::Xft::init(const QColor &txt, const QColor &bnd, int w, int h) |
|
{ |
|
// FIXME: no Xft on Wayland |
|
if (!QX11Info::isPlatformX11()) { |
|
return false; |
|
} |
|
|
|
if (m_draw |
|
&& (txt.red() << 8 != m_txtColor.color.red || txt.green() << 8 != m_txtColor.color.green || txt.blue() << 8 != m_txtColor.color.blue |
|
|| bnd.red() << 8 != m_bgndColor.color.red || bnd.green() << 8 != m_bgndColor.color.green || bnd.blue() << 8 != m_bgndColor.color.blue)) { |
|
freeColors(); |
|
} |
|
|
|
if (0x0000 == m_txtColor.color.alpha) { |
|
XRenderColor xrenderCol; |
|
Visual *visual = DefaultVisual(QX11Info::display(), 0); |
|
Colormap colorMap = DefaultColormap(QX11Info::display(), 0); |
|
|
|
xrenderCol.red = bnd.red() << 8; |
|
xrenderCol.green = bnd.green() << 8; |
|
xrenderCol.blue = bnd.green() << 8; |
|
xrenderCol.alpha = 0xFFFF; |
|
XftColorAllocValue(QX11Info::display(), visual, colorMap, &xrenderCol, &m_bgndColor); |
|
xrenderCol.red = txt.red() << 8; |
|
xrenderCol.green = txt.green() << 8; |
|
xrenderCol.blue = txt.green() << 8; |
|
xrenderCol.alpha = 0xFFFF; |
|
XftColorAllocValue(QX11Info::display(), visual, colorMap, &xrenderCol, &m_txtColor); |
|
} |
|
|
|
XVisualInfo defaultVinfo; |
|
defaultVinfo.depth = DefaultDepth(QX11Info::display(), 0); |
|
// normal/failsafe |
|
imageFormat = QImage::Format_RGB32; // 24bit |
|
switch (defaultVinfo.depth) { |
|
case 32: |
|
imageFormat = QImage::Format_ARGB32_Premultiplied; |
|
break; |
|
case 30: |
|
imageFormat = QImage::Format_RGB30; |
|
break; |
|
case 16: |
|
imageFormat = QImage::Format_RGB16; |
|
break; |
|
case 8: |
|
imageFormat = QImage::Format_Grayscale8; |
|
break; |
|
default: |
|
break; |
|
} |
|
if (defaultVinfo.depth == 30 || defaultVinfo.depth == 32) { |
|
// detect correct format |
|
int num_vinfo = 0; |
|
defaultVinfo.visual = DefaultVisual(QX11Info::display(), 0); |
|
defaultVinfo.screen = 0; |
|
defaultVinfo.visualid = XVisualIDFromVisual(defaultVinfo.visual); |
|
XVisualInfo *vinfo = XGetVisualInfo(QX11Info::display(), VisualIDMask | VisualScreenMask | VisualDepthMask, &defaultVinfo, &num_vinfo); |
|
for (int i = 0; i < num_vinfo; ++i) { |
|
if (vinfo[i].visual == defaultVinfo.visual) { |
|
if (defaultVinfo.depth == 30) { |
|
if (vinfo[i].red_mask == 0x3ff) { |
|
imageFormat = QImage::Format_BGR30; |
|
} else if (vinfo[i].blue_mask == 0x3ff) { |
|
imageFormat = QImage::Format_RGB30; |
|
} |
|
} else if (defaultVinfo.depth == 32) { |
|
if (vinfo[i].blue_mask == 0xff) { |
|
imageFormat = QImage::Format_ARGB32_Premultiplied; |
|
} else if (vinfo[i].red_mask == 0x3ff) { |
|
imageFormat = QImage::Format_A2BGR30_Premultiplied; |
|
} else if (vinfo[i].blue_mask == 0x3ff) { |
|
imageFormat = QImage::Format_A2RGB30_Premultiplied; |
|
} |
|
} |
|
break; |
|
} |
|
} |
|
XFree(vinfo); |
|
} |
|
|
|
if (m_pix.allocate(w, h) && m_draw) { |
|
XftDrawChange(m_draw, m_pix.x11); |
|
} |
|
|
|
if (!m_draw) { |
|
m_draw = XftDrawCreate(QX11Info::display(), m_pix.x11, DefaultVisual(QX11Info::display(), 0), DefaultColormap(QX11Info::display(), 0)); |
|
} |
|
|
|
if (m_draw) { |
|
XftDrawRect(m_draw, &m_bgndColor, 0, 0, w, h); |
|
} |
|
|
|
return m_draw; |
|
} |
|
|
|
void CFcEngine::Xft::freeColors() |
|
{ |
|
// FIXME: no Xft on Wayland |
|
if (!QX11Info::isPlatformX11()) { |
|
return; |
|
} |
|
|
|
XftColorFree(QX11Info::display(), DefaultVisual(QX11Info::display(), 0), DefaultColormap(QX11Info::display(), 0), &m_txtColor); |
|
XftColorFree(QX11Info::display(), DefaultVisual(QX11Info::display(), 0), DefaultColormap(QX11Info::display(), 0), &m_bgndColor); |
|
m_txtColor.color.alpha = 0x0000; |
|
} |
|
|
|
bool CFcEngine::Xft::drawChar32Centre(XftFont *xftFont, quint32 ch, int w, int h) const |
|
{ |
|
if (XftCharExists(QX11Info::display(), xftFont, ch)) { |
|
XGlyphInfo extents; |
|
|
|
XftTextExtents32(QX11Info::display(), xftFont, &ch, 1, &extents); |
|
|
|
int rx(((w - extents.width) / 2) + extents.x), ry(((h - extents.height) / 2) + (extents.y)); |
|
|
|
XftDrawString32(m_draw, &m_txtColor, xftFont, rx, ry, &ch, 1); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
static const int constBorder = 2; |
|
|
|
bool CFcEngine::Xft::drawChar32(XftFont *xftFont, quint32 ch, int &x, int &y, int w, int h, int fontHeight, QRect &r) const |
|
{ |
|
r = QRect(); |
|
if (XftCharExists(QX11Info::display(), xftFont, ch)) { |
|
XGlyphInfo extents; |
|
|
|
XftTextExtents32(QX11Info::display(), xftFont, &ch, 1, &extents); |
|
|
|
if (extents.x > 0) { |
|
x += extents.x; |
|
} |
|
|
|
if (x + extents.width + constBorder > w) { |
|
x = 0; |
|
if (extents.x > 0) { |
|
x += extents.x; |
|
} |
|
y += fontHeight + constBorder; |
|
} |
|
|
|
if (y < h) { |
|
r = QRect(x - extents.x, y - extents.y, extents.width + constBorder, extents.height); |
|
|
|
XftDrawString32(m_draw, &m_txtColor, xftFont, x, y, &ch, 1); |
|
x += extents.xOff + constBorder; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
return true; |
|
} |
|
|
|
bool CFcEngine::Xft::drawString(XftFont *xftFont, const QString &text, int x, int &y, int h) const |
|
{ |
|
XGlyphInfo extents; |
|
const FcChar16 *str = (FcChar16 *)(text.utf16()); |
|
|
|
XftTextExtents16(QX11Info::display(), xftFont, str, text.length(), &extents); |
|
if (y + extents.height <= h) { |
|
XftDrawString16(m_draw, &m_txtColor, xftFont, x, y + extents.y, str, text.length()); |
|
} |
|
if (extents.height > 0) { |
|
y += extents.height; |
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
void CFcEngine::Xft::drawString(const QString &text, int x, int &y, int h) const |
|
{ |
|
QFont qt(QFontDatabase::systemFont(QFontDatabase::GeneralFont)); |
|
XftFont *xftFont = XftFontOpen(QX11Info::display(), |
|
0, |
|
FC_FAMILY, |
|
FcTypeString, |
|
(const FcChar8 *)(qt.family().toUtf8().data()), |
|
FC_WEIGHT, |
|
FcTypeInteger, |
|
qt.bold() ? FC_WEIGHT_BOLD : FC_WEIGHT_REGULAR, |
|
FC_SLANT, |
|
FcTypeInteger, |
|
qt.italic() ? FC_SLANT_ITALIC : FC_SLANT_ROMAN, |
|
FC_SIZE, |
|
FcTypeDouble, |
|
(double)qt.pointSize(), |
|
NULL); |
|
|
|
if (xftFont) { |
|
drawString(xftFont, text, x, y, h); |
|
closeFont(xftFont); |
|
} |
|
} |
|
|
|
bool CFcEngine::Xft::drawGlyph(XftFont *xftFont, FT_UInt i, int &x, int &y, int w, int h, int fontHeight, bool oneLine, QRect &r) const |
|
{ |
|
XGlyphInfo extents; |
|
|
|
XftGlyphExtents(QX11Info::display(), xftFont, &i, 1, &extents); |
|
|
|
if (0 == extents.width || 0 == extents.height) { |
|
r = QRect(0, 0, 0, 0); |
|
return true; |
|
} |
|
|
|
if (x + extents.width + 2 > w) { |
|
if (oneLine) { |
|
return false; |
|
} |
|
|
|
x = 0; |
|
y += fontHeight + 2; |
|
} |
|
|
|
if (y < h) { |
|
XftDrawGlyphs(m_draw, &m_txtColor, xftFont, x, y, &i, 1); |
|
r = QRect(x - extents.x, y - extents.y, extents.width + constBorder, extents.height); |
|
x += extents.width + 2; |
|
|
|
return true; |
|
} |
|
return false; |
|
} |
|
|
|
bool CFcEngine::Xft::drawAllGlyphs(XftFont *xftFont, int fontHeight, int &x, int &y, int w, int h, bool oneLine, int max, QRect *used) const |
|
{ |
|
bool rv(false); |
|
|
|
if (xftFont) { |
|
FT_Face face = XftLockFace(xftFont); |
|
|
|
if (face) { |
|
int space(fontHeight / 10), drawn(0); |
|
QRect r; |
|
|
|
if (!space) { |
|
space = 1; |
|
} |
|
|
|
rv = true; |
|
y += fontHeight; |
|
for (int i = 1; i < face->num_glyphs && y < h; ++i) { |
|
if (drawGlyph(xftFont, i, x, y, w, h, fontHeight, oneLine, r)) { |
|
if (r.height() > 0) { |
|
if (used) { |
|
if (used->isEmpty()) { |
|
*used = r; |
|
} else { |
|
*used = used->united(r); |
|
} |
|
} |
|
if (max > 0 && ++drawn >= max) { |
|
break; |
|
} |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
|
|
if (oneLine) { |
|
x = 0; |
|
} |
|
XftUnlockFace(xftFont); |
|
} |
|
} |
|
|
|
return rv; |
|
} |
|
|
|
bool CFcEngine::Xft::drawAllChars(XftFont *xftFont, int fontHeight, int &x, int &y, int w, int h, bool oneLine, int max, QRect *used) const |
|
{ |
|
bool rv(false); |
|
|
|
if (xftFont) { |
|
FT_Face face = XftLockFace(xftFont); |
|
|
|
if (face) { |
|
int space(fontHeight / 10), drawn(0); |
|
QRect r; |
|
|
|
if (!space) { |
|
space = 1; |
|
} |
|
|
|
rv = true; |
|
y += fontHeight; |
|
|
|
FT_Select_Charmap(face, FT_ENCODING_UNICODE); |
|
for (int cmap = 0; cmap < face->num_charmaps; ++cmap) { |
|
if (face->charmaps[cmap] && FT_ENCODING_ADOBE_CUSTOM == face->charmaps[cmap]->encoding) { |
|
FT_Select_Charmap(face, FT_ENCODING_ADOBE_CUSTOM); |
|
break; |
|
} |
|
} |
|
|
|
for (unsigned int i = 1; i < 65535 && y < h; ++i) { |
|
int glyph = FT_Get_Char_Index(face, i); |
|
|
|
if (glyph) { |
|
if (drawGlyph(xftFont, glyph, x, y, w, h, fontHeight, oneLine, r)) { |
|
if (r.height() > 0) { |
|
if (used) { |
|
if (used->isEmpty()) { |
|
*used = r; |
|
} else { |
|
*used = used->united(r); |
|
} |
|
} |
|
if (max > 0 && ++drawn >= max) { |
|
break; |
|
} |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (oneLine) { |
|
x = 0; |
|
} |
|
XftUnlockFace(xftFont); |
|
} |
|
} |
|
|
|
return rv; |
|
} |
|
|
|
void cleanupXImage(void *data) |
|
{ |
|
xcb_image_destroy((xcb_image_t *)data); |
|
} |
|
|
|
QImage CFcEngine::Xft::toImage(int w, int h) const |
|
{ |
|
Q_UNUSED(w) |
|
Q_UNUSED(h) |
|
|
|
if (!XftDrawPicture(m_draw)) { |
|
return QImage(); |
|
} |
|
xcb_image_t *xImage = xcb_image_get(QX11Info::connection(), m_pix.x11, 0, 0, m_pix.currentW, m_pix.currentH, ~0, XCB_IMAGE_FORMAT_Z_PIXMAP); |
|
if (!xImage) { |
|
return QImage(); |
|
} |
|
if (imageFormat == QImage::Format_RGB32) { |
|
// the RGB32 format requires data format 0xffRRGGBB, ensure that this fourth byte really is 0xff |
|
// (i.e. when using NVidia proprietary driver) |
|
auto lData = reinterpret_cast<quint32 *>(xImage->data); |
|
for (size_t iIter = 0; iIter < (xImage->stride / 4) * xImage->height; iIter++) { |
|
lData[iIter] |= 0xff000000; |
|
} |
|
} |
|
return QImage(xImage->data, xImage->width, xImage->height, xImage->stride, imageFormat, &cleanupXImage, xImage); |
|
} |
|
|
|
inline int point2Pixel(int point) |
|
{ |
|
return (point * QX11Info::appDpiX() + 36) / 72; |
|
} |
|
|
|
static bool hasStr(XftFont *font, QString &str) |
|
{ |
|
unsigned int slen = str.length(), ch; |
|
|
|
for (ch = 0; ch < slen; ++ch) { |
|
if (!FcCharSetHasChar(font->charset, str[ch].unicode())) { |
|
return false; |
|
} |
|
} |
|
return true; |
|
} |
|
|
|
static QString usableStr(XftFont *font, QString &str) |
|
{ |
|
unsigned int slen = str.length(), ch; |
|
QString newStr; |
|
|
|
for (ch = 0; ch < slen; ++ch) { |
|
if (FcCharSetHasChar(font->charset, str[ch].unicode())) { |
|
newStr += str[ch]; |
|
} |
|
} |
|
return newStr; |
|
} |
|
|
|
static bool isFileName(const QString &name, quint32 style) |
|
{ |
|
return QChar('/') == name[0] || KFI_NO_STYLE_INFO == style; |
|
} |
|
|
|
static void setTransparentBackground(QImage &img, const QColor &col) |
|
{ |
|
// Convert background to transparent, and text to correct colour... |
|
img = img.convertToFormat(QImage::Format_ARGB32); |
|
for (int x = 0; x < img.width(); ++x) { |
|
for (int y = 0; y < img.height(); ++y) { |
|
int v(qRed(img.pixel(x, y))); |
|
img.setPixel(x, y, qRgba(qMin(col.red() + v, 255), qMin(col.green() + v, 255), qMin(col.blue() + v, 255), 255 - v)); |
|
} |
|
} |
|
} |
|
|
|
CFcEngine::CFcEngine(bool init) |
|
: m_index(-1) |
|
, m_indexCount(1) |
|
, m_alphaSizeIndex(-1) |
|
, m_previewString(getDefaultPreviewString()) |
|
, m_xft(nullptr) |
|
{ |
|
if (init) { |
|
reinit(); |
|
} |
|
} |
|
|
|
CFcEngine::~CFcEngine() |
|
{ |
|
// Clear any fonts that may have been added... |
|
FcConfigAppFontClear(FcConfigGetCurrent()); |
|
delete m_xft; |
|
} |
|
|
|
void CFcEngine::readConfig(KConfig &cfg) |
|
{ |
|
cfg.group(KFI_PREVIEW_GROUP).readEntry(KFI_PREVIEW_STRING_KEY, getDefaultPreviewString()); |
|
} |
|
|
|
void CFcEngine::writeConfig(KConfig &cfg) |
|
{ |
|
cfg.group(KFI_PREVIEW_GROUP).writeEntry(KFI_PREVIEW_STRING_KEY, m_previewString); |
|
} |
|
|
|
QImage CFcEngine::drawPreview(const QString &name, quint32 style, int faceNo, const QColor &txt, const QColor &bgnd, int h) |
|
{ |
|
QImage img; |
|
|
|
if (!name.isEmpty() && ((name == m_name && style == m_style && File::equalIndex(faceNo, m_index)) || parse(name, style, faceNo))) { |
|
static const int constOffset = 2; |
|
static const int constInitialWidth = 1536; |
|
|
|
getSizes(); |
|
|
|
if (!m_sizes.isEmpty()) { |
|
// |
|
// Calculate size of text... |
|
int fSize = ((int)(h * 0.75)) - 2, origHeight(0); |
|
bool needAlpha(bgnd.alpha() < 255); |
|
|
|
if (!m_scalable) // Then need to get nearest size... |
|
{ |
|
int bSize = 0; |
|
|
|
for (int s = 0; s < m_sizes.size(); ++s) { |
|
if (m_sizes[s] <= fSize || 0 == bSize) { |
|
bSize = m_sizes[s]; |
|
} |
|
} |
|
fSize = bSize; |
|
if (bSize > h) { |
|
origHeight = h; |
|
h = bSize + 8; |
|
} |
|
} |
|
|
|
if (xft()->init(needAlpha ? Qt::black : txt, needAlpha ? Qt::white : bgnd, constInitialWidth, h)) { |
|
XftFont *xftFont = getFont(fSize); |
|
QString text(m_previewString); |
|
|
|
if (xftFont) { |
|
bool rv = false; |
|
int usedWidth = 0; |
|
|
|
if (hasStr(xftFont, text) || hasStr(xftFont, text = text.toUpper()) || hasStr(xftFont, text = text.toLower())) { |
|
XGlyphInfo extents; |
|
const FcChar16 *str = (FcChar16 *)(text.utf16()); |
|
|
|
XftTextExtents16(QX11Info::display(), xftFont, str, text.length(), &extents); |
|
|
|
int y = (h - extents.height) / 2; |
|
|
|
rv = xft()->drawString(xftFont, text, constOffset, y, h); |
|
usedWidth = extents.width; |
|
} else { |
|
int x = constOffset, y = constOffset; |
|
QRect used; |
|
|
|
rv = xft()->drawAllGlyphs(xftFont, fSize, x, y, constInitialWidth, h, true, text.length(), &used); |
|
if (rv) { |
|
usedWidth = used.width(); |
|
} |
|
} |
|
|
|
if (rv) { |
|
img = xft()->toImage(constInitialWidth, h); |
|
if (!img.isNull()) { |
|
if (origHeight) { |
|
int width = (int)((usedWidth * (double)(((double)h) / ((double)origHeight))) + 0.5); |
|
img = |
|
img.scaledToHeight(origHeight, Qt::SmoothTransformation) |
|
.copy(0, 0, width + (2 * constOffset) < constInitialWidth ? width + (2 * constOffset) : constInitialWidth, origHeight); |
|
} else { |
|
img = img.copy(0, 0, usedWidth + (2 * constOffset) < constInitialWidth ? usedWidth + (2 * constOffset) : constInitialWidth, h); |
|
} |
|
|
|
if (needAlpha) { |
|
setTransparentBackground(img, txt); |
|
} |
|
} |
|
} |
|
closeFont(xftFont); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return img; |
|
} |
|
|
|
QImage CFcEngine::draw(const QString &name, quint32 style, int faceNo, const QColor &txt, const QColor &bgnd, int fSize, const QString &text_) |
|
{ |
|
QImage img; |
|
QString text = text_; |
|
|
|
if (!name.isEmpty() && ((name == m_name && style == m_style) || parse(name, style, faceNo))) { |
|
getSizes(); |
|
|
|
if (!m_sizes.isEmpty()) { |
|
if (!m_scalable) // Then need to get nearest size... |
|
{ |
|
int bSize = 0; |
|
|
|
for (int s = 0; s < m_sizes.size(); ++s) { |
|
if (m_sizes[s] <= fSize || 0 == bSize) { |
|
bSize = m_sizes[s]; |
|
} |
|
} |
|
fSize = bSize; |
|
} |
|
|
|
int h = fSize; |
|
int w = 0; |
|
|
|
XftFont *xftFont = getFont(fSize); |
|
|
|
if (xftFont) { |
|
XGlyphInfo extents; |
|
const FcChar16 *str = (FcChar16 *)(text.utf16()); |
|
|
|
XftTextExtents16(QX11Info::display(), xftFont, str, text.length(), &extents); |
|
|
|
h = extents.height; |
|
w = extents.width; |
|
|
|
bool needAlpha(bgnd.alpha() < 255); |
|
|
|
if (xft()->init(needAlpha ? Qt::black : txt, needAlpha ? Qt::white : bgnd, w, h)) { |
|
bool rv = false; |
|
|
|
if (hasStr(xftFont, text) || hasStr(xftFont, text = text.toUpper()) || hasStr(xftFont, text = text.toLower())) { |
|
XGlyphInfo extents; |
|
const FcChar16 *str = (FcChar16 *)(text.utf16()); |
|
|
|
XftTextExtents16(QX11Info::display(), xftFont, str, text.length(), &extents); |
|
|
|
int x = 0, y = 0; |
|
|
|
rv = xft()->drawString(xftFont, text, x, y, h); |
|
} else { |
|
int x = 0, y = 0; |
|
QRect used; |
|
|
|
rv = xft()->drawAllGlyphs(xftFont, h, x, y, w, h, true, text.length(), &used); |
|
} |
|
|
|
if (rv) { |
|
img = xft()->toImage(w, h); |
|
if (!img.isNull()) { |
|
img = img.copy(0, 0, w, h); |
|
|
|
if (needAlpha) { |
|
setTransparentBackground(img, txt); |
|
} |
|
} |
|
} |
|
} |
|
closeFont(xftFont); |
|
} |
|
} |
|
} |
|
|
|
return img; |
|
} |
|
|
|
QImage CFcEngine::draw(const QString &name, |
|
quint32 style, |
|
int faceNo, |
|
const QColor &txt, |
|
const QColor &bgnd, |
|
int w, |
|
int h, |
|
bool thumb, |
|
const QList<TRange> &range, |
|
QList<TChar> *chars) |
|
{ |
|
QImage img; |
|
const qreal dpr = qApp->devicePixelRatio(); |
|
w = w * dpr; |
|
h = h * dpr; |
|
bool rv = false; |
|
|
|
if (chars) { |
|
chars->clear(); |
|
} |
|
|
|
if (!name.isEmpty() && ((name == m_name && style == m_style && File::equalIndex(faceNo, m_index)) || parse(name, style, faceNo))) { |
|
// |
|
// We allow kio_thumbnail to cache our thumbs. Normal is 128x128, and large is 256x256 |
|
// ...if kio_thumbnail asks us for a bigger size, then it is probably the file info dialog, in |
|
// which case treat it as a normal preview... |
|
if (thumb && (h > 256 || w != h)) { |
|
thumb = false; |
|
} |
|
|
|
int x = 0, y = 0; |
|
|
|
getSizes(); |
|
|
|
if (!m_sizes.isEmpty()) { |
|
int imgWidth(thumb && m_scalable ? w * 4 : w), imgHeight(thumb && m_scalable ? h * 4 : h); |
|
bool needAlpha(bgnd.alpha() < 255); |
|
|
|
if (xft()->init(needAlpha ? Qt::black : txt, needAlpha ? Qt::white : bgnd, imgWidth, imgHeight)) { |
|
XftFont *xftFont = nullptr; |
|
int line1Pos(0), line2Pos(0); |
|
QRect used(0, 0, 0, 0); |
|
|
|
if (thumb) { |
|
QString text(m_scalable ? i18nc("First letter of the alphabet (in upper then lower case)", "Aa") |
|
: i18nc("All letters of the alphabet (in upper/lower case pairs), followed by numbers", |
|
"AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789")); |
|
// |
|
// Calculate size of text... |
|
int fSize = h; |
|
|
|
if (!m_scalable) // Then need to get nearest size... |
|
{ |
|
int bSize = 0; |
|
|
|
for (int s = 0; s < m_sizes.size(); ++s) { |
|
if (m_sizes[s] <= fSize || 0 == bSize) { |
|
bSize = m_sizes[s]; |
|
} |
|
} |
|
fSize = bSize; |
|
} |
|
|
|
xftFont = getFont(fSize); |
|
|
|
if (xftFont) { |
|
QString valid(usableStr(xftFont, text)); |
|
|
|
y = fSize; |
|
rv = true; |
|
|
|
if (m_scalable) { |
|
if (valid.length() != text.length()) { |
|
text = getPunctuation().mid(1, 2); // '1' '2' |
|
valid = usableStr(xftFont, text); |
|
} |
|
} else if (valid.length() < (text.length() / 2)) { |
|
for (int i = 0; i < 3; ++i) { |
|
text = 0 == i ? getUppercaseLetters() : 1 == i ? getLowercaseLetters() : getPunctuation(); |
|
valid = usableStr(xftFont, text); |
|
|
|
if (valid.length() >= (text.length() / 2)) { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (m_scalable ? valid.length() != text.length() : valid.length() < (text.length() / 2)) { |
|
xft()->drawAllChars(xftFont, fSize, x, y, imgWidth, imgHeight, true, m_scalable ? 2 : -1, m_scalable ? &used : nullptr); |
|
} else { |
|
QVector<uint> ucs4(valid.toUcs4()); |
|
QRect r; |
|
|
|
for (int ch = 0; ch < ucs4.size(); ++ch) { // Display char by char so wraps... |
|
if (xft()->drawChar32(xftFont, ucs4[ch], x, y, imgWidth, imgHeight, fSize, r)) { |
|
if (used.isEmpty()) { |
|
used = r; |
|
} else { |
|
used = used.united(r); |
|
} |
|
} else { |
|
break; |
|
} |
|
} |
|
} |
|
|
|
closeFont(xftFont); |
|
} |
|
} else if (0 == range.count()) { |
|
QString lowercase(getLowercaseLetters()), uppercase(getUppercaseLetters()), punctuation(getPunctuation()); |
|
|
|
drawName(x, y, h); |
|
y += 4; |
|
line1Pos = y; |
|
y += 8; |
|
|
|
xftFont = getFont(alphaSize()); |
|
if (xftFont) { |
|
bool lc(hasStr(xftFont, lowercase)), uc(hasStr(xftFont, uppercase)), drawGlyphs = !lc && !uc; |
|
|
|
if (drawGlyphs) { |
|
y -= 8; |
|
} else { |
|
QString validPunc(usableStr(xftFont, punctuation)); |
|
bool punc(validPunc.length() >= (punctuation.length() / 2)); |
|
|
|
if (lc) { |
|
xft()->drawString(xftFont, lowercase, x, y, h); |
|
} |
|
if (uc) { |
|
xft()->drawString(xftFont, uppercase, x, y, h); |
|
} |
|
if (punc) { |
|
xft()->drawString(xftFont, validPunc, x, y, h); |
|
} |
|
if (lc || uc || punc) { |
|
line2Pos = y + 2; |
|
} |
|
y += 8; |
|
} |
|
|
|
QString previewString(getPreviewString()); |
|
|
|
if (!drawGlyphs) { |
|
if (!lc && uc) { |
|
previewString = previewString.toUpper(); |
|
} |
|
if (!uc && lc) { |
|
previewString = previewString.toLower(); |
|
} |
|
} |
|
|
|
closeFont(xftFont); |
|
for (int s = 0; s < m_sizes.size(); ++s) { |
|
if ((xftFont = getFont(m_sizes[s]))) { |
|
int fontHeight = xftFont->ascent + xftFont->descent; |
|
|
|
rv = true; |
|
if (drawGlyphs) { |
|
xft()->drawAllChars(xftFont, fontHeight, x, y, w, h, m_sizes.count() > 1); |
|
} else { |
|
xft()->drawString(xftFont, previewString, x, y, h); |
|
} |
|
closeFont(xftFont); |
|
} |
|
} |
|
} |
|
} else if (1 == range.count() && (range.first().null() || 0 == range.first().to)) { |
|
if (range.first().null()) { |
|
drawName(x, y, h); |
|
|
|
if ((xftFont = getFont(alphaSize()))) { |
|
int fontHeight = xftFont->ascent + xftFont->descent; |
|
|
|
xft()->drawAllGlyphs(xftFont, fontHeight, x, y, w, h, false); |
|
rv = true; |
|
closeFont(xftFont); |
|
} |
|
} else if ((xftFont = getFont(int(imgWidth * 0.85)))) { |
|
rv = xft()->drawChar32Centre(xftFont, (*(range.begin())).from, imgWidth, imgHeight); |
|
closeFont(xftFont); |
|
} |
|
} else { |
|
QList<TRange>::ConstIterator it(range.begin()), end(range.end()); |
|
|
|
if ((xftFont = getFont(alphaSize()))) { |
|
rv = true; |
|
drawName(x, y, h); |
|
y += alphaSize(); |
|
|
|
bool stop = false; |
|
int fontHeight = xftFont->ascent + xftFont->descent, xOrig(x), yOrig(y); |
|
QRect r; |
|
|
|
for (it = range.begin(); it != end && !stop; ++it) { |
|
for (quint32 c = (*it).from; c <= (*it).to && !stop; ++c) { |
|
if (xft()->drawChar32(xftFont, c, x, y, w, h, fontHeight, r)) { |
|
if (chars && !r.isEmpty()) { |
|
chars->append(TChar(r, c)); |
|
} |
|
} else { |
|
stop = true; |
|
} |
|
} |
|
} |
|
|
|
if (x == xOrig && y == yOrig) { |
|
// No characters found within the selected range... |
|
xft()->drawString(i18n("No characters found."), x, y, h); |
|
rv = true; |
|
} |
|
closeFont(xftFont); |
|
} |
|
} |
|
|
|
if (rv) { |
|
img = xft()->toImage(imgWidth, imgHeight); |
|
if (!img.isNull() && line1Pos) { |
|
QPainter p(&img); |
|
|
|
p.setPen(txt); |
|
p.drawLine(0, line1Pos, w - 1, line1Pos); |
|
if (line2Pos) { |
|
p.drawLine(0, line2Pos, w - 1, line2Pos); |
|
} |
|
} |
|
|
|
if (!img.isNull()) { |
|
if (m_scalable && !used.isEmpty() && (used.width() < imgWidth || used.height() < imgHeight)) { |
|
img = img.copy(used); |
|
} |
|
if (needAlpha) { |
|
setTransparentBackground(img, txt); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
img.setDevicePixelRatio(dpr); |
|
return img; |
|
} |
|
|
|
QString CFcEngine::getDefaultPreviewString() |
|
{ |
|
return i18nc("A sentence that uses all of the letters of the alphabet", "The quick brown fox jumps over the lazy dog"); |
|
} |
|
|
|
QString CFcEngine::getUppercaseLetters() |
|
{ |
|
return i18nc("All of the letters of the alphabet, uppercase", "ABCDEFGHIJKLMNOPQRSTUVWXYZ"); |
|
} |
|
|
|
QString CFcEngine::getLowercaseLetters() |
|
{ |
|
return i18nc("All of the letters of the alphabet, lowercase", "abcdefghijklmnopqrstuvwxyz"); |
|
} |
|
|
|
QString CFcEngine::getPunctuation() |
|
{ |
|
return i18nc("Numbers and characters", "0123456789.:,;(*!?'/\\\")£$€%^&-+@~#<>{}[]"); // krazy:exclude=i18ncheckarg |
|
} |
|
|
|
#ifdef KFI_USE_TRANSLATED_FAMILY_NAME |
|
// |
|
// Try to get the 'string' that matches the users KDE locale.. |
|
QString CFcEngine::getFcLangString(FcPattern *pat, const char *val, const char *valLang) |
|
{ |
|
QString rv; |
|
QStringList kdeLangs = KLocale::global()->languageList(), fontLangs; |
|
QStringList::ConstIterator it(kdeLangs.begin()), end(kdeLangs.end()); |
|
|
|
// Create list of langs that this font's 'val' is encoded in... |
|
for (int i = 0; true; ++i) { |
|
QString lang = getFcString(pat, valLang, i); |
|
|
|
if (lang.isEmpty()) |
|
break; |
|
else |
|
fontLangs.append(lang); |
|
} |
|
|
|
// Now go through the user's KDE locale, and try to find a font match... |
|
for (; it != end; ++it) { |
|
int index = fontLangs.findIndex(*it); |
|
|
|
if (-1 != index) { |
|
rv = getFcString(pat, val, index); |
|
|
|
if (!rv.isEmpty()) |
|
break; |
|
} |
|
} |
|
|
|
if (rv.isEmpty()) |
|
rv = getFcString(pat, val, 0); |
|
return rv; |
|
} |
|
#endif |
|
|
|
QFont CFcEngine::getQFont(const QString &family, quint32 style, int size) |
|
{ |
|
int weight, width, slant; |
|
|
|
FC::decomposeStyleVal(style, weight, width, slant); |
|
|
|
QFont font(family, size, fcToQtWeight(weight), fcToQtSlant(slant)); |
|
|
|
#ifndef KFI_FC_NO_WIDTHS |
|
font.setStretch(fcToQtWidth(width)); |
|
#endif |
|
return font; |
|
} |
|
|
|
bool CFcEngine::parse(const QString &name, quint32 style, int face) |
|
{ |
|
if (name.isEmpty()) { |
|
return false; |
|
} |
|
|
|
reinit(); |
|
|
|
m_name = name; |
|
m_style = style; |
|
m_sizes.clear(); |
|
m_installed = !isFileName(name, style); |
|
|
|
if (!m_installed) { |
|
int count; |
|
FcPattern *pat = FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(m_name).data()), face < 1 ? 0 : face, nullptr, &count); |
|
if (!pat) { |
|
return false; |
|
} |
|
m_descriptiveName = FC::createName(pat); |
|
FcPatternDestroy(pat); |
|
} else { |
|
m_descriptiveName = FC::createName(m_name, m_style); |
|
} |
|
|
|
m_index = face < 1 ? 0 : face; |
|
|
|
if (!m_installed) { // Then add to fontconfig's list, so that Xft can display it... |
|
addFontFile(m_name); |
|
} |
|
return true; |
|
} |
|
|
|
XftFont *CFcEngine::queryFont() |
|
{ |
|
static const int constQuerySize = 8; |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug(); |
|
#endif |
|
|
|
XftFont *f = getFont(constQuerySize); |
|
|
|
if (f && !isCorrect(f, true)) { |
|
closeFont(f); |
|
} |
|
|
|
if (m_installed && !f) { |
|
// Perhaps it is a newly installed font? If so try re-initialising fontconfig... |
|
theirFcDirty = true; |
|
reinit(); |
|
|
|
f = getFont(constQuerySize); |
|
|
|
// This time don't bother checking family - we've re-inited fc anyway, so things should be |
|
// up to date... And for "Symbol" Fc returns "Standard Symbols L", so wont match anyway! |
|
if (f && !isCorrect(f, false)) { |
|
closeFont(f); |
|
} |
|
} |
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "ret" << (int)f; |
|
#endif |
|
return f; |
|
} |
|
|
|
XftFont *CFcEngine::getFont(int size) |
|
{ |
|
XftFont *f = nullptr; |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << m_name << ' ' << m_style << ' ' << size; |
|
#endif |
|
|
|
if (!QX11Info::isPlatformX11()) { |
|
// FIXME: no Xft on Wayland |
|
} else if (m_installed) { |
|
int weight, width, slant; |
|
|
|
FC::decomposeStyleVal(m_style, weight, width, slant); |
|
|
|
#ifndef KFI_FC_NO_WIDTHS |
|
if (KFI_NULL_SETTING != width) { |
|
f = XftFontOpen(QX11Info::display(), |
|
0, |
|
FC_FAMILY, |
|
FcTypeString, |
|
(const FcChar8 *)(m_name.toUtf8().data()), |
|
FC_WEIGHT, |
|
FcTypeInteger, |
|
weight, |
|
FC_SLANT, |
|
FcTypeInteger, |
|
slant, |
|
FC_WIDTH, |
|
FcTypeInteger, |
|
width, |
|
FC_PIXEL_SIZE, |
|
FcTypeDouble, |
|
(double)size, |
|
NULL); |
|
} else { |
|
#endif |
|
f = XftFontOpen(QX11Info::display(), |
|
0, |
|
FC_FAMILY, |
|
FcTypeString, |
|
(const FcChar8 *)(m_name.toUtf8().data()), |
|
FC_WEIGHT, |
|
FcTypeInteger, |
|
weight, |
|
FC_SLANT, |
|
FcTypeInteger, |
|
slant, |
|
FC_PIXEL_SIZE, |
|
FcTypeDouble, |
|
(double)size, |
|
NULL); |
|
} |
|
} else { |
|
FcPattern *pattern = FcPatternBuild(nullptr, |
|
FC_FILE, |
|
FcTypeString, |
|
QFile::encodeName(m_name).constData(), |
|
FC_INDEX, |
|
FcTypeInteger, |
|
m_index < 0 ? 0 : m_index, |
|
FC_PIXEL_SIZE, |
|
FcTypeDouble, |
|
(double)size, |
|
NULL); |
|
f = XftFontOpenPattern(QX11Info::display(), pattern); |
|
} |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "ret: " << (int)f; |
|
#endif |
|
|
|
return f; |
|
} |
|
|
|
bool CFcEngine::isCorrect(XftFont *f, bool checkFamily) |
|
{ |
|
int iv, weight, width, slant; |
|
FcChar8 *str; |
|
|
|
if (m_installed) { |
|
FC::decomposeStyleVal(m_style, weight, width, slant); |
|
} |
|
|
|
#ifdef KFI_FC_DEBUG |
|
QString xxx; |
|
QTextStream s(&xxx); |
|
if (f) { |
|
if (m_installed) { |
|
s << "weight:"; |
|
if (FcResultMatch == FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv)) |
|
s << iv << '/' << weight; |
|
else |
|
s << "no"; |
|
|
|
s << " slant:"; |
|
if (FcResultMatch == FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv)) |
|
s << iv << '/' << slant; |
|
else |
|
s << "no"; |
|
|
|
s << " width:"; |
|
if (FcResultMatch == FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv)) |
|
s << iv << '/' << width; |
|
else |
|
s << "no"; |
|
|
|
s << " fam:"; |
|
if (checkFamily) |
|
if (FcResultMatch == FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str) |
|
s << QString::fromUtf8((char *)str) << '/' << m_name; |
|
else |
|
s << "no"; |
|
else |
|
s << "ok"; |
|
} else |
|
s << "NOT Installed... "; |
|
} else |
|
s << "No font!!! "; |
|
qDebug() << "isCorrect? " << xxx; |
|
#endif |
|
|
|
return f ? m_installed ? FcResultMatch == FcPatternGetInteger(f->pattern, FC_WEIGHT, 0, &iv) && equalWeight(iv, weight) |
|
&& FcResultMatch == FcPatternGetInteger(f->pattern, FC_SLANT, 0, &iv) && equalSlant(iv, slant) && |
|
#ifndef KFI_FC_NO_WIDTHS |
|
(KFI_NULL_SETTING == width || (FcResultMatch == FcPatternGetInteger(f->pattern, FC_WIDTH, 0, &iv) && equalWidth(iv, width))) && |
|
#endif |
|
(!checkFamily || (FcResultMatch == FcPatternGetString(f->pattern, FC_FAMILY, 0, &str) && str && QString::fromUtf8((char *)str) == m_name)) |
|
: (m_index < 0 || (FcResultMatch == FcPatternGetInteger(f->pattern, FC_INDEX, 0, &iv) && m_index == iv)) |
|
&& FcResultMatch == FcPatternGetString(f->pattern, FC_FILE, 0, &str) && str && QString::fromUtf8((char *)str) == m_name |
|
: false; |
|
} |
|
|
|
void CFcEngine::getSizes() |
|
{ |
|
if (!m_sizes.isEmpty()) { |
|
return; |
|
} |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug(); |
|
#endif |
|
XftFont *f = queryFont(); |
|
int alphaSize(m_sizes.size() > m_alphaSizeIndex && m_alphaSizeIndex >= 0 ? m_sizes[m_alphaSizeIndex] : constDefaultAlphaSize); |
|
|
|
m_scalable = FcTrue; |
|
|
|
m_alphaSizeIndex = 0; |
|
|
|
if (f) { |
|
double px(0.0); |
|
|
|
if (m_installed) { |
|
if (FcResultMatch != FcPatternGetBool(f->pattern, FC_SCALABLE, 0, &m_scalable)) { |
|
m_scalable = FcFalse; |
|
} |
|
|
|
if (!m_scalable) { |
|
FcPattern *pat = nullptr; |
|
FcObjectSet *os = FcObjectSetBuild(FC_PIXEL_SIZE, (void *)nullptr); |
|
int weight, width, slant; |
|
|
|
FC::decomposeStyleVal(m_style, weight, width, slant); |
|
|
|
#ifndef KFI_FC_NO_WIDTHS |
|
if (KFI_NULL_SETTING != width) { |
|
pat = FcPatternBuild(nullptr, |
|
FC_FAMILY, |
|
FcTypeString, |
|
(const FcChar8 *)(m_name.toUtf8().data()), |
|
FC_WEIGHT, |
|
FcTypeInteger, |
|
weight, |
|
FC_SLANT, |
|
FcTypeInteger, |
|
slant, |
|
FC_WIDTH, |
|
FcTypeInteger, |
|
width, |
|
NULL); |
|
} else { |
|
#endif |
|
pat = FcPatternBuild(nullptr, |
|
FC_FAMILY, |
|
FcTypeString, |
|
(const FcChar8 *)(m_name.toUtf8().data()), |
|
FC_WEIGHT, |
|
FcTypeInteger, |
|
weight, |
|
FC_SLANT, |
|
FcTypeInteger, |
|
slant, |
|
NULL); |
|
} |
|
|
|
FcFontSet *set = FcFontList(nullptr, pat, os); |
|
|
|
FcPatternDestroy(pat); |
|
FcObjectSetDestroy(os); |
|
|
|
if (set) { |
|
int size(0); |
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "got fixed sizes: " << set->nfont; |
|
#endif |
|
m_sizes.reserve(set->nfont); |
|
for (int i = 0; i < set->nfont; i++) { |
|
if (FcResultMatch == FcPatternGetDouble(set->fonts[i], FC_PIXEL_SIZE, 0, &px)) { |
|
m_sizes.push_back((int)px); |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "got fixed: " << px; |
|
#endif |
|
if (px <= alphaSize) { |
|
m_alphaSizeIndex = size; |
|
} |
|
size++; |
|
} |
|
} |
|
FcFontSetDestroy(set); |
|
} |
|
} |
|
} else { |
|
FT_Face face = XftLockFace(f); |
|
|
|
if (face) { |
|
m_indexCount = face->num_faces; |
|
if (!(m_scalable = FT_IS_SCALABLE(face))) { |
|
int numSizes = face->num_fixed_sizes, size; |
|
|
|
m_sizes.reserve(numSizes); |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "numSizes fixed: " << numSizes; |
|
#endif |
|
for (size = 0; size < numSizes; size++) { |
|
#if (FREETYPE_MAJOR * 10000 + FREETYPE_MINOR * 100 + FREETYPE_PATCH) >= 20105 |
|
double px = face->available_sizes[size].y_ppem >> 6; |
|
#else |
|
double px = face->available_sizes[size].width; |
|
#endif |
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "px: " << px; |
|
#endif |
|
m_sizes.push_back((int)px); |
|
|
|
if (px <= alphaSize) { |
|
m_alphaSizeIndex = size; |
|
} |
|
} |
|
} |
|
XftUnlockFace(f); |
|
} |
|
} |
|
|
|
closeFont(f); |
|
} |
|
|
|
if (m_scalable) { |
|
m_sizes.reserve(sizeof(constScalableSizes) / sizeof(int)); |
|
|
|
for (int i = 0; constScalableSizes[i]; ++i) { |
|
int px = point2Pixel(constScalableSizes[i]); |
|
|
|
if (px <= alphaSize) { |
|
m_alphaSizeIndex = i; |
|
} |
|
m_sizes.push_back(px); |
|
} |
|
} |
|
|
|
#ifdef KFI_FC_DEBUG |
|
qDebug() << "end"; |
|
#endif |
|
} |
|
|
|
void CFcEngine::drawName(int x, int &y, int h) |
|
{ |
|
QString title(m_descriptiveName.isEmpty() ? i18n("ERROR: Could not determine font's name.") : m_descriptiveName); |
|
|
|
if (1 == m_sizes.size()) { |
|
title = i18np("%2 [1 pixel]", "%2 [%1 pixels]", m_sizes[0], title); |
|
} |
|
|
|
xft()->drawString(title, x, y, h); |
|
} |
|
|
|
void CFcEngine::addFontFile(const QString &file) |
|
{ |
|
if (!m_addedFiles.contains(file)) { |
|
FcInitReinitialize(); |
|
FcConfigAppFontAddFile(FcConfigGetCurrent(), (const FcChar8 *)(QFile::encodeName(file).data())); |
|
m_addedFiles.append(file); |
|
} |
|
} |
|
|
|
void CFcEngine::reinit() |
|
{ |
|
if (theirFcDirty) { |
|
FcInitReinitialize(); |
|
theirFcDirty = false; |
|
} |
|
} |
|
|
|
CFcEngine::Xft *CFcEngine::xft() |
|
{ |
|
if (!m_xft) { |
|
m_xft = new Xft; |
|
} |
|
return m_xft; |
|
} |
|
|
|
}
|
|
|