1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the test suite of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL-EXCEPT$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 as published by the Free Software
20** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21** included in the packaging of this file. Please review the following
22** information to ensure the GNU General Public License requirements will
23** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24**
25** $QT_END_LICENSE$
26**
27****************************************************************************/
28
29
30#include <QtTest/QtTest>
31#include <qfont.h>
32#include <qfontmetrics.h>
33#include <qfontdatabase.h>
34#include <private/qfontengine_p.h>
35#include <qstringlist.h>
36#include <qlist.h>
37
38class tst_QFontMetrics : public QObject
39{
40Q_OBJECT
41
42private slots:
43 void same();
44 void metrics();
45 void boundingRect();
46 void elidedText_data();
47 void elidedText();
48 void veryNarrowElidedText();
49 void averageCharWidth();
50
51#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
52 void bypassShaping();
53#endif
54
55 void elidedMultiLength();
56 void elidedMultiLengthF();
57 void inFontUcs4();
58 void lineWidth();
59 void mnemonicTextWidth();
60 void leadingBelowLine();
61 void elidedMetrics();
62 void zeroWidthMetrics();
63};
64
65void tst_QFontMetrics::same()
66{
67 QFont font;
68 font.setBold(true);
69 QFontMetrics fm(font);
70 const QString text = QLatin1String("Some stupid STRING");
71 QCOMPARE(fm.size(0, text), fm.size(0, text)) ;
72
73 for (int i = 10; i <= 32; ++i) {
74 font.setPixelSize(i);
75 QFontMetrics fm1(font);
76 QCOMPARE(fm1.size(0, text), fm1.size(0, text));
77 }
78
79 {
80 QImage image;
81 QFontMetrics fm2(font, &image);
82 QString text2 = QLatin1String("Foo Foo");
83 QCOMPARE(fm2.size(0, text2), fm2.size(0, text2)); //used to crash
84 }
85
86 {
87 QImage image;
88 QFontMetricsF fm3(font, &image);
89 QString text2 = QLatin1String("Foo Foo");
90 QCOMPARE(fm3.size(0, text2), fm3.size(0, text2)); //used to crash
91 }
92}
93
94
95void tst_QFontMetrics::metrics()
96{
97 QFont font;
98 QFontDatabase fdb;
99
100 // Query the QFontDatabase for a specific font, store the
101 // result in family, style and size.
102 QStringList families = fdb.families();
103 if (families.isEmpty())
104 return;
105
106 QStringList::ConstIterator f_it, f_end = families.end();
107 for (f_it = families.begin(); f_it != f_end; ++f_it) {
108 const QString &family = *f_it;
109
110 QStringList styles = fdb.styles(family);
111 QStringList::ConstIterator s_it, s_end = styles.end();
112 for (s_it = styles.begin(); s_it != s_end; ++s_it) {
113 const QString &style = *s_it;
114
115 if (fdb.isSmoothlyScalable(family, style)) {
116 // smoothly scalable font... don't need to load every pointsize
117 font = fdb.font(family, style, pointSize: 12);
118
119 QFontMetrics fontmetrics(font);
120 QCOMPARE(fontmetrics.ascent() + fontmetrics.descent(),
121 fontmetrics.height());
122
123 QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
124 fontmetrics.lineSpacing());
125 } else {
126 QList<int> sizes = fdb.pointSizes(family, style);
127 QVERIFY(!sizes.isEmpty());
128 QList<int>::ConstIterator z_it, z_end = sizes.end();
129 for (z_it = sizes.begin(); z_it != z_end; ++z_it) {
130 const int size = *z_it;
131
132 // Initialize the font, and check if it is an exact match
133 font = fdb.font(family, style, pointSize: size);
134
135 QFontMetrics fontmetrics(font);
136 QCOMPARE(fontmetrics.ascent() + fontmetrics.descent(),
137 fontmetrics.height());
138 QCOMPARE(fontmetrics.height() + fontmetrics.leading(),
139 fontmetrics.lineSpacing());
140 }
141 }
142 }
143 }
144}
145
146void tst_QFontMetrics::boundingRect()
147{
148 QFont f;
149 f.setPointSize(24);
150 QFontMetrics fm(f);
151 QRect r = fm.boundingRect(QChar('Y'));
152 QVERIFY(r.top() < 0);
153 r = fm.boundingRect(text: QString("Y"));
154 QVERIFY(r.top() < 0);
155}
156
157void tst_QFontMetrics::elidedText_data()
158{
159 QTest::addColumn<QFont>(name: "font");
160 QTest::addColumn<QString>(name: "text");
161
162 QTest::newRow(dataTag: "helvetica hello") << QFont("helvetica",10) << QString("hello") ;
163 QTest::newRow(dataTag: "helvetica hello &Bye") << QFont("helvetica",10) << QString("hello&Bye") ;
164}
165
166
167void tst_QFontMetrics::elidedText()
168{
169 QFETCH(QFont, font);
170 QFETCH(QString, text);
171 QFontMetrics fm(font);
172 int w = fm.horizontalAdvance(text);
173 QString newtext = fm.elidedText(text,mode: Qt::ElideRight,width: w+1, flags: 0);
174 QCOMPARE(text,newtext); // should not elide
175 newtext = fm.elidedText(text,mode: Qt::ElideRight,width: w-1, flags: 0);
176 QVERIFY(text != newtext); // should elide
177}
178
179void tst_QFontMetrics::veryNarrowElidedText()
180{
181 QFont f;
182 QFontMetrics fm(f);
183 QString text("hello");
184 QCOMPARE(fm.elidedText(text, Qt::ElideRight, 0), QString());
185}
186
187void tst_QFontMetrics::averageCharWidth()
188{
189 QFont f;
190 QFontMetrics fm(f);
191 QVERIFY(fm.averageCharWidth() != 0);
192 QFontMetricsF fmf(f);
193 QVERIFY(fmf.averageCharWidth() != 0);
194}
195
196#if QT_DEPRECATED_SINCE(5, 11) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
197void tst_QFontMetrics::bypassShaping()
198{
199 QFont f;
200 f.setStyleStrategy(QFont::ForceIntegerMetrics);
201 QFontMetrics fm(f);
202 QString text = " A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z";
203 int textWidth = fm.width(text, len: -1, flags: Qt::TextBypassShaping);
204 QVERIFY(textWidth != 0);
205 int charsWidth = 0;
206 for (int i = 0; i < text.size(); ++i)
207 charsWidth += fm.horizontalAdvance(text[i]);
208 // This assertion is needed in Qt WebKit's WebCore::Font::offsetForPositionForSimpleText
209 QCOMPARE(textWidth, charsWidth);
210}
211#endif
212
213template<class FontMetrics, typename PrimitiveType> void elidedMultiLength_helper()
214{
215 QString text1 = QLatin1String("Long Text 1\x9cShorter\x9csmall");
216 QString text1_long = "Long Text 1";
217 QString text1_short = "Shorter";
218 QString text1_small = "small";
219 FontMetrics fm = FontMetrics(QFont());
220 PrimitiveType width_long = fm.size(0, text1_long).width();
221 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, 8000), text1_long);
222 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long + 1), text1_long);
223 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_long - 1), text1_short);
224 PrimitiveType width_short = fm.size(0, text1_short).width();
225 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short + 1), text1_short);
226 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_short - 1), text1_small);
227
228 // Not even wide enough for "small" - should use ellipsis
229 QChar ellipsisChar(0x2026);
230 QString text1_el = QString::fromLatin1(str: "s") + ellipsisChar;
231 PrimitiveType width_small = fm.horizontalAdvance(text1_el);
232 QCOMPARE(fm.elidedText(text1,Qt::ElideRight, width_small + 1), text1_el);
233}
234
235void tst_QFontMetrics::elidedMultiLength()
236{
237 elidedMultiLength_helper<QFontMetrics, int>();
238}
239
240void tst_QFontMetrics::elidedMultiLengthF()
241{
242 elidedMultiLength_helper<QFontMetricsF, qreal>();
243}
244
245void tst_QFontMetrics::inFontUcs4()
246{
247 int id = QFontDatabase::addApplicationFont(fileName: ":/fonts/ucs4font.ttf");
248 QVERIFY(id >= 0);
249
250 QFont font("QtTestUcs4");
251 {
252 QFontMetrics fm(font);
253 QVERIFY(fm.inFontUcs4(0x1D7FF));
254 }
255
256 {
257 QFontMetricsF fm(font);
258 QVERIFY(fm.inFontUcs4(0x1D7FF));
259 }
260
261 {
262 QFontEngine *engine = QFontPrivate::get(font)->engineForScript(script: QChar::Script_Common);
263 QGlyphLayout glyphs;
264 glyphs.numGlyphs = 3;
265 uint buf[3];
266 glyphs.glyphs = buf;
267
268 QString string;
269 {
270 string.append(c: QChar::highSurrogate(ucs4: 0x1D7FF));
271 string.append(c: QChar::lowSurrogate(ucs4: 0x1D7FF));
272
273 glyphs.numGlyphs = 3;
274 glyphs.glyphs[0] = 0;
275 QVERIFY(engine->stringToCMap(string.constData(), string.size(),
276 &glyphs, &glyphs.numGlyphs,
277 QFontEngine::GlyphIndicesOnly));
278 QCOMPARE(glyphs.numGlyphs, 1);
279 QCOMPARE(glyphs.glyphs[0], uint(1));
280 }
281 {
282 string.clear();
283 string.append(c: QChar::ObjectReplacementCharacter);
284
285 glyphs.numGlyphs = 3;
286 glyphs.glyphs[0] = 0;
287 QVERIFY(engine->stringToCMap(string.constData(), string.size(),
288 &glyphs, &glyphs.numGlyphs,
289 QFontEngine::GlyphIndicesOnly));
290 QVERIFY(glyphs.glyphs[0] != 1);
291 }
292 }
293
294 QFontDatabase::removeApplicationFont(id);
295}
296
297void tst_QFontMetrics::lineWidth()
298{
299 // QTBUG-13009, QTBUG-13011
300 QFont smallFont;
301 smallFont.setPointSize(8);
302 smallFont.setWeight(QFont::Light);
303 const QFontMetrics smallFontMetrics(smallFont);
304
305 QFont bigFont;
306 bigFont.setPointSize(40);
307 bigFont.setWeight(QFont::Black);
308 const QFontMetrics bigFontMetrics(bigFont);
309
310 QVERIFY(smallFontMetrics.lineWidth() >= 1);
311 QVERIFY(smallFontMetrics.lineWidth() < bigFontMetrics.lineWidth());
312}
313
314void tst_QFontMetrics::mnemonicTextWidth()
315{
316 // QTBUG-41593
317 QFont f;
318 QFontMetrics fm(f);
319 const QString f1 = "File";
320 const QString f2 = "&File";
321
322 QCOMPARE(fm.size(Qt::TextShowMnemonic, f1), fm.size(Qt::TextShowMnemonic, f2));
323 QCOMPARE(fm.size(Qt::TextHideMnemonic, f1), fm.size(Qt::TextHideMnemonic, f2));
324}
325
326void tst_QFontMetrics::leadingBelowLine()
327{
328 QScriptLine line;
329 line.leading = 10;
330 line.leadingIncluded = true;
331 line.ascent = 5;
332 QCOMPARE(line.height(), line.ascent + line.descent + line.leading);
333 QCOMPARE(line.base(), line.ascent);
334}
335
336void tst_QFontMetrics::elidedMetrics()
337{
338 QString testFont = QFINDTESTDATA("fonts/testfont.ttf");
339 QVERIFY(!testFont.isEmpty());
340
341 int id = QFontDatabase::addApplicationFont(fileName: testFont);
342 QVERIFY(id >= 0);
343
344 QFont font(QFontDatabase::applicationFontFamilies(id).at(i: 0));
345 font.setPixelSize(12.0);
346
347 QFontMetricsF metrics(font);
348 QString s = QStringLiteral("VeryLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongLongText");
349
350 QRectF boundingRect = metrics.boundingRect(string: s);
351
352 QString elided = metrics.elidedText(text: s, mode: Qt::ElideRight, width: boundingRect.width() / 2.0);
353
354 QRectF elidedBoundingRect = metrics.boundingRect(string: elided);
355
356 QCOMPARE(boundingRect.height(), elidedBoundingRect.height());
357 QCOMPARE(boundingRect.y(), elidedBoundingRect.y());
358
359 QFontDatabase::removeApplicationFont(id);
360}
361
362void tst_QFontMetrics::zeroWidthMetrics()
363{
364 QString zwnj(QChar(0x200c));
365 QString zwsp(QChar(0x200b));
366
367 QFont font;
368 QFontMetricsF fm(font);
369 QCOMPARE(fm.horizontalAdvance(zwnj), 0);
370 QCOMPARE(fm.horizontalAdvance(zwsp), 0);
371 QCOMPARE(fm.boundingRect(zwnj).width(), 0);
372 QCOMPARE(fm.boundingRect(zwsp).width(), 0);
373
374 QString string1 = QStringLiteral("(") + zwnj + QStringLiteral(")");
375 QString string2 = QStringLiteral("(") + zwnj + zwnj + QStringLiteral(")");
376 QString string3 = QStringLiteral("(") + zwsp + QStringLiteral(")");
377 QString string4 = QStringLiteral("(") + zwsp + zwsp + QStringLiteral(")");
378
379 QCOMPARE(fm.horizontalAdvance(string1), fm.horizontalAdvance(string2));
380 QCOMPARE(fm.horizontalAdvance(string3), fm.horizontalAdvance(string4));
381 QCOMPARE(fm.boundingRect(string1).width(), fm.boundingRect(string2).width());
382 QCOMPARE(fm.boundingRect(string3).width(), fm.boundingRect(string4).width());
383}
384
385QTEST_MAIN(tst_QFontMetrics)
386#include "tst_qfontmetrics.moc"
387

source code of qtbase/tests/auto/gui/text/qfontmetrics/tst_qfontmetrics.cpp