diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 59250480f9..2cf83d4c6a 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -1043,7 +1043,7 @@ MainWindow::~MainWindow() { PreLaunchWindow *PreLaunchWindowInstance = 0; PreLaunchWindow::PreLaunchWindow(QString title) : TWidget(0) { - Fonts::start(); + Fonts::Start(); QIcon icon(App::pixmapFromImageInPlace(QImage(cPlatform() == dbipMac ? qsl(":/gui/art/iconbig256.png") : qsl(":/gui/art/icon256.png")))); if (cPlatform() == dbipLinux32 || cPlatform() == dbipLinux64) { @@ -1090,7 +1090,7 @@ PreLaunchWindow::~PreLaunchWindow() { PreLaunchLabel::PreLaunchLabel(QWidget *parent) : QLabel(parent) { QFont labelFont(font()); - labelFont.setFamily(qsl("Open Sans Semibold")); + labelFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold"))); labelFont.setPixelSize(static_cast(parent)->basicSize()); setFont(labelFont); @@ -1108,7 +1108,7 @@ void PreLaunchLabel::setText(const QString &text) { PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(parent) { QFont logFont(font()); - logFont.setFamily(qsl("Open Sans")); + logFont.setFamily(Fonts::GetOverride(qsl("Open Sans"))); logFont.setPixelSize(static_cast(parent)->basicSize()); setFont(logFont); @@ -1126,7 +1126,7 @@ PreLaunchInput::PreLaunchInput(QWidget *parent, bool password) : QLineEdit(paren PreLaunchLog::PreLaunchLog(QWidget *parent) : QTextEdit(parent) { QFont logFont(font()); - logFont.setFamily(qsl("Open Sans")); + logFont.setFamily(Fonts::GetOverride(qsl("Open Sans"))); logFont.setPixelSize(static_cast(parent)->basicSize()); setFont(logFont); @@ -1148,7 +1148,7 @@ PreLaunchButton::PreLaunchButton(QWidget *parent, bool confirm) : QPushButton(pa setObjectName(confirm ? "confirm" : "cancel"); QFont closeFont(font()); - closeFont.setFamily(qsl("Open Sans Semibold")); + closeFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold"))); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); @@ -1167,7 +1167,7 @@ PreLaunchCheckbox::PreLaunchCheckbox(QWidget *parent) : QCheckBox(parent) { setCheckState(Qt::Checked); QFont closeFont(font()); - closeFont.setFamily(qsl("Open Sans Semibold")); + closeFont.setFamily(Fonts::GetOverride(qsl("Open Sans Semibold"))); closeFont.setPixelSize(static_cast(parent)->basicSize()); setFont(closeFont); diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index 6ed2b4a6a7..01a90a6701 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -62,7 +62,7 @@ Messenger::Messenger() : QObject() t_assert(SingleInstance == nullptr); SingleInstance = this; - Fonts::start(); + Fonts::Start(); ThirdParty::start(); Global::start(); diff --git a/Telegram/SourceFiles/ui/style/style_core_font.cpp b/Telegram/SourceFiles/ui/style/style_core_font.cpp index d5836cffba..edcb7c7f7e 100644 --- a/Telegram/SourceFiles/ui/style/style_core_font.cpp +++ b/Telegram/SourceFiles/ui/style/style_core_font.cpp @@ -56,7 +56,12 @@ int registerFontFamily(const QString &family) { return result; } -FontData::FontData(int size, uint32 flags, int family, Font *other) : f(fontFamilies[family]), m(f), _size(size), _flags(flags), _family(family) { +FontData::FontData(int size, uint32 flags, int family, Font *other) +: f(Fonts::GetOverride(fontFamilies[family])) +, m(f) +, _size(size) +, _flags(flags) +, _family(family) { if (other) { memcpy(modified, other, sizeof(modified)); } else { diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index 089a17ffc3..dbc2b0db02 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -567,7 +567,7 @@ public: ch = emojiLookback = 0; lastSkipped = false; - checkTilde = !cRetina() && _t->_st->font->size() == 13 && _t->_st->font->flags() == 0; // tilde Open Sans fix + checkTilde = !cRetina() && (_t->_st->font->size() == 13) && (_t->_st->font->flags() == 0) && (_t->_st->font->f.family() == qstr("Open Sans")); // tilde Open Sans fix for (; ptr <= end; ++ptr) { while (checkEntities() || (rich && checkCommand())) { } diff --git a/Telegram/SourceFiles/ui/twidget.cpp b/Telegram/SourceFiles/ui/twidget.cpp index abfcd27e48..5162ac92a2 100644 --- a/Telegram/SourceFiles/ui/twidget.cpp +++ b/Telegram/SourceFiles/ui/twidget.cpp @@ -22,16 +22,96 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwindow.h" namespace Fonts { +namespace { + +bool ValidateFont(const QString &familyName, int flags = 0) { + QFont checkFont(familyName); + checkFont.setPixelSize(13); + checkFont.setBold(flags & style::internal::FontBold); + checkFont.setItalic(flags & style::internal::FontItalic); + checkFont.setUnderline(flags & style::internal::FontUnderline); + checkFont.setStyleStrategy(QFont::PreferQuality); + auto realFamily = QFontInfo(checkFont).family(); + if (realFamily.trimmed().compare(familyName, Qt::CaseInsensitive)) { + LOG(("Font Error: could not resolve '%1' font, got '%2' after feeding '%3'.").arg(familyName).arg(realFamily)); + return false; + } + + auto metrics = QFontMetrics(checkFont); + if (!metrics.height()) { + LOG(("Font Error: got a zero height in '%1'.").arg(familyName)); + return false; + } + + return true; +} + +bool LoadCustomFont(const QString &filePath, const QString &familyName, int flags = 0) { + auto regularId = QFontDatabase::addApplicationFont(filePath); + if (regularId < 0) { + LOG(("Font Error: could not add '%1'.").arg(filePath)); + return false; + } + + auto found = [&familyName, regularId] { + for (auto &family : QFontDatabase::applicationFontFamilies(regularId)) { + if (!family.trimmed().compare(familyName, Qt::CaseInsensitive)) { + return true; + } + } + return false; + }; + if (!found()) { + LOG(("Font Error: could not locate '%1' font in '%2'.").arg(familyName).arg(filePath)); + return false; + } + + return ValidateFont(familyName, flags); +} bool Started = false; -void start() { - if (!Started) { - Started = true; +QString OpenSansOverride; +QString OpenSansSemiboldOverride; - QFontDatabase::addApplicationFont(qsl(":/gui/fonts/OpenSans-Regular.ttf")); - QFontDatabase::addApplicationFont(qsl(":/gui/fonts/OpenSans-Bold.ttf")); - QFontDatabase::addApplicationFont(qsl(":/gui/fonts/OpenSans-Semibold.ttf")); +} // namespace + +void Start() { + if (Started) { + return; } + Started = true; + + auto regular = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Regular.ttf"), qsl("Open Sans")); + auto bold = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Bold.ttf"), qsl("Open Sans"), style::internal::FontBold); + auto semibold = LoadCustomFont(qsl(":/gui/fonts/OpenSans-Semibold.ttf"), qsl("Open Sans Semibold")); + +#ifdef Q_OS_WIN + // Attempt to workaround a strange font bug with Open Sans Semibold not loading. + // See https://github.com/telegramdesktop/tdesktop/issues/3276 for details. + // Crash happens on "options.maxh / _t->_st->font->height" with "division by zero". + // In that place "_t->_st->font" is "semiboldFont" is "font(13 "Open Sans Semibold"). + if (QSysInfo::WindowsVersion >= QSysInfo::WV_WINDOWS10) { + if (!regular || !bold) { + if (ValidateFont(qsl("Segoe UI")) && ValidateFont(qsl("Segoe UI"), style::internal::FontBold)) { + OpenSansOverride = qsl("Segoe UI"); + } + } + if (!semibold) { + if (ValidateFont(qsl("Segoe UI Semibold"))) { + OpenSansSemiboldOverride = qsl("Segoe UI Semibold"); + } + } + } +#endif // Q_OS_WIN +} + +QString GetOverride(const QString &familyName) { + if (familyName == qstr("Open Sans")) { + return OpenSansOverride.isEmpty() ? familyName : OpenSansOverride; + } else if (familyName == qstr("Open Sans Semibold")) { + return OpenSansSemiboldOverride.isEmpty() ? familyName : OpenSansSemiboldOverride; + } + return familyName; } } // Fonts diff --git a/Telegram/SourceFiles/ui/twidget.h b/Telegram/SourceFiles/ui/twidget.h index d3f7f5af6b..c0a88a7a4c 100644 --- a/Telegram/SourceFiles/ui/twidget.h +++ b/Telegram/SourceFiles/ui/twidget.h @@ -21,8 +21,11 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once namespace Fonts { - void start(); -} + +void Start(); +QString GetOverride(const QString &familyName); + +} // namespace class Painter : public QPainter { public: diff --git a/Telegram/SourceFiles/ui/widgets/input_fields.cpp b/Telegram/SourceFiles/ui/widgets/input_fields.cpp index d643c862b4..265651572a 100644 --- a/Telegram/SourceFiles/ui/widgets/input_fields.cpp +++ b/Telegram/SourceFiles/ui/widgets/input_fields.cpp @@ -1079,9 +1079,10 @@ struct FormattingAction { void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { // Tilde formatting. - auto regularFont = qsl("Open Sans"), semiboldFont = qsl("Open Sans Semibold"); - bool tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == regularFont); - bool isTildeFragment = false; + auto tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == qstr("Open Sans")); + auto isTildeFragment = false; + auto tildeRegularFont = tildeFormatting ? qsl("Open Sans") : QString(); + auto tildeFixedFont = tildeFormatting ? Fonts::GetOverride(qsl("Open Sans Semibold")) : QString(); // First tag handling (the one we inserted text to). bool startTagFound = false; @@ -1118,7 +1119,7 @@ void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { auto charFormat = fragment.charFormat(); if (tildeFormatting) { - isTildeFragment = (charFormat.fontFamily() == semiboldFont); + isTildeFragment = (charFormat.fontFamily() == tildeFixedFont); } auto fragmentText = fragment.text(); @@ -1202,7 +1203,7 @@ void FlatTextarea::processFormatting(int insertPosition, int insertEnd) { c.mergeCharFormat(format); } else if (action.type == ActionType::TildeFont) { QTextCharFormat format; - format.setFontFamily(action.isTilde ? semiboldFont : regularFont); + format.setFontFamily(action.isTilde ? tildeFixedFont : tildeRegularFont); c.mergeCharFormat(format); insertPosition = action.intervalEnd; } @@ -2126,9 +2127,11 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) { int32 replacePosition = -1, replaceLen = 0; EmojiPtr emoji = nullptr; - static QString regular = qsl("Open Sans"), semibold = qsl("Open Sans Semibold"); - bool checkTilde = !cRetina() && (_inner->font().pixelSize() == 13) && (_inner->font().family() == regular); - bool wasTildeFragment = false; + // Tilde formatting. + auto tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == qstr("Open Sans")); + auto isTildeFragment = false; + auto tildeRegularFont = tildeFormatting ? qsl("Open Sans") : QString(); + auto tildeFixedFont = tildeFormatting ? Fonts::GetOverride(qsl("Open Sans Semibold")) : QString(); QTextDocument *doc(_inner->document()); QTextCursor c(_inner->textCursor()); @@ -2148,8 +2151,8 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) { continue; } - if (checkTilde) { - wasTildeFragment = (fragment.charFormat().fontFamily() == semibold); + if (tildeFormatting) { + isTildeFragment = (fragment.charFormat().fontFamily() == tildeFixedFont); } QString t(fragment.text()); @@ -2167,9 +2170,9 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) { break; } - if (checkTilde && fp >= position) { // tilde fix in OpenSans + if (tildeFormatting && fp >= position) { // tilde fix in OpenSans bool tilde = (ch->unicode() == '~'); - if ((tilde && !wasTildeFragment) || (!tilde && wasTildeFragment)) { + if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) { if (replacePosition < 0) { replacePosition = fp; replaceLen = 1; @@ -2201,7 +2204,7 @@ void InputArea::processDocumentContentsChange(int position, int charsAdded) { insertEmoji(emoji, c); } else { QTextCharFormat format; - format.setFontFamily(wasTildeFragment ? regular : semibold); + format.setFontFamily(isTildeFragment ? tildeRegularFont : tildeFixedFont); c.mergeCharFormat(format); } charsAdded -= replacePosition + replaceLen - position; @@ -2857,9 +2860,11 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { EmojiPtr emoji = nullptr; bool newlineFound = false; - static QString regular = qsl("Open Sans"), semibold = qsl("Open Sans Semibold"), space(' '); - bool checkTilde = !cRetina() && (_inner->font().pixelSize() == 13) && (_inner->font().family() == regular); - bool wasTildeFragment = false; + // Tilde formatting. + auto tildeFormatting = !cRetina() && (font().pixelSize() == 13) && (font().family() == qstr("Open Sans")); + auto isTildeFragment = false; + auto tildeRegularFont = tildeFormatting ? qsl("Open Sans") : QString(); + auto tildeFixedFont = tildeFormatting ? Fonts::GetOverride(qsl("Open Sans Semibold")) : QString(); QTextDocument *doc(_inner->document()); QTextCursor c(_inner->textCursor()); @@ -2879,8 +2884,8 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { continue; } - if (checkTilde) { - wasTildeFragment = (fragment.charFormat().fontFamily() == semibold); + if (tildeFormatting) { + isTildeFragment = (fragment.charFormat().fontFamily() == tildeFixedFont); } QString t(fragment.text()); @@ -2910,9 +2915,9 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { break; } - if (checkTilde && fp >= position) { // tilde fix in OpenSans + if (tildeFormatting && fp >= position) { // tilde fix in OpenSans bool tilde = (ch->unicode() == '~'); - if ((tilde && !wasTildeFragment) || (!tilde && wasTildeFragment)) { + if ((tilde && !isTildeFragment) || (!tilde && isTildeFragment)) { if (replacePosition < 0) { replacePosition = fp; replaceLen = 1; @@ -2948,14 +2953,14 @@ void InputField::processDocumentContentsChange(int position, int charsAdded) { c.setPosition(replacePosition + replaceLen, QTextCursor::KeepAnchor); if (newlineFound) { QTextCharFormat format; - format.setFontFamily(regular); + format.setFontFamily(font().family()); c.mergeCharFormat(format); - c.insertText(space); + c.insertText(" "); } else if (emoji) { insertEmoji(emoji, c); } else { QTextCharFormat format; - format.setFontFamily(wasTildeFragment ? regular : semibold); + format.setFontFamily(isTildeFragment ? tildeRegularFont : tildeFixedFont); c.mergeCharFormat(format); } charsAdded -= replacePosition + replaceLen - position;