Read font selection flags and use them when querying for metrics

Certain fonts with multiple styles have the same family name. When
loading these as application fonts we were not specific enough when
querying for the text metrics. This meant that e.g. the bold version in
a font family would get the metrics of the regular one.

Fixes: QTBUG-67273
Change-Id: Ic988d62cddde0a1f77ddcaf2891cadc21c9b31e6
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
bb10
Mårten Nordheim 2018-11-09 17:56:08 +01:00
parent d23d146175
commit 17ced070fd
5 changed files with 71 additions and 6 deletions

View File

@ -1490,7 +1490,8 @@ static void getFontTable(const uchar *fileBegin, const uchar *data, quint32 tag,
static void getFamiliesAndSignatures(const QByteArray &fontData,
QList<QFontNames> *families,
QVector<FONTSIGNATURE> *signatures)
QVector<FONTSIGNATURE> *signatures,
QVector<QFontValues> *values)
{
const uchar *data = reinterpret_cast<const uchar *>(fontData.constData());
@ -1511,9 +1512,25 @@ static void getFamiliesAndSignatures(const QByteArray &fontData,
families->append(qMove(names));
if (values || signatures)
getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
if (values) {
QFontValues fontValues;
if (table && length >= 64) {
// Read in some details about the font, offset calculated based on the specification
fontValues.weight = qFromBigEndian<quint16>(table + 4);
quint16 fsSelection = qFromBigEndian<quint16>(table + 62);
fontValues.isItalic = (fsSelection & 1) != 0;
fontValues.isUnderlined = (fsSelection & (1 << 1)) != 0;
fontValues.isOverstruck = (fsSelection & (1 << 4)) != 0;
}
values->append(std::move(fontValues));
}
if (signatures) {
FONTSIGNATURE signature;
getFontTable(data, font, MAKE_TAG('O', 'S', '/', '2'), &table, &length);
if (table && length >= 86) {
// Offsets taken from OS/2 table in the TrueType spec
signature.fsUsb[0] = qFromBigEndian<quint32>(table + 42);
@ -1536,11 +1553,12 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
WinApplicationFont font;
font.fileName = fileName;
QVector<FONTSIGNATURE> signatures;
QVector<QFontValues> fontValues;
QList<QFontNames> families;
QStringList familyNames;
if (!fontData.isEmpty()) {
getFamiliesAndSignatures(fontData, &families, &signatures);
getFamiliesAndSignatures(fontData, &families, &signatures, &fontValues);
if (families.isEmpty())
return familyNames;
@ -1553,14 +1571,23 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
// Memory fonts won't show up in enumeration, so do add them the hard way.
for (int j = 0; j < families.count(); ++j) {
const QString familyName = families.at(j).name;
const QString styleName = families.at(j).style;
const auto &family = families.at(j);
const QString &familyName = family.name;
const QString &styleName = family.style;
familyNames << familyName;
HDC hdc = GetDC(0);
LOGFONT lf;
memset(&lf, 0, sizeof(LOGFONT));
memcpy(lf.lfFaceName, familyName.utf16(), sizeof(wchar_t) * qMin(LF_FACESIZE - 1, familyName.size()));
lf.lfCharSet = DEFAULT_CHARSET;
const QFontValues &values = fontValues.at(j);
lf.lfWeight = values.weight;
if (values.isItalic)
lf.lfItalic = TRUE;
if (values.isOverstruck)
lf.lfStrikeOut = TRUE;
if (values.isUnderlined)
lf.lfUnderline = TRUE;
HFONT hfont = CreateFontIndirect(&lf);
HGDIOBJ oldobj = SelectObject(hdc, hfont);
@ -1581,7 +1608,7 @@ QStringList QWindowsFontDatabase::addApplicationFont(const QByteArray &fontData,
QByteArray data = f.readAll();
f.close();
getFamiliesAndSignatures(data, &families, 0);
getFamiliesAndSignatures(data, &families, nullptr, nullptr);
if (families.isEmpty())
return QStringList();

View File

@ -177,6 +177,14 @@ struct QFontNames
QString preferredStyle; // e.g. "Condensed Italic"
};
struct QFontValues
{
quint16 weight = 0;
bool isItalic = false;
bool isOverstruck = false;
bool isUnderlined = false;
};
bool qt_localizedName(const QString &name);
QString qt_getEnglishName(const QString &familyName, bool includeStyle = false);
QFontNames qt_getCanonicalFontNames(const LOGFONT &lf);

View File

@ -3,5 +3,6 @@
<file>LED_REAL.TTF</file>
<file alias="testfont.ttf">../../../shared/resources/testfont.ttf</file>
<file alias="testfont_condensed.ttf">../../../shared/resources/testfont_condensed.ttf</file>
<file alias="testfont_italic.ttf">../../../shared/resources/testfont_italic.ttf</file>
</qresource>
</RCC>

View File

@ -61,6 +61,8 @@ private slots:
void addAppFont_data();
void addAppFont();
void addTwoAppFontsFromFamily();
void aliases();
void fallbackFonts();
@ -75,6 +77,7 @@ private:
QString m_ledFont;
QString m_testFont;
QString m_testFontCondensed;
QString m_testFontItalic;
};
tst_QFontDatabase::tst_QFontDatabase()
@ -86,9 +89,11 @@ void tst_QFontDatabase::initTestCase()
m_ledFont = QFINDTESTDATA("LED_REAL.TTF");
m_testFont = QFINDTESTDATA("testfont.ttf");
m_testFontCondensed = QFINDTESTDATA("testfont_condensed.ttf");
m_testFontItalic = QFINDTESTDATA("testfont_italic.ttf");
QVERIFY(!m_ledFont.isEmpty());
QVERIFY(!m_testFont.isEmpty());
QVERIFY(!m_testFontCondensed.isEmpty());
QVERIFY(!m_testFontItalic.isEmpty());
}
void tst_QFontDatabase::styles_data()
@ -259,6 +264,30 @@ void tst_QFontDatabase::addAppFont()
QCOMPARE(db.families(), oldFamilies);
}
void tst_QFontDatabase::addTwoAppFontsFromFamily()
{
int regularId = QFontDatabase::addApplicationFont(m_testFont);
if (regularId == -1)
QSKIP("Skip the test since app fonts are not supported on this system");
int italicId = QFontDatabase::addApplicationFont(m_testFontItalic);
QVERIFY(italicId != -1);
QVERIFY(!QFontDatabase::applicationFontFamilies(regularId).isEmpty());
QVERIFY(!QFontDatabase::applicationFontFamilies(italicId).isEmpty());
QString regularFontName = QFontDatabase::applicationFontFamilies(regularId).first();
QString italicFontName = QFontDatabase::applicationFontFamilies(italicId).first();
QCOMPARE(regularFontName, italicFontName);
QFont italicFont = QFontDatabase().font(italicFontName,
QString::fromLatin1("Italic"), 14);
QVERIFY(italicFont.italic());
QFontDatabase::removeApplicationFont(regularId);
QFontDatabase::removeApplicationFont(italicId);
}
void tst_QFontDatabase::aliases()
{
QFontDatabase db;

Binary file not shown.