forked from Qortal/Brooklyn
1504 lines
48 KiB
C++
1504 lines
48 KiB
C++
/*
|
|
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;
|
|
}
|
|
|
|
}
|