1 /*
2 SPDX-FileCopyrightText: 2020-2020 Gustavo Carneiro <gcarneiroa@hotmail.com>
3 SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
4 SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>
5
6 SPDX-License-Identifier: GPL-2.0-or-later
7 */
8
9 // Own
10 #include "TerminalFonts.h"
11
12 // Konsole
13 #include "konsoledebug.h"
14 #include "session/Session.h"
15 #include "session/SessionController.h"
16 #include "session/SessionManager.h"
17 #include "terminalDisplay/TerminalDisplay.h"
18
19 // Qt
20 #include <QFont>
21 #include <QFontMetrics>
22
23 namespace Konsole
24 {
TerminalFont(QWidget * parent)25 TerminalFont::TerminalFont(QWidget *parent)
26 : m_parent(parent)
27 , m_lineSpacing(0)
28 , m_fontHeight(1)
29 , m_fontWidth(1)
30 , m_fontAscent(1)
31 , m_boldIntense(false)
32 , m_antialiasText(true)
33 , m_useFontLineCharacters(false)
34 , m_profile(nullptr)
35 {
36 }
37
applyProfile(const Profile::Ptr & profile)38 void TerminalFont::applyProfile(const Profile::Ptr &profile)
39 {
40 m_profile = profile;
41 m_antialiasText = profile->antiAliasFonts();
42 m_boldIntense = profile->boldIntense();
43 m_useFontLineCharacters = profile->useFontLineCharacters();
44 m_lineSpacing = uint(profile->lineSpacing());
45 setVTFont(profile->font());
46 }
47
setVTFont(const QFont & f)48 void TerminalFont::setVTFont(const QFont &f)
49 {
50 QFont newFont(f);
51 int strategy = 0;
52
53 // hint that text should be drawn with- or without anti-aliasing.
54 // depending on the user's font configuration, this may not be respected
55 strategy |= m_antialiasText ? QFont::PreferAntialias : QFont::NoAntialias;
56
57 // Konsole cannot handle non-integer font metrics
58 // TODO: Qt6 will remove ForceIntegerMetrics
59 // "Use QFontMetrics to retrieve rounded font metrics."
60 strategy |= QFont::ForceIntegerMetrics;
61
62 // In case the provided font doesn't have some specific characters it should
63 // fall back to a Monospace fonts.
64 newFont.setStyleHint(QFont::TypeWriter, QFont::StyleStrategy(strategy));
65
66 // Try to check that a good font has been loaded.
67 // For some fonts, ForceIntegerMetrics causes height() == 0 which
68 // will cause Konsole to crash later.
69 QFontMetrics fontMetrics2(newFont);
70 if (fontMetrics2.height() < 1) {
71 qCDebug(KonsoleDebug) << "The font " << newFont.toString() << " has an invalid height()";
72 // Ask for a generic font so at least it is usable.
73 // Font listed in profile's dialog will not be updated.
74 newFont = QFont(QStringLiteral("Monospace"));
75 // Set style strategy without ForceIntegerMetrics for the font
76 // TODO: Qt6 will remove ForceIntegerMetrics
77 // "Use QFontMetrics to retrieve rounded font metrics."
78 strategy &= ~QFont::ForceIntegerMetrics;
79 newFont.setStyleHint(QFont::TypeWriter, QFont::StyleStrategy(strategy));
80 qCDebug(KonsoleDebug) << "Font changed to " << newFont.toString();
81 }
82
83 // experimental optimization. Konsole assumes that the terminal is using a
84 // mono-spaced font, in which case kerning information should have an effect.
85 // Disabling kerning saves some computation when rendering text.
86 newFont.setKerning(false);
87
88 // "Draw intense colors in bold font" feature needs to use different font weights. StyleName
89 // property, when set, doesn't allow weight changes. Since all properties (weight, stretch,
90 // italic, etc) are stored in QFont independently, in almost all cases styleName is not needed.
91 newFont.setStyleName(QString());
92
93 if (newFont == qobject_cast<QWidget *>(m_parent)->font()) {
94 // Do not process the same font again
95 return;
96 }
97
98 QFontInfo fontInfo(newFont);
99
100 // QFontInfo::fixedPitch() appears to not match QFont::fixedPitch() - do not test it.
101 // related? https://bugreports.qt.io/browse/QTBUG-34082
102 if (fontInfo.family() != newFont.family() || !qFuzzyCompare(fontInfo.pointSizeF(), newFont.pointSizeF()) || fontInfo.styleHint() != newFont.styleHint()
103 || fontInfo.weight() != newFont.weight() || fontInfo.style() != newFont.style() || fontInfo.underline() != newFont.underline()
104 || fontInfo.strikeOut() != newFont.strikeOut() || fontInfo.rawMode() != newFont.rawMode()) {
105 const QString nonMatching = QString::asprintf("%s,%g,%d,%d,%d,%d,%d,%d,%d,%d",
106 qPrintable(fontInfo.family()),
107 fontInfo.pointSizeF(),
108 -1, // pixelSize is not used
109 static_cast<int>(fontInfo.styleHint()),
110 fontInfo.weight(),
111 static_cast<int>(fontInfo.style()),
112 static_cast<int>(fontInfo.underline()),
113 static_cast<int>(fontInfo.strikeOut()),
114 // Intentional newFont use - fixedPitch is bugged, see comment above
115 static_cast<int>(newFont.fixedPitch()),
116 static_cast<int>(fontInfo.rawMode()));
117 qCDebug(KonsoleDebug) << "The font to use in the terminal can not be matched exactly on your system.";
118 qCDebug(KonsoleDebug) << " Selected: " << newFont.toString();
119 qCDebug(KonsoleDebug) << " System : " << nonMatching;
120 }
121
122 qobject_cast<QWidget *>(m_parent)->setFont(newFont);
123 fontChange(newFont);
124 }
125
getVTFont() const126 QFont TerminalFont::getVTFont() const
127 {
128 return qobject_cast<QWidget *>(m_parent)->font();
129 }
130
increaseFontSize()131 void TerminalFont::increaseFontSize()
132 {
133 QFont font = qobject_cast<QWidget *>(m_parent)->font();
134 font.setPointSizeF(font.pointSizeF() + 1);
135 setVTFont(font);
136 }
137
decreaseFontSize()138 void TerminalFont::decreaseFontSize()
139 {
140 const qreal MinimumFontSize = 6;
141
142 QFont font = qobject_cast<QWidget *>(m_parent)->font();
143 font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize));
144 setVTFont(font);
145 }
146
resetFontSize()147 void TerminalFont::resetFontSize()
148 {
149 const qreal MinimumFontSize = 6;
150
151 TerminalDisplay *display = qobject_cast<TerminalDisplay *>(m_parent);
152 QFont font = display->font();
153 Profile::Ptr currentProfile = SessionManager::instance()->sessionProfile(display->sessionController()->session());
154 const qreal defaultFontSize = currentProfile->font().pointSizeF();
155 font.setPointSizeF(qMax(defaultFontSize, MinimumFontSize));
156 setVTFont(font);
157 }
158
setLineSpacing(uint i)159 void TerminalFont::setLineSpacing(uint i)
160 {
161 m_lineSpacing = i;
162 fontChange(qobject_cast<QWidget *>(m_parent)->font());
163 }
164
lineSpacing() const165 uint TerminalFont::lineSpacing() const
166 {
167 return m_lineSpacing;
168 }
169
fontHeight() const170 int TerminalFont::fontHeight() const
171 {
172 return m_fontHeight;
173 }
174
fontWidth() const175 int TerminalFont::fontWidth() const
176 {
177 return m_fontWidth;
178 }
179
fontAscent() const180 int TerminalFont::fontAscent() const
181 {
182 return m_fontAscent;
183 }
184
boldIntense() const185 bool TerminalFont::boldIntense() const
186 {
187 return m_boldIntense;
188 }
189
antialiasText() const190 bool TerminalFont::antialiasText() const
191 {
192 return m_antialiasText;
193 }
194
useFontLineCharacters() const195 bool TerminalFont::useFontLineCharacters() const
196 {
197 return m_useFontLineCharacters;
198 }
199
fontChange(const QFont &)200 void TerminalFont::fontChange(const QFont &)
201 {
202 QFontMetrics fm(qobject_cast<QWidget *>(m_parent)->font());
203 m_fontHeight = fm.height() + m_lineSpacing;
204
205 Q_ASSERT(m_fontHeight > 0);
206
207 m_fontWidth = fm.horizontalAdvance(QLatin1Char('M'));
208
209 if (m_fontWidth < 1) {
210 m_fontWidth = 1;
211 }
212
213 m_fontAscent = fm.ascent();
214
215 qobject_cast<TerminalDisplay *>(m_parent)->propagateSize();
216 }
217
218 }
219