TerminalFonts.cpp 7.7 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/*
    SPDX-FileCopyrightText: 2020-2020 Gustavo Carneiro <gcarneiroa@hotmail.com>
    SPDX-FileCopyrightText: 2007-2008 Robert Knight <robertknight@gmail.com>
    SPDX-FileCopyrightText: 1997, 1998 Lars Doelle <lars.doelle@on-line.de>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

// Own
#include "TerminalFonts.h"

// Konsole
#include "konsoledebug.h"
#include "terminalDisplay/TerminalDisplay.h"
#include "session/SessionManager.h"
#include "session/SessionController.h"
#include "session/Session.h"

// Qt
#include <QFont>
#include <QFontMetrics>

namespace Konsole
{
    TerminalFont::TerminalFont(QWidget *parent)
        : QWidget(parent)
        , m_lineSpacing(0)
        , m_fontHeight(1)
        , m_fontWidth(1)
        , m_fontAscent(1)
        , m_boldIntense(false)
        , m_antialiasText(true)
        , m_useFontLineCharacters(false)
        , m_profile(nullptr)
    {
    }
37
38

    void TerminalFont::applyProfile(const Profile::Ptr &profile)
39
40
41
42
43
44
45
46
    {
        m_profile = profile;
        m_antialiasText = profile->antiAliasFonts();
        m_boldIntense = profile->boldIntense();
        m_useFontLineCharacters = profile->useFontLineCharacters();
        m_lineSpacing = uint(profile->lineSpacing());
        setVTFont(profile->font());
    }
47

48
49
50
51
52
53
54
55
56
57
    void TerminalFont::setVTFont(const QFont &f)
    {
        QFont newFont(f);
        int strategy = 0;

        // hint that text should be drawn with- or without anti-aliasing.
        // depending on the user's font configuration, this may not be respected
        strategy |= m_antialiasText ? QFont::PreferAntialias : QFont::NoAntialias;

        // Konsole cannot handle non-integer font metrics
58
59
        // TODO: Qt6 will remove ForceIntegerMetrics
        // "Use QFontMetrics to retrieve rounded font metrics."
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
        strategy |= QFont::ForceIntegerMetrics;

        // In case the provided font doesn't have some specific characters it should
        // fall back to a Monospace fonts.
        newFont.setStyleHint(QFont::TypeWriter, QFont::StyleStrategy(strategy));

        // Try to check that a good font has been loaded.
        // For some fonts, ForceIntegerMetrics causes height() == 0 which
        // will cause Konsole to crash later.
        QFontMetrics fontMetrics2(newFont);
        if (fontMetrics2.height() < 1) {
            qCDebug(KonsoleDebug)<<"The font "<<newFont.toString()<<" has an invalid height()";
            // Ask for a generic font so at least it is usable.
            // Font listed in profile's dialog will not be updated.
            newFont = QFont(QStringLiteral("Monospace"));
            // Set style strategy without ForceIntegerMetrics for the font
76
77
            // TODO: Qt6 will remove ForceIntegerMetrics
            // "Use QFontMetrics to retrieve rounded font metrics."
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
            strategy &= ~QFont::ForceIntegerMetrics;
            newFont.setStyleHint(QFont::TypeWriter, QFont::StyleStrategy(strategy));
            qCDebug(KonsoleDebug)<<"Font changed to "<<newFont.toString();
        }

        // experimental optimization.  Konsole assumes that the terminal is using a
        // mono-spaced font, in which case kerning information should have an effect.
        // Disabling kerning saves some computation when rendering text.
        newFont.setKerning(false);

        // "Draw intense colors in bold font" feature needs to use different font weights. StyleName
        // property, when set, doesn't allow weight changes. Since all properties (weight, stretch,
        // italic, etc) are stored in QFont independently, in almost all cases styleName is not needed.
        newFont.setStyleName(QString());

        if (newFont == qobject_cast<QWidget*>(parent())->font()) {
            // Do not process the same font again
            return;
        }

        QFontInfo fontInfo(newFont);

        // QFontInfo::fixedPitch() appears to not match QFont::fixedPitch() - do not test it.
        // related?  https://bugreports.qt.io/browse/QTBUG-34082
        if (fontInfo.family() != newFont.family()
                || !qFuzzyCompare(fontInfo.pointSizeF(), newFont.pointSizeF())
                || fontInfo.styleHint()  != newFont.styleHint()
                || fontInfo.weight()     != newFont.weight()
                || fontInfo.style()      != newFont.style()
                || fontInfo.underline()  != newFont.underline()
                || fontInfo.strikeOut()  != newFont.strikeOut()
                || fontInfo.rawMode()    != newFont.rawMode()) {
            const QString nonMatching = QString::asprintf("%s,%g,%d,%d,%d,%d,%d,%d,%d,%d",
                    qPrintable(fontInfo.family()),
                    fontInfo.pointSizeF(),
                    -1, // pixelSize is not used
                    static_cast<int>(fontInfo.styleHint()),
                    fontInfo.weight(),
                    static_cast<int>(fontInfo.style()),
                    static_cast<int>(fontInfo.underline()),
                    static_cast<int>(fontInfo.strikeOut()),
                    // Intentional newFont use - fixedPitch is bugged, see comment above
                    static_cast<int>(newFont.fixedPitch()),
                    static_cast<int>(fontInfo.rawMode()));
            qCDebug(KonsoleDebug) << "The font to use in the terminal can not be matched exactly on your system.";
            qCDebug(KonsoleDebug) << " Selected: " << newFont.toString();
            qCDebug(KonsoleDebug) << " System  : " << nonMatching;
        }

        qobject_cast<QWidget*>(parent())->setFont(newFont);
        fontChange(newFont);
    }
130

131
132
133
134
    QFont TerminalFont::getVTFont() const
    {
        return qobject_cast<QWidget*>(parent())->font();
    }
135
136

    void TerminalFont::increaseFontSize()
137
138
139
140
141
    {
        QFont font = qobject_cast<QWidget*>(parent())->font();
        font.setPointSizeF(font.pointSizeF() + 1);
        setVTFont(font);
    }
142
143

    void TerminalFont::decreaseFontSize()
144
145
146
147
148
149
150
    {
        const qreal MinimumFontSize = 6;

        QFont font = qobject_cast<QWidget*>(parent())->font();
        font.setPointSizeF(qMax(font.pointSizeF() - 1, MinimumFontSize));
        setVTFont(font);
    }
151
152

    void TerminalFont::resetFontSize()
153
154
    {
        const qreal MinimumFontSize = 6;
155

156
157
158
159
160
161
162
        TerminalDisplay *display = qobject_cast<TerminalDisplay*>(parent());
        QFont font = display->font();
        Profile::Ptr currentProfile = SessionManager::instance()->sessionProfile(display->sessionController()->session());
        const qreal defaultFontSize = currentProfile->font().pointSizeF();
        font.setPointSizeF(qMax(defaultFontSize, MinimumFontSize));
        setVTFont(font);
    }
163
164

    void TerminalFont::setLineSpacing(uint i)
165
166
167
168
    {
        m_lineSpacing = i;
        fontChange(qobject_cast<QWidget*>(parent())->font());
    }
169

170
171
172
173
    uint TerminalFont::lineSpacing() const
    {
        return m_lineSpacing;
    }
174

175
176
177
178
179
180
181
182
183
184
185
186
187
188
    int TerminalFont::fontHeight() const
    {
        return m_fontHeight;
    }

    int TerminalFont::fontWidth() const
    {
        return m_fontWidth;
    }

    int TerminalFont::fontAscent() const
    {
        return m_fontAscent;
    }
189

190
191
192
193
194
195
196
197
198
    bool TerminalFont::boldIntense() const
    {
        return m_boldIntense;
    }

    bool TerminalFont::antialiasText() const
    {
        return m_antialiasText;
    }
199

200
201
202
203
    bool TerminalFont::useFontLineCharacters() const
    {
        return m_useFontLineCharacters;
    }
204
205

    void TerminalFont::fontChange(const QFont &)
206
207
208
209
210
211
    {
        QFontMetrics fm(qobject_cast<QWidget*>(parent())->font());
        m_fontHeight = fm.height() + m_lineSpacing;

        Q_ASSERT(m_fontHeight > 0);

212
        m_fontWidth = fm.horizontalAdvance(QLatin1Char('M'));
213
214
215
216
217
218

        if (m_fontWidth < 1) {
            m_fontWidth = 1;
        }

        m_fontAscent = fm.ascent();
219

220
221
222
223
224
        qobject_cast<TerminalDisplay*>(parent())->propagateSize();
        update();
    }

}