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#include <QtTest/QtTest>
30#include <private/qfontengine_p.h>
31#include <private/qtextengine_p.h>
32#include <qtextlayout.h>
33#include <qfontdatabase.h>
34#include <qfontinfo.h>
35
36class tst_QTextScriptEngine : public QObject
37{
38 Q_OBJECT
39
40public:
41 tst_QTextScriptEngine();
42
43private slots:
44 void initTestCase();
45 void devanagari_data();
46 void devanagari();
47 void bengali_data();
48 void bengali();
49 void gurmukhi_data();
50 void gurmukhi();
51 // gujarati missing
52 void oriya_data();
53 void oriya();
54 void tamil_data();
55 void tamil();
56 void telugu_data();
57 void telugu();
58 void kannada_data();
59 void kannada();
60 void malayalam_data();
61 void malayalam();
62 void sinhala_data();
63 void sinhala();
64 void khmer_data();
65 void khmer();
66 void linearB_data();
67 void linearB();
68 void greek_data();
69 void greek();
70
71 void mirroredChars_data();
72 void mirroredChars();
73
74 void controlInSyllable_qtbug14204();
75 void combiningMarks_qtbug15675_data();
76 void combiningMarks_qtbug15675();
77
78 void thaiIsolatedSaraAm();
79 void thaiWithZWJ();
80 void thaiMultipleVowels();
81
82 void shapingDisabledDevanagari();
83 void shapingDisabledLatin();
84private:
85 bool haveTestFonts;
86};
87
88tst_QTextScriptEngine::tst_QTextScriptEngine()
89 : haveTestFonts(qgetenv(varName: "QT_HAVE_TEST_FONTS") == QByteArray("1"))
90{
91}
92
93void tst_QTextScriptEngine::initTestCase()
94{
95 if (!haveTestFonts) {
96 qWarning(
97 msg: "Some of these tests depend on the internals of some test fonts which are not freely "
98 "distributable.\n"
99 "These tests will be skipped.\n"
100 "If you have the fonts available, set QT_HAVE_TEST_FONTS=1 in your environment and "
101 "run the test again."
102 );
103 }
104}
105
106struct ShapeTable {
107 unsigned short unicode[16];
108 unsigned short glyphs[16];
109};
110
111static void prepareShapingTest(const QFont &font, const ShapeTable *shape_table)
112{
113 for (const ShapeTable *s = shape_table; s->unicode[0]; ++s) {
114 QByteArray testName = font.family().toLatin1() + ':';
115 QString string;
116 for (const ushort *u = s->unicode; *u; ++u) {
117 string.append(c: QChar(*u));
118 testName.append(a: " 0x" + QByteArray::number(*u, base: 16));
119 }
120 QVector<ushort> glyphs;
121 for (const ushort *g = s->glyphs; *g; ++g)
122 glyphs.append(t: *g);
123
124 QTest::newRow(dataTag: testName.constData()) << font << string << glyphs;
125 }
126}
127
128static void doShapingTests()
129{
130 QFETCH(QFont, font);
131 QFETCH(QString, string);
132 QFETCH(QVector<ushort>, glyphs);
133
134 QVERIFY(!string.isEmpty());
135
136 QTextLayout layout(string, font);
137 QTextEngine *e = layout.engine();
138 e->itemize();
139 e->shape(item: 0);
140
141 QVERIFY(!e->layoutData->items.isEmpty());
142 if (e->fontEngine(si: e->layoutData->items[0])->type() == QFontEngine::Box)
143 QSKIP("OpenType support missing for script");
144
145 QCOMPARE(e->fontEngine(e->layoutData->items[0])->fontDef.family, font.family());
146
147 ushort nglyphs = glyphs.size();
148 if (!glyphs.isEmpty()) {
149 QCOMPARE(e->layoutData->items[0].num_glyphs, nglyphs);
150 for (ushort i = 0; i < glyphs.size(); ++i) {
151 ushort glyph = (e->layoutData->glyphLayout.glyphs[i] & 0xffffff);
152 QCOMPARE(glyph, glyphs.at(i));
153 }
154 } else {
155 // decomposed shaping
156 if (string.at(i: 0) == QChar(0x1fc1) || string.at(i: 0) == QChar(0x1fed))
157 return;
158 if (string.normalized(mode: QString::NormalizationForm_D).normalized(mode: QString::NormalizationForm_C) != string)
159 return;
160
161 QTextLayout decomposedLayout(string.normalized(mode: QString::NormalizationForm_D), font);
162 QTextEngine *de = decomposedLayout.engine();
163 de->itemize();
164 de->shape(item: 0);
165
166 QCOMPARE(de->layoutData->items[0].num_glyphs, e->layoutData->items[0].num_glyphs);
167 for (ushort i = 0; i < nglyphs; ++i) {
168 ushort glyph = (e->layoutData->glyphLayout.glyphs[i] & 0xffffff);
169 ushort glyph2 = (de->layoutData->glyphLayout.glyphs[i] & 0xffffff);
170 QCOMPARE(glyph2, glyph);
171 }
172 }
173}
174
175void tst_QTextScriptEngine::devanagari_data()
176{
177 QTest::addColumn<QFont>(name: "font");
178 QTest::addColumn<QString>(name: "string");
179 QTest::addColumn<QVector<ushort> >(name: "glyphs");
180
181 if (!haveTestFonts)
182 QSKIP("Test fonts are not available");
183
184 {
185 if (QFontDatabase().families(writingSystem: QFontDatabase::Devanagari).contains(str: "Raghindi")) {
186 QFont f("Raghindi");
187 const ShapeTable shape_table [] = {
188 // Ka
189 { .unicode: { 0x0915, 0x0 },
190 .glyphs: { 0x0080, 0x0 } },
191 // Ka Halant
192 { .unicode: { 0x0915, 0x094d, 0x0 },
193 .glyphs: { 0x0080, 0x0051, 0x0 } },
194 // Ka Halant Ka
195 { .unicode: { 0x0915, 0x094d, 0x0915, 0x0 },
196 .glyphs: { 0x00c8, 0x0080, 0x0 } },
197 // Ka MatraI
198 { .unicode: { 0x0915, 0x093f, 0x0 },
199 .glyphs: { 0x01d1, 0x0080, 0x0 } },
200 // Ra Halant Ka
201 { .unicode: { 0x0930, 0x094d, 0x0915, 0x0 },
202 .glyphs: { 0x0080, 0x005b, 0x0 } },
203 // Ra Halant Ka MatraI
204 { .unicode: { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
205 .glyphs: { 0x01d1, 0x0080, 0x005b, 0x0 } },
206 // MatraI
207 { .unicode: { 0x093f, 0x0 },
208 .glyphs: { 0x01d4, 0x029c, 0x0 } },
209 // Ka Nukta
210 { .unicode: { 0x0915, 0x093c, 0x0 },
211 .glyphs: { 0x00a4, 0x0 } },
212 // Ka Halant Ra
213 { .unicode: { 0x0915, 0x094d, 0x0930, 0x0 },
214 .glyphs: { 0x0110, 0x0 } },
215 // Ka Halant Ra Halant Ka
216 { .unicode: { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
217 .glyphs: { 0x0158, 0x0080, 0x0 } },
218 { .unicode: { 0x0930, 0x094d, 0x200d, 0x0 },
219 .glyphs: { 0x00e2, 0x0 } },
220 { .unicode: { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
221 .glyphs: { 0x0158, 0x0 } },
222
223 { .unicode: {0}, .glyphs: {0} }
224 };
225 prepareShapingTest(font: f, shape_table);
226 } else
227 QSKIP("couldn't find Raghindi");
228 }
229
230 {
231 if (QFontDatabase().families(writingSystem: QFontDatabase::Devanagari).contains(str: "Mangal")) {
232 QFont f("Mangal");
233 const ShapeTable shape_table [] = {
234 // Ka
235 { .unicode: { 0x0915, 0x0 },
236 .glyphs: { 0x0080, 0x0 } },
237 // Ka Halant
238 { .unicode: { 0x0915, 0x094d, 0x0 },
239 .glyphs: { 0x0080, 0x0051, 0x0 } },
240 // Ka Halant Ka
241 { .unicode: { 0x0915, 0x094d, 0x0915, 0x0 },
242 .glyphs: { 0x00c8, 0x0080, 0x0 } },
243 // Ka MatraI
244 { .unicode: { 0x0915, 0x093f, 0x0 },
245 .glyphs: { 0x01d1, 0x0080, 0x0 } },
246 // Ra Halant Ka
247 { .unicode: { 0x0930, 0x094d, 0x0915, 0x0 },
248 .glyphs: { 0x0080, 0x005b, 0x0 } },
249 // Ra Halant Ka MatraI
250 { .unicode: { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
251 .glyphs: { 0x01d1, 0x0080, 0x005b, 0x0 } },
252 // MatraI
253 { .unicode: { 0x093f, 0x0 },
254 .glyphs: { 0x01d4, 0x029c, 0x0 } },
255 // Ka Nukta
256 { .unicode: { 0x0915, 0x093c, 0x0 },
257 .glyphs: { 0x00a4, 0x0 } },
258 // Ka Halant Ra
259 { .unicode: { 0x0915, 0x094d, 0x0930, 0x0 },
260 .glyphs: { 0x0110, 0x0 } },
261 // Ka Halant Ra Halant Ka
262 { .unicode: { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
263 .glyphs: { 0x0158, 0x0080, 0x0 } },
264
265 { .unicode: { 0x92b, 0x94d, 0x930, 0x0 },
266 .glyphs: { 0x125, 0x0 } },
267 { .unicode: { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
268 .glyphs: { 0x149, 0x0 } },
269 { .unicode: {0}, .glyphs: {0} }
270 };
271 prepareShapingTest(font: f, shape_table);
272 } else
273 QSKIP("couldn't find mangal");
274 }
275}
276
277void tst_QTextScriptEngine::devanagari()
278{
279 doShapingTests();
280}
281
282void tst_QTextScriptEngine::bengali_data()
283{
284 QTest::addColumn<QFont>(name: "font");
285 QTest::addColumn<QString>(name: "string");
286 QTest::addColumn<QVector<ushort> >(name: "glyphs");
287
288 if (!haveTestFonts)
289 QSKIP("Test fonts are not available");
290
291 {
292 if (QFontDatabase().families(writingSystem: QFontDatabase::Bengali).contains(str: "Akaash")) {
293 QFont f("Akaash");
294 const ShapeTable shape_table [] = {
295 // Ka
296 { .unicode: { 0x0995, 0x0 },
297 .glyphs: { 0x0151, 0x0 } },
298 // Ka Halant
299 { .unicode: { 0x0995, 0x09cd, 0x0 },
300 .glyphs: { 0x0151, 0x017d, 0x0 } },
301 // Ka Halant Ka
302 { .unicode: { 0x0995, 0x09cd, 0x0995, 0x0 },
303 .glyphs: { 0x019b, 0x0 } },
304 // Ka MatraI
305 { .unicode: { 0x0995, 0x09bf, 0x0 },
306 .glyphs: { 0x0173, 0x0151, 0x0 } },
307 // Ra Halant Ka
308 { .unicode: { 0x09b0, 0x09cd, 0x0995, 0x0 },
309 .glyphs: { 0x0151, 0x0276, 0x0 } },
310 // Ra Halant Ka MatraI
311 { .unicode: { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
312 .glyphs: { 0x0173, 0x0151, 0x0276, 0x0 } },
313 // Ka Nukta
314 { .unicode: { 0x0995, 0x09bc, 0x0 },
315 .glyphs: { 0x0151, 0x0171, 0x0 } },
316 // Ka Halant Ra
317 { .unicode: { 0x0995, 0x09cd, 0x09b0, 0x0 },
318 .glyphs: { 0x01f4, 0x0 } },
319 // Ka Halant Ra Halant Ka
320 { .unicode: { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
321 .glyphs: { 0x025c, 0x0276, 0x0151, 0x0 } },
322 // Ya + Halant
323 { .unicode: { 0x09af, 0x09cd, 0x0 },
324 .glyphs: { 0x016a, 0x017d, 0x0 } },
325 // Da Halant Ya -> Da Ya-Phala
326 { .unicode: { 0x09a6, 0x09cd, 0x09af, 0x0 },
327 .glyphs: { 0x01e5, 0x0 } },
328 // A Halant Ya -> A Ya-phala
329 { .unicode: { 0x0985, 0x09cd, 0x09af, 0x0 },
330 .glyphs: { 0x0145, 0x01cf, 0x0 } },
331 // Na Halant Ka
332 { .unicode: { 0x09a8, 0x09cd, 0x0995, 0x0 },
333 .glyphs: { 0x026f, 0x0151, 0x0 } },
334 // Na Halant ZWNJ Ka
335 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
336 .glyphs: { 0x0164, 0x017d, 0x0151, 0x0 } },
337 // Na Halant ZWJ Ka
338 { .unicode: { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
339 .glyphs: { 0x026f, 0x0151, 0x0 } },
340 // Ka Halant ZWNJ Ka
341 { .unicode: { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
342 .glyphs: { 0x0151, 0x017d, 0x0151, 0x0 } },
343 // Ka Halant ZWJ Ka
344 { .unicode: { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
345 .glyphs: { 0x025c, 0x0151, 0x0 } },
346 // Na Halant Ra
347 { .unicode: { 0x09a8, 0x09cd, 0x09b0, 0x0 },
348 .glyphs: { 0x0207, 0x0 } },
349 // Na Halant ZWNJ Ra
350 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
351 .glyphs: { 0x0164, 0x017d, 0x016b, 0x0 } },
352 // Na Halant ZWJ Ra
353 { .unicode: { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
354 .glyphs: { 0x026f, 0x016b, 0x0 } },
355 // Na Halant Ba
356 { .unicode: { 0x09a8, 0x09cd, 0x09ac, 0x0 },
357 .glyphs: { 0x022f, 0x0 } },
358 // Na Halant ZWNJ Ba
359 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
360 .glyphs: { 0x0164, 0x017d, 0x0167, 0x0 } },
361 // Na Halant ZWJ Ba
362 { .unicode: { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
363 .glyphs: { 0x026f, 0x0167, 0x0 } },
364 // Na Halant Dha
365 { .unicode: { 0x09a8, 0x09cd, 0x09a7, 0x0 },
366 .glyphs: { 0x01d3, 0x0 } },
367 // Na Halant ZWNJ Dha
368 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
369 .glyphs: { 0x0164, 0x017d, 0x0163, 0x0 } },
370 // Na Halant ZWJ Dha
371 { .unicode: { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
372 .glyphs: { 0x026f, 0x0163, 0x0 } },
373 // Ra Halant Ka MatraAU
374 { .unicode: { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
375 .glyphs: { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
376 // Ra Halant Ba Halant Ba
377 { .unicode: { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
378 .glyphs: { 0x0232, 0x0276, 0x0 } },
379 { .unicode: { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
380 .glyphs: { 0x151, 0x276, 0x172, 0x143, 0x0 } },
381 { .unicode: { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
382 .glyphs: { 0x151, 0x276, 0x172, 0x144, 0x0 } },
383 // test decomposed two parts matras
384 { .unicode: { 0x995, 0x9c7, 0x9be, 0x0 },
385 .glyphs: { 0x179, 0x151, 0x172, 0x0 } },
386 { .unicode: { 0x995, 0x9c7, 0x9d7, 0x0 },
387 .glyphs: { 0x179, 0x151, 0x17e, 0x0 } },
388 { .unicode: {0}, .glyphs: {0} }
389 };
390 prepareShapingTest(font: f, shape_table);
391 } else
392 QSKIP("couldn't find Akaash");
393 }
394 {
395 if (QFontDatabase().families(writingSystem: QFontDatabase::Bengali).contains(str: "Mukti Narrow")) {
396 QFont f("Mukti Narrow");
397 const ShapeTable shape_table [] = {
398 // Ka
399 { .unicode: { 0x0995, 0x0 },
400 .glyphs: { 0x0073, 0x0 } },
401 // Ka Halant
402 { .unicode: { 0x0995, 0x09cd, 0x0 },
403 .glyphs: { 0x00b9, 0x0 } },
404 // Ka Halant Ka
405 { .unicode: { 0x0995, 0x09cd, 0x0995, 0x0 },
406 .glyphs: { 0x0109, 0x0 } },
407 // Ka MatraI
408 { .unicode: { 0x0995, 0x09bf, 0x0 },
409 .glyphs: { 0x0095, 0x0073, 0x0 } },
410 // Ra Halant Ka
411 { .unicode: { 0x09b0, 0x09cd, 0x0995, 0x0 },
412 .glyphs: { 0x0073, 0x00e1, 0x0 } },
413 // Ra Halant Ka MatraI
414 { .unicode: { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
415 .glyphs: { 0x0095, 0x0073, 0x00e1, 0x0 } },
416 // MatraI
417 { .unicode: { 0x09bf, 0x0 },
418 .glyphs: { 0x0095, 0x01c8, 0x0 } },
419 // Ka Nukta
420 { .unicode: { 0x0995, 0x09bc, 0x0 },
421 .glyphs: { 0x0073, 0x0093, 0x0 } },
422 // Ka Halant Ra
423 { .unicode: { 0x0995, 0x09cd, 0x09b0, 0x0 },
424 .glyphs: { 0x00e5, 0x0 } },
425 // Ka Halant Ra Halant Ka
426 { .unicode: { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
427 .glyphs: { 0x234, 0x24e, 0x73, 0x0 } },
428 // Ya + Halant
429 { .unicode: { 0x09af, 0x09cd, 0x0 },
430 .glyphs: { 0x00d2, 0x0 } },
431 // Da Halant Ya -> Da Ya-Phala
432 { .unicode: { 0x09a6, 0x09cd, 0x09af, 0x0 },
433 .glyphs: { 0x0084, 0x00e2, 0x0 } },
434 // A Halant Ya -> A Ya-phala
435 { .unicode: { 0x0985, 0x09cd, 0x09af, 0x0 },
436 .glyphs: { 0x0067, 0x00e2, 0x0 } },
437 // Na Halant Ka
438 { .unicode: { 0x09a8, 0x09cd, 0x0995, 0x0 },
439 .glyphs: { 0x0188, 0x0 } },
440 // Na Halant ZWNJ Ka
441 { .unicode: { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
442 .glyphs: { 0xcc, 0x73, 0x0 } },
443 // Na Halant ZWJ Ka
444 { .unicode: { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
445 .glyphs: { 0x247, 0x73, 0x0 } },
446 // Ka Halant ZWNJ Ka
447 { .unicode: { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
448 .glyphs: { 0x247, 0x73, 0x0 } },
449 // Ka Halant ZWJ Ka
450 { .unicode: { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
451 .glyphs: { 0x247, 0x73, 0x0 } },
452 // Na Halant Ra
453 { .unicode: { 0x09a8, 0x09cd, 0x09b0, 0x0 },
454 .glyphs: { 0x00f8, 0x0 } },
455 // Na Halant ZWNJ Ra
456 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
457 .glyphs: { 0xcc, 0x8d, 0x0 } },
458 // Na Halant ZWJ Ra
459 { .unicode: { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
460 .glyphs: { 0x247, 0x8d, 0x0 } },
461 // Na Halant Ba
462 { .unicode: { 0x09a8, 0x09cd, 0x09ac, 0x0 },
463 .glyphs: { 0x0139, 0x0 } },
464 // Na Halant ZWNJ Ba
465 { .unicode: { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
466 .glyphs: { 0xcc, 0x89, 0x0 } },
467 // Na Halant ZWJ Ba
468 { .unicode: { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
469 .glyphs: { 0x247, 0x89, 0x0 } },
470 // Na Halant Dha
471 { .unicode: { 0x09a8, 0x09cd, 0x09a7, 0x0 },
472 .glyphs: { 0x0145, 0x0 } },
473 // Na Halant ZWNJ Dha
474 { .unicode: { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
475 .glyphs: { 0xcc, 0x85, 0x0 } },
476 // Na Halant ZWJ Dha
477 { .unicode: { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
478 .glyphs: { 0x247, 0x85, 0x0 } },
479 // Ra Halant Ka MatraAU
480 { .unicode: { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
481 .glyphs: { 0x232, 0x73, 0xe1, 0xa0, 0x0 } },
482 // Ra Halant Ba Halant Ba
483 { .unicode: { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
484 .glyphs: { 0x013b, 0x00e1, 0x0 } },
485
486 { .unicode: {0}, .glyphs: {0} }
487 };
488 prepareShapingTest(font: f, shape_table);
489 } else
490 QSKIP("couldn't find Mukti");
491 }
492 {
493 if (QFontDatabase().families(writingSystem: QFontDatabase::Bengali).contains(str: "Likhan")) {
494 QFont f("Likhan");
495 const ShapeTable shape_table [] = {
496 { .unicode: { 0x9a8, 0x9cd, 0x9af, 0x0 },
497 .glyphs: { 0x1ca, 0x0 } },
498 { .unicode: { 0x09b8, 0x09cd, 0x09af, 0x0 },
499 .glyphs: { 0x020e, 0x0 } },
500 { .unicode: { 0x09b6, 0x09cd, 0x09af, 0x0 },
501 .glyphs: { 0x01f4, 0x0 } },
502 { .unicode: { 0x09b7, 0x09cd, 0x09af, 0x0 },
503 .glyphs: { 0x01fe, 0x0 } },
504 { .unicode: { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
505 .glyphs: { 0x10b, 0x167, 0x0 } },
506
507 { .unicode: {0}, .glyphs: {0} }
508 };
509 prepareShapingTest(font: f, shape_table);
510 } else
511 QSKIP("couldn't find Likhan");
512 }
513}
514
515void tst_QTextScriptEngine::bengali()
516{
517 doShapingTests();
518}
519
520void tst_QTextScriptEngine::gurmukhi_data()
521{
522 QTest::addColumn<QFont>(name: "font");
523 QTest::addColumn<QString>(name: "string");
524 QTest::addColumn<QVector<ushort> >(name: "glyphs");
525
526 if (!haveTestFonts)
527 QSKIP("Test fonts are not available");
528
529 {
530 if (QFontDatabase().families(writingSystem: QFontDatabase::Gurmukhi).contains(str: "Lohit Punjabi")) {
531 QFont f("Lohit Punjabi");
532 const ShapeTable shape_table [] = {
533 { .unicode: { 0xA15, 0xA4D, 0xa39, 0x0 },
534 .glyphs: { 0x3b, 0x8b, 0x0 } },
535 { .unicode: {0}, .glyphs: {0} }
536 };
537 prepareShapingTest(font: f, shape_table);
538 } else
539 QSKIP("couldn't find Lohit Punjabi");
540 }
541}
542
543void tst_QTextScriptEngine::gurmukhi()
544{
545 doShapingTests();
546}
547
548void tst_QTextScriptEngine::oriya_data()
549{
550 QTest::addColumn<QFont>(name: "font");
551 QTest::addColumn<QString>(name: "string");
552 QTest::addColumn<QVector<ushort> >(name: "glyphs");
553
554 if (!haveTestFonts)
555 QSKIP("Test fonts are not available");
556
557 {
558 if (QFontDatabase().families(writingSystem: QFontDatabase::Oriya).contains(str: "utkal")) {
559 QFont f("utkal");
560 const ShapeTable shape_table [] = {
561 { .unicode: { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
562 .glyphs: { 0x150, 0x125, 0x0 } },
563 { .unicode: { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
564 .glyphs: { 0x151, 0x120, 0x0 } },
565 { .unicode: { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
566 .glyphs: { 0x152, 0x120, 0x0 } },
567 { .unicode: { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
568 .glyphs: { 0x152, 0x120, 0x0 } },
569 { .unicode: { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
570 .glyphs: { 0x176, 0x0 } },
571 { .unicode: { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
572 .glyphs: { 0x177, 0x0 } },
573 { .unicode: { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
574 .glyphs: { 0x176, 0x124, 0x0 } },
575 { .unicode: {0}, .glyphs: {0} }
576
577 };
578 prepareShapingTest(font: f, shape_table);
579 } else
580 QSKIP("couldn't find utkal");
581 }
582}
583
584void tst_QTextScriptEngine::oriya()
585{
586 doShapingTests();
587}
588
589void tst_QTextScriptEngine::tamil_data()
590{
591 QTest::addColumn<QFont>(name: "font");
592 QTest::addColumn<QString>(name: "string");
593 QTest::addColumn<QVector<ushort> >(name: "glyphs");
594
595 if (!haveTestFonts)
596 QSKIP("Test fonts are not available");
597
598 {
599 if (QFontDatabase().families(writingSystem: QFontDatabase::Tamil).contains(str: "AkrutiTml1")) {
600 QFont f("AkrutiTml1");
601 const ShapeTable shape_table [] = {
602 { .unicode: { 0x0b95, 0x0bc2, 0x0 },
603 .glyphs: { 0x004e, 0x0 } },
604 { .unicode: { 0x0bae, 0x0bc2, 0x0 },
605 .glyphs: { 0x009e, 0x0 } },
606 { .unicode: { 0x0b9a, 0x0bc2, 0x0 },
607 .glyphs: { 0x0058, 0x0 } },
608 { .unicode: { 0x0b99, 0x0bc2, 0x0 },
609 .glyphs: { 0x0053, 0x0 } },
610 { .unicode: { 0x0bb0, 0x0bc2, 0x0 },
611 .glyphs: { 0x00a8, 0x0 } },
612 { .unicode: { 0x0ba4, 0x0bc2, 0x0 },
613 .glyphs: { 0x008e, 0x0 } },
614 { .unicode: { 0x0b9f, 0x0bc2, 0x0 },
615 .glyphs: { 0x0062, 0x0 } },
616 { .unicode: { 0x0b95, 0x0bc6, 0x0 },
617 .glyphs: { 0x000a, 0x0031, 0x0 } },
618 { .unicode: { 0x0b95, 0x0bca, 0x0 },
619 .glyphs: { 0x000a, 0x0031, 0x0007, 0x0 } },
620 { .unicode: { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
621 .glyphs: { 0x000a, 0x0031, 0x007, 0x0 } },
622 { .unicode: { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
623 .glyphs: { 0x0049, 0x0 } },
624 { .unicode: { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
625 .glyphs: { 0x000a, 0x0049, 0x007, 0x0 } },
626 { .unicode: { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
627 .glyphs: { 0x000a, 0x0049, 0x007, 0x0 } },
628 { .unicode: { 0x0b9f, 0x0bbf, 0x0 },
629 .glyphs: { 0x005f, 0x0 } },
630 { .unicode: { 0x0b9f, 0x0bc0, 0x0 },
631 .glyphs: { 0x0060, 0x0 } },
632 { .unicode: { 0x0bb2, 0x0bc0, 0x0 },
633 .glyphs: { 0x00ab, 0x0 } },
634 { .unicode: { 0x0bb2, 0x0bbf, 0x0 },
635 .glyphs: { 0x00aa, 0x0 } },
636 { .unicode: { 0x0bb0, 0x0bcd, 0x0 },
637 .glyphs: { 0x00a4, 0x0 } },
638 { .unicode: { 0x0bb0, 0x0bbf, 0x0 },
639 .glyphs: { 0x00a5, 0x0 } },
640 { .unicode: { 0x0bb0, 0x0bc0, 0x0 },
641 .glyphs: { 0x00a6, 0x0 } },
642 { .unicode: { 0x0b83, 0x0 },
643 .glyphs: { 0x0025, 0x0 } },
644 { .unicode: { 0x0b83, 0x0b95, 0x0 },
645 .glyphs: { 0x0025, 0x0031, 0x0 } },
646 { .unicode: { 0xb95, 0xbc6, 0xbbe, 0x0 },
647 .glyphs: { 0xa, 0x31, 0x7, 0x0 } },
648 { .unicode: { 0xb95, 0xbc7, 0xbbe, 0x0 },
649 .glyphs: { 0xb, 0x31, 0x7, 0x0 } },
650 { .unicode: { 0xb95, 0xbc6, 0xbd7, 0x0 },
651 .glyphs: { 0xa, 0x31, 0x40, 0x0 } },
652
653 { .unicode: {0}, .glyphs: {0} }
654 };
655 prepareShapingTest(font: f, shape_table);
656 } else
657 QSKIP("couldn't find AkrutiTml1");
658 }
659}
660
661void tst_QTextScriptEngine::tamil()
662{
663 doShapingTests();
664}
665
666void tst_QTextScriptEngine::telugu_data()
667{
668 QTest::addColumn<QFont>(name: "font");
669 QTest::addColumn<QString>(name: "string");
670 QTest::addColumn<QVector<ushort> >(name: "glyphs");
671
672 if (!haveTestFonts)
673 QSKIP("Test fonts are not available");
674
675 {
676 if (QFontDatabase().families(writingSystem: QFontDatabase::Telugu).contains(str: "Pothana2000")) {
677 QFont f("Pothana2000");
678 const ShapeTable shape_table [] = {
679 { .unicode: { 0xc15, 0xc4d, 0x0 },
680 .glyphs: { 0xbb, 0x0 } },
681 { .unicode: { 0xc15, 0xc4d, 0xc37, 0x0 },
682 .glyphs: { 0x4b, 0x0 } },
683 { .unicode: { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
684 .glyphs: { 0xe0, 0x0 } },
685 { .unicode: { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
686 .glyphs: { 0x4b, 0x91, 0x0 } },
687 { .unicode: { 0xc15, 0xc4d, 0xc30, 0x0 },
688 .glyphs: { 0x5a, 0xb2, 0x0 } },
689 { .unicode: { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
690 .glyphs: { 0xbb, 0xb2, 0x0 } },
691 { .unicode: { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
692 .glyphs: { 0x5a, 0xb2, 0x83, 0x0 } },
693 { .unicode: { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
694 .glyphs: { 0xe2, 0xb2, 0x0 } },
695 { .unicode: { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
696 .glyphs: { 0xe6, 0xb3, 0x83, 0x0 } },
697 { .unicode: { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
698 .glyphs: { 0xe6, 0xb3, 0x9f, 0x0 } },
699 { .unicode: { 0xc15, 0xc46, 0xc56, 0x0 },
700 .glyphs: { 0xe6, 0xb3, 0x0 } },
701 { .unicode: {0}, .glyphs: {0} }
702
703 };
704 prepareShapingTest(font: f, shape_table);
705 } else
706 QSKIP("couldn't find Pothana2000");
707 }
708}
709
710void tst_QTextScriptEngine::telugu()
711{
712 doShapingTests();
713}
714
715void tst_QTextScriptEngine::kannada_data()
716{
717 QTest::addColumn<QFont>(name: "font");
718 QTest::addColumn<QString>(name: "string");
719 QTest::addColumn<QVector<ushort> >(name: "glyphs");
720
721 if (!haveTestFonts)
722 QSKIP("Test fonts are not available");
723
724 {
725 if (QFontDatabase().families(writingSystem: QFontDatabase::Kannada).contains(str: "Sampige")) {
726 QFont f("Sampige");
727 const ShapeTable shape_table [] = {
728 { .unicode: { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
729 .glyphs: { 0x0049, 0x00ba, 0x0 } },
730 { .unicode: { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
731 .glyphs: { 0x0049, 0x00b3, 0x0 } },
732 { .unicode: { 0x0caf, 0x0cc2, 0x0 },
733 .glyphs: { 0x004f, 0x005d, 0x0 } },
734 { .unicode: { 0x0ce0, 0x0 },
735 .glyphs: { 0x006a, 0x0 } },
736 { .unicode: { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
737 .glyphs: { 0x006b, 0x006c, 0x006d, 0x0 } },
738 { .unicode: { 0x0cb5, 0x0ccb, 0x0 },
739 .glyphs: { 0x015f, 0x0067, 0x0 } },
740 { .unicode: { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
741 .glyphs: { 0x004e, 0x0082, 0x0 } },
742 { .unicode: { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
743 .glyphs: { 0x0036, 0x0082, 0x0 } },
744 { .unicode: { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
745 .glyphs: { 0x0036, 0x00c1, 0x0 } },
746 { .unicode: { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
747 .glyphs: { 0x0050, 0x00a7, 0x0 } },
748
749 { .unicode: {0}, .glyphs: {0} }
750 };
751 prepareShapingTest(font: f, shape_table);
752 } else
753 QSKIP("couldn't find Sampige");
754 }
755 {
756 if (QFontDatabase().families(writingSystem: QFontDatabase::Kannada).contains(str: "Tunga")) {
757 QFont f("Tunga");
758 const ShapeTable shape_table [] = {
759 { .unicode: { 0x0cb7, 0x0cc6, 0x0 },
760 .glyphs: { 0x00b0, 0x006c, 0x0 } },
761 { .unicode: { 0x0cb7, 0x0ccd, 0x0 },
762 .glyphs: { 0x0163, 0x0 } },
763 { .unicode: { 0xc95, 0xcbf, 0xcd5, 0x0 },
764 .glyphs: { 0x114, 0x73, 0x0 } },
765 { .unicode: { 0xc95, 0xcc6, 0xcd5, 0x0 },
766 .glyphs: { 0x90, 0x6c, 0x73, 0x0 } },
767 { .unicode: { 0xc95, 0xcc6, 0xcd6, 0x0 },
768 .glyphs: { 0x90, 0x6c, 0x74, 0x0 } },
769 { .unicode: { 0xc95, 0xcc6, 0xcc2, 0x0 },
770 .glyphs: { 0x90, 0x6c, 0x69, 0x0 } },
771 { .unicode: { 0xc95, 0xcca, 0xcd5, 0x0 },
772 .glyphs: { 0x90, 0x6c, 0x69, 0x73, 0x0 } },
773 { .unicode: {0}, .glyphs: {0} }
774 };
775 prepareShapingTest(font: f, shape_table);
776 } else
777 QSKIP("couldn't find Tunga");
778 }
779}
780
781void tst_QTextScriptEngine::kannada()
782{
783 doShapingTests();
784}
785
786void tst_QTextScriptEngine::malayalam_data()
787{
788 QTest::addColumn<QFont>(name: "font");
789 QTest::addColumn<QString>(name: "string");
790 QTest::addColumn<QVector<ushort> >(name: "glyphs");
791
792 if (!haveTestFonts)
793 QSKIP("Test fonts are not available");
794
795 {
796 if (QFontDatabase().families(writingSystem: QFontDatabase::Malayalam).contains(str: "AkrutiMal2")) {
797 QFont f("AkrutiMal2");
798 const ShapeTable shape_table [] = {
799 { .unicode: { 0x0d15, 0x0d46, 0x0 },
800 .glyphs: { 0x005e, 0x0034, 0x0 } },
801 { .unicode: { 0x0d15, 0x0d47, 0x0 },
802 .glyphs: { 0x005f, 0x0034, 0x0 } },
803 { .unicode: { 0x0d15, 0x0d4b, 0x0 },
804 .glyphs: { 0x005f, 0x0034, 0x0058, 0x0 } },
805 { .unicode: { 0x0d15, 0x0d48, 0x0 },
806 .glyphs: { 0x0060, 0x0034, 0x0 } },
807 { .unicode: { 0x0d15, 0x0d4a, 0x0 },
808 .glyphs: { 0x005e, 0x0034, 0x0058, 0x0 } },
809 { .unicode: { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
810 .glyphs: { 0x009e, 0x0034, 0x0 } },
811 { .unicode: { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
812 .glyphs: { 0x0034, 0x007a, 0x0 } },
813 { .unicode: { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
814 .glyphs: { 0x0034, 0x00a2, 0x0 } },
815 { .unicode: { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
816 .glyphs: { 0x0069, 0x0 } },
817 { .unicode: { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
818 .glyphs: { 0x0074, 0x0 } },
819 { .unicode: { 0x0d30, 0x0d4d, 0x0 },
820 .glyphs: { 0x009e, 0x0 } },
821 { .unicode: { 0x0d30, 0x0d4d, 0x200c, 0x0 },
822 .glyphs: { 0x009e, 0x0 } },
823 { .unicode: { 0x0d30, 0x0d4d, 0x200d, 0x0 },
824 .glyphs: { 0x009e, 0x0 } },
825 { .unicode: { 0xd15, 0xd46, 0xd3e, 0x0 },
826 .glyphs: { 0x5e, 0x34, 0x58, 0x0 } },
827 { .unicode: { 0xd15, 0xd47, 0xd3e, 0x0 },
828 .glyphs: { 0x5f, 0x34, 0x58, 0x0 } },
829 { .unicode: { 0xd15, 0xd46, 0xd57, 0x0 },
830 .glyphs: { 0x5e, 0x34, 0x65, 0x0 } },
831 { .unicode: { 0xd15, 0xd57, 0x0 },
832 .glyphs: { 0x34, 0x65, 0x0 } },
833 { .unicode: {0}, .glyphs: {0} }
834 };
835 prepareShapingTest(font: f, shape_table);
836 } else
837 QSKIP("couldn't find AkrutiMal2");
838 }
839 {
840 if (QFontDatabase().families(writingSystem: QFontDatabase::Malayalam).contains(str: "Rachana")) {
841 QFont f("Rachana");
842 const ShapeTable shape_table [] = {
843 { .unicode: { 0xd37, 0xd4d, 0xd1f, 0xd4d, 0xd30, 0xd40, 0x0 },
844 .glyphs: { 0x385, 0xa3, 0x0 } },
845 { .unicode: { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
846 .glyphs: { 0x2ff, 0x0 } },
847 { .unicode: { 0xd33, 0xd4d, 0xd33, 0x0 },
848 .glyphs: { 0x3f8, 0x0 } },
849 { .unicode: { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
850 .glyphs: { 0x2ff, 0x0 } },
851 { .unicode: { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 },
852 .glyphs: { 0xf3, 0x350, 0x0 } },
853
854 { .unicode: {0}, .glyphs: {0} }
855 };
856 prepareShapingTest(font: f, shape_table);
857 } else
858 QSKIP("couldn't find Rachana");
859 }
860}
861
862void tst_QTextScriptEngine::malayalam()
863{
864 doShapingTests();
865}
866
867void tst_QTextScriptEngine::sinhala_data()
868{
869 QTest::addColumn<QFont>(name: "font");
870 QTest::addColumn<QString>(name: "string");
871 QTest::addColumn<QVector<ushort> >(name: "glyphs");
872
873 if (!haveTestFonts)
874 QSKIP("Test fonts are not available");
875
876 {
877 if (QFontDatabase().families(writingSystem: QFontDatabase::Sinhala).contains(str: "Malithi Web")) {
878 QFont f("Malithi Web");
879 const ShapeTable shape_table [] = {
880 { .unicode: { 0xd9a, 0xdd9, 0xdcf, 0x0 },
881 .glyphs: { 0x4a, 0x61, 0x42, 0x0 } },
882 { .unicode: { 0xd9a, 0xdd9, 0xddf, 0x0 },
883 .glyphs: { 0x4a, 0x61, 0x50, 0x0 } },
884 { .unicode: { 0xd9a, 0xdd9, 0xdca, 0x0 },
885 .glyphs: { 0x4a, 0x62, 0x0 } },
886 { .unicode: { 0xd9a, 0xddc, 0xdca, 0x0 },
887 .glyphs: { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
888 { .unicode: { 0xd9a, 0xdda, 0x0 },
889 .glyphs: { 0x4a, 0x62, 0x0 } },
890 { .unicode: { 0xd9a, 0xddd, 0x0 },
891 .glyphs: { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
892 { .unicode: {0}, .glyphs: {0} }
893 };
894 prepareShapingTest(font: f, shape_table);
895 } else
896 QSKIP("couldn't find Malithi Web");
897 }
898}
899
900void tst_QTextScriptEngine::sinhala()
901{
902 doShapingTests();
903}
904
905void tst_QTextScriptEngine::khmer_data()
906{
907 QTest::addColumn<QFont>(name: "font");
908 QTest::addColumn<QString>(name: "string");
909 QTest::addColumn<QVector<ushort> >(name: "glyphs");
910
911 if (!haveTestFonts)
912 QSKIP("Test fonts are not available");
913
914 {
915 if (QFontDatabase().families(writingSystem: QFontDatabase::Khmer).contains(str: "Khmer OS")) {
916 QFont f("Khmer OS");
917 const ShapeTable shape_table [] = {
918 { .unicode: { 0x179a, 0x17cd, 0x0 },
919 .glyphs: { 0x24c, 0x27f, 0x0 } },
920 { .unicode: { 0x179f, 0x17c5, 0x0 },
921 .glyphs: { 0x273, 0x203, 0x0 } },
922 { .unicode: { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
923 .glyphs: { 0x275, 0x242, 0x182, 0x0 } },
924 { .unicode: { 0x179a, 0x0 },
925 .glyphs: { 0x24c, 0x0 } },
926 { .unicode: { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
927 .glyphs: { 0x274, 0x233, 0x197, 0x0 } },
928 { .unicode: { 0x1798, 0x17b6, 0x0 },
929 .glyphs: { 0x1cb, 0x0 } },
930 { .unicode: { 0x179a, 0x17b8, 0x0 },
931 .glyphs: { 0x24c, 0x26a, 0x0 } },
932 { .unicode: { 0x1787, 0x17b6, 0x0 },
933 .glyphs: { 0x1ba, 0x0 } },
934 { .unicode: { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
935 .glyphs: { 0x24a, 0x195, 0x26d, 0x0 } },
936 { .unicode: {0}, .glyphs: {0} }
937 };
938 prepareShapingTest(font: f, shape_table);
939 } else
940 QSKIP("couldn't find Khmer OS");
941 }
942}
943
944void tst_QTextScriptEngine::khmer()
945{
946 doShapingTests();
947}
948
949void tst_QTextScriptEngine::linearB_data()
950{
951 QTest::addColumn<QFont>(name: "font");
952 QTest::addColumn<QString>(name: "string");
953 QTest::addColumn<QVector<ushort> >(name: "glyphs");
954
955 if (!haveTestFonts)
956 QSKIP("Test fonts are not available");
957
958 {
959 if (QFontDatabase().families(writingSystem: QFontDatabase::Any).contains(str: "Penuturesu")) {
960 QFont f("Penuturesu");
961 const ShapeTable shape_table [] = {
962 { .unicode: { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 },
963 .glyphs: { 0x5, 0x6, 0x7, 0 } },
964 { .unicode: {0}, .glyphs: {0} }
965 };
966 prepareShapingTest(font: f, shape_table);
967 } else
968 QSKIP("couldn't find Penuturesu");
969 }
970}
971
972void tst_QTextScriptEngine::linearB()
973{
974 doShapingTests();
975}
976
977void tst_QTextScriptEngine::greek_data()
978{
979 QTest::addColumn<QFont>(name: "font");
980 QTest::addColumn<QString>(name: "string");
981 QTest::addColumn<QVector<ushort> >(name: "glyphs");
982
983 if (!haveTestFonts)
984 QSKIP("Test fonts are not available");
985
986 {
987 if (QFontDatabase().families(writingSystem: QFontDatabase::Any).contains(str: "DejaVu Sans")) {
988 QFont f("DejaVu Sans");
989 for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
990 QString string;
991 string.append(c: QChar(uc));
992 QByteArray testName = f.family().toLatin1() + ": 0x" + QByteArray::number(uc, base: 16);
993 QTest::newRow(dataTag: testName.constData()) << f << string << QVector<ushort>();
994 }
995 } else
996 QSKIP("couldn't find DejaVu Sans");
997 }
998
999 {
1000 if (QFontDatabase().families(writingSystem: QFontDatabase::Any).contains(str: "SBL Greek")) {
1001 QFont f("SBL Greek");
1002 for (int uc = 0x1f00; uc <= 0x1fff; ++uc) {
1003 QString string;
1004 string.append(c: QChar(uc));
1005 QByteArray testName = f.family().toLatin1() + ": 0x" + QByteArray::number(uc, base: 16);
1006 QTest::newRow(dataTag: testName.constData()) << f << string << QVector<ushort>();
1007 }
1008
1009 const ShapeTable shape_table [] = {
1010 { .unicode: { 0x3b1, 0x300, 0x313, 0x0 },
1011 .glyphs: { 0xb8, 0x3d3, 0x3c7, 0x0 } },
1012 { .unicode: { 0x3b1, 0x313, 0x300, 0x0 },
1013 .glyphs: { 0xd4, 0x0 } },
1014
1015 { .unicode: {0}, .glyphs: {0} }
1016 };
1017 prepareShapingTest(font: f, shape_table);
1018 } else
1019 QSKIP("couldn't find SBL_grk");
1020 }
1021}
1022
1023void tst_QTextScriptEngine::greek()
1024{
1025 doShapingTests();
1026}
1027
1028void tst_QTextScriptEngine::mirroredChars_data()
1029{
1030 QTest::addColumn<QString>(name: "s");
1031
1032 QTest::newRow(dataTag: "()") << QStringLiteral("()");
1033 QTest::newRow(dataTag: "[]") << QStringLiteral("[]");
1034 QTest::newRow(dataTag: "{}") << QStringLiteral("{}");
1035}
1036
1037void tst_QTextScriptEngine::mirroredChars()
1038{
1039 QFETCH(QString, s);
1040
1041 glyph_t leftParenthesis;
1042 glyph_t rightParenthesis;
1043 {
1044 QTextLayout layout(s);
1045 layout.setCacheEnabled(true);
1046 layout.beginLayout();
1047 layout.createLine();
1048 layout.endLayout();
1049
1050 QTextEngine *e = layout.engine();
1051 e->itemize();
1052 QCOMPARE(e->layoutData->items.size(), 1);
1053
1054 e->shape(item: 0);
1055 QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2));
1056
1057 const QGlyphLayout glyphLayout = e->shapedGlyphs(si: &e->layoutData->items[0]);
1058 leftParenthesis = glyphLayout.glyphs[0];
1059 rightParenthesis = glyphLayout.glyphs[1];
1060 }
1061
1062 {
1063 QTextLayout layout(s);
1064 layout.setFlags(Qt::TextForceRightToLeft);
1065
1066 QTextEngine *e = layout.engine();
1067 e->itemize();
1068 QCOMPARE(e->layoutData->items.size(), 1);
1069
1070 e->shape(item: 0);
1071 QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(2));
1072
1073 const QGlyphLayout glyphLayout = e->shapedGlyphs(si: &e->layoutData->items[0]);
1074 QCOMPARE(glyphLayout.glyphs[0], rightParenthesis);
1075 QCOMPARE(glyphLayout.glyphs[1], leftParenthesis);
1076 }
1077}
1078
1079void tst_QTextScriptEngine::controlInSyllable_qtbug14204()
1080{
1081 QFontDatabase db;
1082 if (!db.families().contains(QStringLiteral("Aparajita")))
1083 QSKIP("couldn't find 'Aparajita' font");
1084
1085 QFont font(QStringLiteral("Aparajita"));
1086 font.setStyleStrategy(QFont::NoFontMerging);
1087
1088 QString s;
1089 s.append(c: QChar(0x0915));
1090 s.append(c: QChar(0x094d));
1091 s.append(c: QChar(0x200d));
1092 s.append(c: QChar(0x0915));
1093
1094 QTextLayout layout(s, font);
1095 QTextEngine *e = layout.engine();
1096 e->itemize();
1097 QCOMPARE(e->layoutData->items.size(), 1);
1098
1099 QFontEngine *fe = e->fontEngine(si: e->layoutData->items[0]);
1100 if (fe->type() == QFontEngine::Box)
1101 QSKIP("OpenType support missing for script");
1102 QCOMPARE(fe->fontDef.family, font.family());
1103
1104 e->shape(item: 0);
1105 QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(3));
1106
1107 const ushort *log_clusters = e->logClusters(si: &e->layoutData->items[0]);
1108 QCOMPARE(log_clusters[0], ushort(0));
1109 QCOMPARE(log_clusters[1], ushort(0));
1110 QCOMPARE(log_clusters[2], ushort(0));
1111 QCOMPARE(log_clusters[3], ushort(2));
1112}
1113
1114void tst_QTextScriptEngine::combiningMarks_qtbug15675_data()
1115{
1116 QTest::addColumn<QFont>(name: "font");
1117 QTest::addColumn<QString>(name: "string");
1118
1119 QSKIP("Result differs for HarfBuzz-NG, skip test.");
1120
1121 bool hasTests = false;
1122
1123 QStringList families;
1124 families << QStringLiteral("Monaco");
1125 families << QStringLiteral("DejaVu Sans Mono");
1126
1127 foreach (const QString &family, families) {
1128 QFont font(family);
1129 font.setStyleStrategy(QFont::NoFontMerging);
1130 if (QFontInfo(font).family() != family)
1131 continue;
1132
1133 hasTests = true;
1134
1135 QString s(QStringLiteral("ab cd"));
1136 for (ushort uc = 0x0300; uc < 0x0370; ++uc) {
1137 s[2] = QChar(uc);
1138 QByteArray testName = family.toLatin1() + ": ab<U+" + QByteArray::number(uc, base: 16).rightJustified(width: 4, fill: '0') + ">cd";
1139 QTest::newRow(dataTag: testName.constData()) << font << s;
1140 }
1141 }
1142
1143 if (!hasTests)
1144 QSKIP("Couldn't find required fonts, skip test.");
1145}
1146
1147void tst_QTextScriptEngine::combiningMarks_qtbug15675()
1148{
1149 QFETCH(QFont, font);
1150 QFETCH(QString, string);
1151
1152 QTextLayout layout(string, font);
1153 QTextEngine *e = layout.engine();
1154 e->itemize();
1155 QCOMPARE(e->layoutData->items.size(), 1);
1156
1157 QFontEngine *fe = e->fontEngine(si: e->layoutData->items[0]);
1158 if (fe->type() == QFontEngine::Box)
1159 QSKIP("OpenType support missing for script");
1160 QCOMPARE(fe->fontDef.family, font.family());
1161
1162 e->shape(item: 0);
1163 const int diff = e->layoutData->items[0].num_glyphs - string.size();
1164 QVERIFY(diff >= -1 && diff <= 1); // could compose or decompose exactly one character
1165
1166 const ushort *log_clusters = e->logClusters(si: &e->layoutData->items[0]);
1167 QCOMPARE(log_clusters[0], ushort(0));
1168 QCOMPARE(log_clusters[1], ushort(1));
1169 QCOMPARE(log_clusters[2], ushort(1));
1170 QCOMPARE(log_clusters[3], ushort(3 + diff));
1171 QCOMPARE(log_clusters[4], ushort(4 + diff));
1172
1173 const QGlyphLayout glyphLayout = e->shapedGlyphs(si: &e->layoutData->items[0]);
1174 for (int i = 0; i < glyphLayout.numGlyphs; ++i) {
1175 if ((diff >= 0 && i == 2) || (diff > 0 && i == 2 + diff))
1176 QCOMPARE(glyphLayout.advances[i].toInt(), 0);
1177 else
1178 QVERIFY(glyphLayout.advances[i].toInt() != 0);
1179 }
1180}
1181
1182void tst_QTextScriptEngine::thaiIsolatedSaraAm()
1183{
1184 QFontDatabase db;
1185 if (!db.families().contains(str: "Waree"))
1186 QSKIP("couldn't find 'Waree' font");
1187
1188 QFont font(QStringLiteral("Waree"));
1189 font.setStyleStrategy(QFont::NoFontMerging);
1190
1191 QString s;
1192 s.append(c: QChar(0x0e33));
1193
1194 QTextLayout layout(s, font);
1195 QTextEngine *e = layout.engine();
1196 e->itemize();
1197 QCOMPARE(e->layoutData->items.size(), 1);
1198
1199 QFontEngine *fe = e->fontEngine(si: e->layoutData->items[0]);
1200 if (fe->type() == QFontEngine::Box)
1201 QSKIP("OpenType support missing for script");
1202 QCOMPARE(fe->fontDef.family, font.family());
1203
1204 e->shape(item: 0);
1205 QVERIFY(e->layoutData->items[0].num_glyphs > 0);
1206
1207 const ushort *log_clusters = e->logClusters(si: &e->layoutData->items[0]);
1208 QCOMPARE(log_clusters[0], ushort(0));
1209}
1210
1211void tst_QTextScriptEngine::thaiWithZWJ()
1212{
1213 QFontDatabase db;
1214 if (!db.families().contains(str: "Waree"))
1215 QSKIP("couldn't find 'Waree' font");
1216
1217 QFont font(QStringLiteral("Waree"));
1218 font.setStyleStrategy(QFont::NoFontMerging);
1219
1220 if (QFontInfo(font).styleName() != QStringLiteral("Book"))
1221 QSKIP("couldn't find 'Waree Book' font");
1222
1223 QString s(QString::fromUtf8(str: "\xe0\xb8\xa3\xe2\x80\x8d\xe0\xb8\xa3\xe2\x80"
1224 "\x8c\x2e\xe0\xb8\xa3\x2e\xe2\x80\x9c\xe0\xb8"
1225 "\xa3\xe2\x80\xa6\xe0\xb8\xa3\xe2\x80\x9d\xe0"
1226 "\xb8\xa3\xa0\xe0\xb8\xa3\xe6\x9c\xac\xe0\xb8\xa3")
1227 + QChar(0x0363)/*superscript 'a', for testing Inherited class*/);
1228
1229 QTextLayout layout(s, font);
1230 QTextEngine *e = layout.engine();
1231 e->itemize();
1232 QCOMPARE(e->layoutData->items.size(), 3);
1233
1234 for (int item = 0; item < e->layoutData->items.size(); ++item)
1235 e->shape(item);
1236
1237 QCOMPARE(e->layoutData->items[0].num_glyphs, ushort(15)); // Thai, Inherited and Common
1238 QCOMPARE(e->layoutData->items[1].num_glyphs, ushort(1)); // Japanese: Kanji for tree
1239 QCOMPARE(e->layoutData->items[2].num_glyphs, ushort(2)); // Thai: Thai character followed by superscript "a" which is of inherited type
1240
1241 //A quick sanity check - check all the characters are individual clusters
1242 // A thai implementation could either remove the ZWJ and ZWNJ characters, or hide them.
1243 // The current implementation hides them, so we test for that.
1244 unsigned short *logClusters = e->layoutData->logClustersPtr;
1245 QCOMPARE(logClusters[0], ushort(0));
1246 QCOMPARE(logClusters[1], ushort(0));
1247 QCOMPARE(logClusters[2], ushort(2));
1248 QCOMPARE(logClusters[3], ushort(2));
1249 for (int i = 4; i < 15; i++)
1250 QCOMPARE(logClusters[i], ushort(i));
1251 for (int i = 0; i < 3; i++)
1252 QCOMPARE(logClusters[i+15], ushort(0));
1253
1254 // The only characters that we should be hiding are the ZWJ and ZWNJ characters in position 1 and 3.
1255 const QGlyphLayout glyphLayout = e->layoutData->glyphLayout;
1256 for (int i = 0; i < 18; i++) {
1257 if (i == 1 || i == 3)
1258 QCOMPARE(glyphLayout.advances[i].toInt(), 0);
1259 else
1260 QVERIFY(glyphLayout.advances[i].toInt() != 0);
1261 }
1262}
1263
1264void tst_QTextScriptEngine::thaiMultipleVowels()
1265{
1266 QString s(QString::fromUtf8(str: "\xe0\xb8\xaa"));
1267 for (int i = 0; i < 100; i++)
1268 s += QChar(0x0E47); // Add lots of "VOWEL SIGN MAI TAI KHU N/S-T" stacked on top of the character
1269 s += QChar(0x200D); // Now add a zero width joiner (which adds a circle which is hidden)
1270 for (int i = 0; i < 100; i++)
1271 s += QChar(0x0E47); //Add lots of "VOWEL SIGN MAI TAI KHU N/S-T" stacked on top of the ZWJ
1272
1273 for (int i = 0; i < 10; i++)
1274 s += s; //Repeat the string to make it more likely to crash if we have a buffer overflow
1275
1276 QTextLayout layout(s);
1277 QTextEngine *e = layout.engine();
1278 e->itemize();
1279
1280 for (int item = 0; item < e->layoutData->items.size(); ++item)
1281 e->shape(item);
1282
1283 // If we haven't crashed at this point, then the test has passed.
1284}
1285
1286void tst_QTextScriptEngine::shapingDisabledLatin()
1287{
1288 QString s("fi");
1289
1290 QFont font("Calibri");
1291 font.setStyleStrategy(QFont::PreferNoShaping);
1292
1293 QTextLayout layout(s);
1294 layout.setFont(font);
1295 layout.beginLayout();
1296 layout.createLine();
1297 layout.endLayout();
1298
1299 QList<QGlyphRun> runs = layout.glyphRuns();
1300
1301 QCOMPARE(runs.size(), 1);
1302 QCOMPARE(runs.first().glyphIndexes().size(), 2);
1303}
1304
1305void tst_QTextScriptEngine::shapingDisabledDevanagari()
1306{
1307 QString s;
1308 s += QChar(0x0915); // KA
1309 s += QChar(0x094D); // VIRAMA
1310 s += QChar(0x0915); // KA
1311
1312
1313 QList<QGlyphRun> normalRuns;
1314 {
1315 QTextLayout layout(s);
1316 layout.beginLayout();
1317 layout.createLine();
1318 layout.endLayout();
1319
1320 normalRuns = layout.glyphRuns();
1321 }
1322
1323 QFont font;
1324 font.setStyleStrategy(QFont::PreferNoShaping);
1325
1326 QList<QGlyphRun> noShapingRuns;
1327 {
1328 QTextLayout layout(s);
1329 layout.setFont(font);
1330 layout.beginLayout();
1331 layout.createLine();
1332 layout.endLayout();
1333
1334 noShapingRuns = layout.glyphRuns();
1335 }
1336
1337 // Even though shaping is disabled, Devanagari requires it, so the flag should be ignored.
1338 QCOMPARE(normalRuns.size(), 1);
1339 QCOMPARE(noShapingRuns.size(), 1);
1340 QCOMPARE(noShapingRuns.first().glyphIndexes().size(), normalRuns.first().glyphIndexes().size());
1341}
1342
1343QTEST_MAIN(tst_QTextScriptEngine)
1344#include "tst_qtextscriptengine.moc"
1345

source code of qtbase/tests/auto/gui/text/qtextscriptengine/tst_qtextscriptengine.cpp