1 /*
2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
3 *
4 * This is part of HarfBuzz, an OpenType Layout engine library.
5 *
6 * Permission is hereby granted, without written agreement and without
7 * license or royalty fees, to use, copy, modify, and distribute this
8 * software and its documentation for any purpose, provided that the
9 * above copyright notice and the following two paragraphs appear in
10 * all copies of this software.
11 *
12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16 * DAMAGE.
17 *
18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23 */
24
25 #include <QtTest/QtTest>
26
27 #include <ft2build.h>
28 #include FT_FREETYPE_H
29 #include FT_TRUETYPE_TABLES_H
30
31 #include <harfbuzz-shaper.h>
32 #include <harfbuzz-global.h>
33 #include <harfbuzz-gpos.h>
34
35 static FT_Library freetype;
36
loadFace(const char * name)37 static FT_Face loadFace(const char *name)
38 {
39 FT_Face face;
40 char path[256];
41
42 strcpy(path, SRCDIR);
43 strcat(path, "/fonts/");
44 strcat(path, name);
45
46 if (FT_New_Face(freetype, path, /*index*/0, &face))
47 return 0;
48 return face;
49 }
50
getChar(const HB_UChar16 * string,hb_uint32 length,hb_uint32 & i)51 static HB_UChar32 getChar(const HB_UChar16 *string, hb_uint32 length, hb_uint32 &i)
52 {
53 HB_UChar32 ch;
54 if (HB_IsHighSurrogate(string[i])
55 && i < length - 1
56 && HB_IsLowSurrogate(string[i + 1])) {
57 ch = HB_SurrogateToUcs4(string[i], string[i + 1]);
58 ++i;
59 } else {
60 ch = string[i];
61 }
62 return ch;
63 }
64
hb_stringToGlyphs(HB_Font font,const HB_UChar16 * string,hb_uint32 length,HB_Glyph * glyphs,hb_uint32 * numGlyphs,HB_Bool)65 static HB_Bool hb_stringToGlyphs(HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool /*rightToLeft*/)
66 {
67 FT_Face face = (FT_Face)font->userData;
68 if (length > *numGlyphs)
69 return false;
70
71 int glyph_pos = 0;
72 for (hb_uint32 i = 0; i < length; ++i) {
73 glyphs[glyph_pos] = FT_Get_Char_Index(face, getChar(string, length, i));
74 ++glyph_pos;
75 }
76
77 *numGlyphs = glyph_pos;
78
79 return true;
80 }
81
hb_getAdvances(HB_Font,const HB_Glyph *,hb_uint32 numGlyphs,HB_Fixed * advances,int)82 static void hb_getAdvances(HB_Font /*font*/, const HB_Glyph * /*glyphs*/, hb_uint32 numGlyphs, HB_Fixed *advances, int /*flags*/)
83 {
84 for (hb_uint32 i = 0; i < numGlyphs; ++i)
85 advances[i] = 0; // ### not tested right now
86 }
87
hb_canRender(HB_Font font,const HB_UChar16 * string,hb_uint32 length)88 static HB_Bool hb_canRender(HB_Font font, const HB_UChar16 *string, hb_uint32 length)
89 {
90 FT_Face face = (FT_Face)font->userData;
91
92 for (hb_uint32 i = 0; i < length; ++i)
93 if (!FT_Get_Char_Index(face, getChar(string, length, i)))
94 return false;
95
96 return true;
97 }
98
hb_getSFntTable(void * font,HB_Tag tableTag,HB_Byte * buffer,HB_UInt * length)99 static HB_Error hb_getSFntTable(void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length)
100 {
101 FT_Face face = (FT_Face)font;
102 FT_ULong ftlen = *length;
103 FT_Error error = 0;
104
105 if (!FT_IS_SFNT(face))
106 return HB_Err_Invalid_Argument;
107
108 error = FT_Load_Sfnt_Table(face, tableTag, 0, buffer, &ftlen);
109 *length = ftlen;
110 return (HB_Error)error;
111 }
112
hb_getPointInOutline(HB_Font font,HB_Glyph glyph,int flags,hb_uint32 point,HB_Fixed * xpos,HB_Fixed * ypos,hb_uint32 * nPoints)113 HB_Error hb_getPointInOutline(HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints)
114 {
115 HB_Error error = HB_Err_Ok;
116 FT_Face face = (FT_Face)font->userData;
117
118 int load_flags = (flags & HB_ShaperFlag_UseDesignMetrics) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
119
120 if ((error = (HB_Error)FT_Load_Glyph(face, glyph, load_flags)))
121 return error;
122
123 if (face->glyph->format != ft_glyph_format_outline)
124 return (HB_Error)HB_Err_Invalid_SubTable;
125
126 *nPoints = face->glyph->outline.n_points;
127 if (!(*nPoints))
128 return HB_Err_Ok;
129
130 if (point > *nPoints)
131 return (HB_Error)HB_Err_Invalid_SubTable;
132
133 *xpos = face->glyph->outline.points[point].x;
134 *ypos = face->glyph->outline.points[point].y;
135
136 return HB_Err_Ok;
137 }
138
hb_getGlyphMetrics(HB_Font font,HB_Glyph glyph,HB_GlyphMetrics * metrics)139 void hb_getGlyphMetrics(HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics)
140 {
141 // ###
142 metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
143 }
144
hb_getFontMetric(HB_Font font,HB_FontMetric metric)145 HB_Fixed hb_getFontMetric(HB_Font font, HB_FontMetric metric)
146 {
147 return 0; // ####
148 }
149
150 const HB_FontClass hb_fontClass = {
151 hb_stringToGlyphs, hb_getAdvances, hb_canRender,
152 hb_getPointInOutline, hb_getGlyphMetrics, hb_getFontMetric
153 };
154
155
156 //TESTED_CLASS=
157 //TESTED_FILES= gui/text/qscriptengine.cpp
158
159 class tst_QScriptEngine : public QObject
160 {
161 Q_OBJECT
162
163 public:
164 tst_QScriptEngine();
165 virtual ~tst_QScriptEngine();
166
167
168 public slots:
169 void initTestCase();
170 void cleanupTestCase();
171 private slots:
172 void devanagari();
173 void bengali();
174 void gurmukhi();
175 // gujarati missing
176 void oriya();
177 void tamil();
178 void telugu();
179 void kannada();
180 void malayalam();
181 // sinhala missing
182
183 void khmer();
184 void linearB();
185 };
186
tst_QScriptEngine()187 tst_QScriptEngine::tst_QScriptEngine()
188 {
189 }
190
~tst_QScriptEngine()191 tst_QScriptEngine::~tst_QScriptEngine()
192 {
193 }
194
initTestCase()195 void tst_QScriptEngine::initTestCase()
196 {
197 FT_Init_FreeType(&freetype);
198 }
199
cleanupTestCase()200 void tst_QScriptEngine::cleanupTestCase()
201 {
202 FT_Done_FreeType(freetype);
203 }
204
205 struct ShapeTable {
206 unsigned short unicode[16];
207 unsigned short glyphs[16];
208 };
209
shaping(FT_Face face,const ShapeTable * s,HB_Script script)210 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script)
211 {
212 QString str = QString::fromUtf16( s->unicode );
213
214 HB_Face hbFace = HB_NewFace(face, hb_getSFntTable);
215
216 HB_FontRec hbFont;
217 hbFont.klass = &hb_fontClass;
218 hbFont.userData = face;
219 hbFont.x_ppem = face->size->metrics.x_ppem;
220 hbFont.y_ppem = face->size->metrics.y_ppem;
221 hbFont.x_scale = face->size->metrics.x_scale;
222 hbFont.y_scale = face->size->metrics.y_scale;
223
224 HB_ShaperItem shaper_item;
225 shaper_item.kerning_applied = false;
226 shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData());
227 shaper_item.stringLength = str.length();
228 shaper_item.item.script = script;
229 shaper_item.item.pos = 0;
230 shaper_item.item.length = shaper_item.stringLength;
231 shaper_item.item.bidiLevel = 0; // ###
232 shaper_item.shaperFlags = 0;
233 shaper_item.font = &hbFont;
234 shaper_item.face = hbFace;
235 shaper_item.num_glyphs = shaper_item.item.length;
236 shaper_item.glyphIndicesPresent = false;
237 shaper_item.initialGlyphCount = 0;
238
239 QVarLengthArray<HB_Glyph> hb_glyphs(shaper_item.num_glyphs);
240 QVarLengthArray<HB_GlyphAttributes> hb_attributes(shaper_item.num_glyphs);
241 QVarLengthArray<HB_Fixed> hb_advances(shaper_item.num_glyphs);
242 QVarLengthArray<HB_FixedPoint> hb_offsets(shaper_item.num_glyphs);
243 QVarLengthArray<unsigned short> hb_logClusters(shaper_item.num_glyphs);
244
245 while (1) {
246 hb_glyphs.resize(shaper_item.num_glyphs);
247 hb_attributes.resize(shaper_item.num_glyphs);
248 hb_advances.resize(shaper_item.num_glyphs);
249 hb_offsets.resize(shaper_item.num_glyphs);
250 hb_logClusters.resize(shaper_item.num_glyphs);
251
252 memset(hb_glyphs.data(), 0, hb_glyphs.size() * sizeof(HB_Glyph));
253 memset(hb_attributes.data(), 0, hb_attributes.size() * sizeof(HB_GlyphAttributes));
254 memset(hb_advances.data(), 0, hb_advances.size() * sizeof(HB_Fixed));
255 memset(hb_offsets.data(), 0, hb_offsets.size() * sizeof(HB_FixedPoint));
256
257 shaper_item.glyphs = hb_glyphs.data();
258 shaper_item.attributes = hb_attributes.data();
259 shaper_item.advances = hb_advances.data();
260 shaper_item.offsets = hb_offsets.data();
261 shaper_item.log_clusters = hb_logClusters.data();
262
263 if (HB_ShapeItem(&shaper_item))
264 break;
265
266 }
267
268 HB_FreeFace(hbFace);
269
270 hb_uint32 nglyphs = 0;
271 const unsigned short *g = s->glyphs;
272 while ( *g ) {
273 nglyphs++;
274 g++;
275 }
276
277 if( nglyphs != shaper_item.num_glyphs )
278 goto error;
279
280 for (hb_uint32 i = 0; i < nglyphs; ++i) {
281 if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i])
282 goto error;
283 }
284 return true;
285 error:
286 str = "";
287 const unsigned short *uc = s->unicode;
288 while (*uc) {
289 str += QString("%1 ").arg(*uc, 4, 16);
290 ++uc;
291 }
292 qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
293 face->family_name,
294 str.toLatin1().constData(),
295 shaper_item.num_glyphs, nglyphs);
296
297 str = "";
298 hb_uint32 i = 0;
299 while (i < shaper_item.num_glyphs) {
300 str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16);
301 ++i;
302 }
303 qDebug(" glyph result = %s", str.toLatin1().constData());
304 return false;
305 }
306
devanagari()307 void tst_QScriptEngine::devanagari()
308 {
309 {
310 FT_Face face = loadFace("raghu.ttf");
311 if (face) {
312 const ShapeTable shape_table [] = {
313 // Ka
314 { { 0x0915, 0x0 },
315 { 0x0080, 0x0 } },
316 // Ka Halant
317 { { 0x0915, 0x094d, 0x0 },
318 { 0x0080, 0x0051, 0x0 } },
319 // Ka Halant Ka
320 { { 0x0915, 0x094d, 0x0915, 0x0 },
321 { 0x00c8, 0x0080, 0x0 } },
322 // Ka MatraI
323 { { 0x0915, 0x093f, 0x0 },
324 { 0x01d1, 0x0080, 0x0 } },
325 // Ra Halant Ka
326 { { 0x0930, 0x094d, 0x0915, 0x0 },
327 { 0x0080, 0x005b, 0x0 } },
328 // Ra Halant Ka MatraI
329 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
330 { 0x01d1, 0x0080, 0x005b, 0x0 } },
331 // MatraI
332 { { 0x093f, 0x0 },
333 { 0x01d4, 0x029c, 0x0 } },
334 // Ka Nukta
335 { { 0x0915, 0x093c, 0x0 },
336 { 0x00a4, 0x0 } },
337 // Ka Halant Ra
338 { { 0x0915, 0x094d, 0x0930, 0x0 },
339 { 0x0110, 0x0 } },
340 // Ka Halant Ra Halant Ka
341 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
342 { 0x0158, 0x0080, 0x0 } },
343 { { 0x0930, 0x094d, 0x200d, 0x0 },
344 { 0x00e2, 0x0 } },
345 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
346 { 0x0158, 0x0 } },
347
348 { {0}, {0} }
349 };
350
351
352 const ShapeTable *s = shape_table;
353 while (s->unicode[0]) {
354 QVERIFY( shaping(face, s, HB_Script_Devanagari) );
355 ++s;
356 }
357
358 FT_Done_Face(face);
359 } else {
360 QSKIP("couln't find raghu.ttf", SkipAll);
361 }
362 }
363
364 {
365 FT_Face face = loadFace("mangal.ttf");
366 if (face) {
367 const ShapeTable shape_table [] = {
368 // Ka
369 { { 0x0915, 0x0 },
370 { 0x0080, 0x0 } },
371 // Ka Halant
372 { { 0x0915, 0x094d, 0x0 },
373 { 0x0080, 0x0051, 0x0 } },
374 // Ka Halant Ka
375 { { 0x0915, 0x094d, 0x0915, 0x0 },
376 { 0x00c8, 0x0080, 0x0 } },
377 // Ka MatraI
378 { { 0x0915, 0x093f, 0x0 },
379 { 0x01d1, 0x0080, 0x0 } },
380 // Ra Halant Ka
381 { { 0x0930, 0x094d, 0x0915, 0x0 },
382 { 0x0080, 0x005b, 0x0 } },
383 // Ra Halant Ka MatraI
384 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
385 { 0x01d1, 0x0080, 0x005b, 0x0 } },
386 // MatraI
387 { { 0x093f, 0x0 },
388 { 0x01d4, 0x029c, 0x0 } },
389 // Ka Nukta
390 { { 0x0915, 0x093c, 0x0 },
391 { 0x00a4, 0x0 } },
392 // Ka Halant Ra
393 { { 0x0915, 0x094d, 0x0930, 0x0 },
394 { 0x0110, 0x0 } },
395 // Ka Halant Ra Halant Ka
396 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
397 { 0x0158, 0x0080, 0x0 } },
398
399 { { 0x92b, 0x94d, 0x930, 0x0 },
400 { 0x125, 0x0 } },
401 { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
402 { 0x149, 0x0 } },
403 { {0}, {0} }
404 };
405
406 const ShapeTable *s = shape_table;
407 while (s->unicode[0]) {
408 QVERIFY( shaping(face, s, HB_Script_Devanagari) );
409 ++s;
410 }
411
412 FT_Done_Face(face);
413 } else {
414 QSKIP("couldn't find mangal.ttf", SkipAll);
415 }
416 }
417 }
418
bengali()419 void tst_QScriptEngine::bengali()
420 {
421 {
422 FT_Face face = loadFace("AkaashNormal.ttf");
423 if (face) {
424 const ShapeTable shape_table [] = {
425 // Ka
426 { { 0x0995, 0x0 },
427 { 0x0151, 0x0 } },
428 // Ka Halant
429 { { 0x0995, 0x09cd, 0x0 },
430 { 0x0151, 0x017d, 0x0 } },
431 // Ka Halant Ka
432 { { 0x0995, 0x09cd, 0x0995, 0x0 },
433 { 0x019b, 0x0 } },
434 // Ka MatraI
435 { { 0x0995, 0x09bf, 0x0 },
436 { 0x0173, 0x0151, 0x0 } },
437 // Ra Halant Ka
438 { { 0x09b0, 0x09cd, 0x0995, 0x0 },
439 { 0x0151, 0x0276, 0x0 } },
440 // Ra Halant Ka MatraI
441 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
442 { 0x0173, 0x0151, 0x0276, 0x0 } },
443 // Ka Nukta
444 { { 0x0995, 0x09bc, 0x0 },
445 { 0x0151, 0x0171, 0x0 } },
446 // Ka Halant Ra
447 { { 0x0995, 0x09cd, 0x09b0, 0x0 },
448 { 0x01f4, 0x0 } },
449 // Ka Halant Ra Halant Ka
450 { { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
451 { 0x025c, 0x0276, 0x0151, 0x0 } },
452 // Ya + Halant
453 { { 0x09af, 0x09cd, 0x0 },
454 { 0x016a, 0x017d, 0x0 } },
455 // Da Halant Ya -> Da Ya-Phala
456 { { 0x09a6, 0x09cd, 0x09af, 0x0 },
457 { 0x01e5, 0x0 } },
458 // A Halant Ya -> A Ya-phala
459 { { 0x0985, 0x09cd, 0x09af, 0x0 },
460 { 0x0145, 0x01cf, 0x0 } },
461 // Na Halant Ka
462 { { 0x09a8, 0x09cd, 0x0995, 0x0 },
463 { 0x026f, 0x0151, 0x0 } },
464 // Na Halant ZWNJ Ka
465 { { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
466 { 0x0164, 0x017d, 0x0151, 0x0 } },
467 // Na Halant ZWJ Ka
468 { { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
469 { 0x026f, 0x0151, 0x0 } },
470 // Ka Halant ZWNJ Ka
471 { { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
472 { 0x0151, 0x017d, 0x0151, 0x0 } },
473 // Ka Halant ZWJ Ka
474 { { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
475 { 0x025c, 0x0151, 0x0 } },
476 // Na Halant Ra
477 { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
478 { 0x0207, 0x0 } },
479 // Na Halant ZWNJ Ra
480 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
481 { 0x0164, 0x017d, 0x016b, 0x0 } },
482 // Na Halant ZWJ Ra
483 { { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
484 { 0x026f, 0x016b, 0x0 } },
485 // Na Halant Ba
486 { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
487 { 0x022f, 0x0 } },
488 // Na Halant ZWNJ Ba
489 { { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
490 { 0x0164, 0x017d, 0x0167, 0x0 } },
491 // Na Halant ZWJ Ba
492 { { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
493 { 0x026f, 0x0167, 0x0 } },
494 // Na Halant Dha
495 { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
496 { 0x01d3, 0x0 } },
497 // Na Halant ZWNJ Dha
498 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
499 { 0x0164, 0x017d, 0x0163, 0x0 } },
500 // Na Halant ZWJ Dha
501 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
502 { 0x026f, 0x0163, 0x0 } },
503 // Ra Halant Ka MatraAU
504 { { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
505 { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
506 // Ra Halant Ba Halant Ba
507 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
508 { 0x0232, 0x0276, 0x0 } },
509 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
510 { 0x151, 0x276, 0x172, 0x143, 0x0 } },
511 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
512 { 0x151, 0x276, 0x172, 0x144, 0x0 } },
513
514 { {0}, {0} }
515 };
516
517
518 const ShapeTable *s = shape_table;
519 while (s->unicode[0]) {
520 QVERIFY( shaping(face, s, HB_Script_Bengali) );
521 ++s;
522 }
523
524 FT_Done_Face(face);
525 } else {
526 QSKIP("couln't find AkaashNormal.ttf", SkipAll);
527 }
528 }
529 {
530 FT_Face face = loadFace("MuktiNarrow.ttf");
531 if (face) {
532 const ShapeTable shape_table [] = {
533 // Ka
534 { { 0x0995, 0x0 },
535 { 0x0073, 0x0 } },
536 // Ka Halant
537 { { 0x0995, 0x09cd, 0x0 },
538 { 0x00b9, 0x0 } },
539 // Ka Halant Ka
540 { { 0x0995, 0x09cd, 0x0995, 0x0 },
541 { 0x0109, 0x0 } },
542 // Ka MatraI
543 { { 0x0995, 0x09bf, 0x0 },
544 { 0x0095, 0x0073, 0x0 } },
545 // Ra Halant Ka
546 { { 0x09b0, 0x09cd, 0x0995, 0x0 },
547 { 0x0073, 0x00e1, 0x0 } },
548 // Ra Halant Ka MatraI
549 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
550 { 0x0095, 0x0073, 0x00e1, 0x0 } },
551 // MatraI
552 { { 0x09bf, 0x0 },
553 { 0x0095, 0x01c8, 0x0 } },
554 // Ka Nukta
555 { { 0x0995, 0x09bc, 0x0 },
556 { 0x0073, 0x0093, 0x0 } },
557 // Ka Halant Ra
558 { { 0x0995, 0x09cd, 0x09b0, 0x0 },
559 { 0x00e5, 0x0 } },
560 // Ka Halant Ra Halant Ka
561 { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
562 { 0x234, 0x24e, 0x73, 0x0 } },
563 // Ya + Halant
564 { { 0x09af, 0x09cd, 0x0 },
565 { 0x00d2, 0x0 } },
566 // Da Halant Ya -> Da Ya-Phala
567 { { 0x09a6, 0x09cd, 0x09af, 0x0 },
568 { 0x0084, 0x00e2, 0x0 } },
569 // A Halant Ya -> A Ya-phala
570 { { 0x0985, 0x09cd, 0x09af, 0x0 },
571 { 0x0067, 0x00e2, 0x0 } },
572 // Na Halant Ka
573 { { 0x09a8, 0x09cd, 0x0995, 0x0 },
574 { 0x0188, 0x0 } },
575 // Na Halant ZWNJ Ka
576 { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
577 { 0xcc, 0x73, 0x0 } },
578 // Na Halant ZWJ Ka
579 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
580 { 0x247, 0x73, 0x0 } },
581 // Ka Halant ZWNJ Ka
582 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
583 { 0x247, 0x73, 0x0 } },
584 // Ka Halant ZWJ Ka
585 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
586 { 0x247, 0x73, 0x0 } },
587 // Na Halant Ra
588 { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
589 { 0x00f8, 0x0 } },
590 // Na Halant ZWNJ Ra
591 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
592 { 0xcc, 0x8d, 0x0 } },
593 // Na Halant ZWJ Ra
594 { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
595 { 0x247, 0x8d, 0x0 } },
596 // Na Halant Ba
597 { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
598 { 0x0139, 0x0 } },
599 // Na Halant ZWNJ Ba
600 { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
601 { 0xcc, 0x89, 0x0 } },
602 // Na Halant ZWJ Ba
603 { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
604 { 0x247, 0x89, 0x0 } },
605 // Na Halant Dha
606 { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
607 { 0x0145, 0x0 } },
608 // Na Halant ZWNJ Dha
609 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
610 { 0xcc, 0x85, 0x0 } },
611 // Na Halant ZWJ Dha
612 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
613 { 0x247, 0x85, 0x0 } },
614 // Ra Halant Ka MatraAU
615 { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
616 { 0x232, 0x73, 0xe1, 0xa0, 0x0 } },
617 // Ra Halant Ba Halant Ba
618 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
619 { 0x013b, 0x00e1, 0x0 } },
620
621 { {0}, {0} }
622 };
623
624
625 const ShapeTable *s = shape_table;
626 while (s->unicode[0]) {
627 QVERIFY( shaping(face, s, HB_Script_Bengali) );
628 ++s;
629 }
630
631 FT_Done_Face(face);
632 } else {
633 QSKIP("couln't find MuktiNarrow.ttf", SkipAll);
634 }
635 }
636 {
637 FT_Face face = loadFace("LikhanNormal.ttf");
638 if (face) {
639 const ShapeTable shape_table [] = {
640 { { 0x09a8, 0x09cd, 0x09af, 0x0 },
641 { 0x0192, 0x0 } },
642 { { 0x09b8, 0x09cd, 0x09af, 0x0 },
643 { 0x01d6, 0x0 } },
644 { { 0x09b6, 0x09cd, 0x09af, 0x0 },
645 { 0x01bc, 0x0 } },
646 { { 0x09b7, 0x09cd, 0x09af, 0x0 },
647 { 0x01c6, 0x0 } },
648 { { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
649 { 0xd3, 0x12f, 0x0 } },
650
651 { {0}, {0} }
652 };
653
654
655 const ShapeTable *s = shape_table;
656 while (s->unicode[0]) {
657 QVERIFY( shaping(face, s, HB_Script_Bengali) );
658 ++s;
659 }
660
661 FT_Done_Face(face);
662 } else {
663 QSKIP("couln't find LikhanNormal.ttf", SkipAll);
664 }
665 }
666 }
667
gurmukhi()668 void tst_QScriptEngine::gurmukhi()
669 {
670 {
671 FT_Face face = loadFace("lohit.punjabi.1.1.ttf");
672 if (face) {
673 const ShapeTable shape_table [] = {
674 { { 0xA15, 0xA4D, 0xa39, 0x0 },
675 { 0x3b, 0x8b, 0x0 } },
676 { {0}, {0} }
677 };
678
679
680 const ShapeTable *s = shape_table;
681 while (s->unicode[0]) {
682 QVERIFY( shaping(face, s, HB_Script_Gurmukhi) );
683 ++s;
684 }
685
686 FT_Done_Face(face);
687 } else {
688 QSKIP("couln't find lohit.punjabi.1.1.ttf", SkipAll);
689 }
690 }
691 }
692
oriya()693 void tst_QScriptEngine::oriya()
694 {
695 {
696 FT_Face face = loadFace("utkalm.ttf");
697 if (face) {
698 const ShapeTable shape_table [] = {
699 { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
700 { 0x150, 0x125, 0x0 } },
701 { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
702 { 0x151, 0x120, 0x0 } },
703 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
704 { 0x152, 0x120, 0x0 } },
705 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
706 { 0x152, 0x120, 0x0 } },
707 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
708 { 0x176, 0x0 } },
709 { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
710 { 0x177, 0x0 } },
711 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
712 { 0x176, 0x124, 0x0 } },
713 { {0}, {0} }
714
715 };
716
717 const ShapeTable *s = shape_table;
718 while (s->unicode[0]) {
719 QVERIFY( shaping(face, s, HB_Script_Oriya) );
720 ++s;
721 }
722
723 FT_Done_Face(face);
724 } else {
725 QSKIP("couln't find utkalm.ttf", SkipAll);
726 }
727 }
728 }
729
730
tamil()731 void tst_QScriptEngine::tamil()
732 {
733 {
734 FT_Face face = loadFace("akruti1.ttf");
735 if (face) {
736 const ShapeTable shape_table [] = {
737 { { 0x0b95, 0x0bc2, 0x0 },
738 { 0x004e, 0x0 } },
739 { { 0x0bae, 0x0bc2, 0x0 },
740 { 0x009e, 0x0 } },
741 { { 0x0b9a, 0x0bc2, 0x0 },
742 { 0x0058, 0x0 } },
743 { { 0x0b99, 0x0bc2, 0x0 },
744 { 0x0053, 0x0 } },
745 { { 0x0bb0, 0x0bc2, 0x0 },
746 { 0x00a8, 0x0 } },
747 { { 0x0ba4, 0x0bc2, 0x0 },
748 { 0x008e, 0x0 } },
749 { { 0x0b9f, 0x0bc2, 0x0 },
750 { 0x0062, 0x0 } },
751 { { 0x0b95, 0x0bc6, 0x0 },
752 { 0x000a, 0x0031, 0x0 } },
753 { { 0x0b95, 0x0bca, 0x0 },
754 { 0x000a, 0x0031, 0x0007, 0x0 } },
755 { { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
756 { 0x000a, 0x0031, 0x007, 0x0 } },
757 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
758 { 0x0049, 0x0 } },
759 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
760 { 0x000a, 0x0049, 0x007, 0x0 } },
761 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
762 { 0x000a, 0x0049, 0x007, 0x0 } },
763 { { 0x0b9f, 0x0bbf, 0x0 },
764 { 0x005f, 0x0 } },
765 { { 0x0b9f, 0x0bc0, 0x0 },
766 { 0x0060, 0x0 } },
767 { { 0x0bb2, 0x0bc0, 0x0 },
768 { 0x00ab, 0x0 } },
769 { { 0x0bb2, 0x0bbf, 0x0 },
770 { 0x00aa, 0x0 } },
771 { { 0x0bb0, 0x0bcd, 0x0 },
772 { 0x00a4, 0x0 } },
773 { { 0x0bb0, 0x0bbf, 0x0 },
774 { 0x00a5, 0x0 } },
775 { { 0x0bb0, 0x0bc0, 0x0 },
776 { 0x00a6, 0x0 } },
777 { { 0x0b83, 0x0 },
778 { 0x0025, 0x0 } },
779 { { 0x0b83, 0x0b95, 0x0 },
780 { 0x0025, 0x0031, 0x0 } },
781
782 { {0}, {0} }
783 };
784
785
786 const ShapeTable *s = shape_table;
787 while (s->unicode[0]) {
788 QVERIFY( shaping(face, s, HB_Script_Tamil) );
789 ++s;
790 }
791
792 FT_Done_Face(face);
793 } else {
794 QSKIP("couln't find akruti1.ttf", SkipAll);
795 }
796 }
797 }
798
799
telugu()800 void tst_QScriptEngine::telugu()
801 {
802 {
803 FT_Face face = loadFace("Pothana2000.ttf");
804 if (face) {
805 const ShapeTable shape_table [] = {
806 { { 0xc15, 0xc4d, 0x0 },
807 { 0xbb, 0x0 } },
808 { { 0xc15, 0xc4d, 0xc37, 0x0 },
809 { 0x4b, 0x0 } },
810 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
811 { 0xe0, 0x0 } },
812 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
813 { 0x4b, 0x91, 0x0 } },
814 { { 0xc15, 0xc4d, 0xc30, 0x0 },
815 { 0x5a, 0xb2, 0x0 } },
816 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
817 { 0xbb, 0xb2, 0x0 } },
818 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
819 { 0x5a, 0xb2, 0x83, 0x0 } },
820 { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
821 { 0xe2, 0xb2, 0x0 } },
822 { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
823 { 0xe6, 0xb3, 0x83, 0x0 } },
824 { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
825 { 0xe6, 0xb3, 0x9f, 0x0 } },
826 { {0}, {0} }
827
828 };
829
830 const ShapeTable *s = shape_table;
831 while (s->unicode[0]) {
832 QVERIFY( shaping(face, s, HB_Script_Telugu) );
833 ++s;
834 }
835
836 FT_Done_Face(face);
837 } else {
838 QSKIP("couln't find Pothana2000.ttf", SkipAll);
839 }
840 }
841 }
842
843
kannada()844 void tst_QScriptEngine::kannada()
845 {
846 {
847 FT_Face face = loadFace("Sampige.ttf");
848 if (face) {
849 const ShapeTable shape_table [] = {
850 { { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
851 { 0x0049, 0x00ba, 0x0 } },
852 { { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
853 { 0x0049, 0x00b3, 0x0 } },
854 { { 0x0caf, 0x0cc2, 0x0 },
855 { 0x004f, 0x005d, 0x0 } },
856 { { 0x0ce0, 0x0 },
857 { 0x006a, 0x0 } },
858 { { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
859 { 0x006b, 0x006c, 0x006d, 0x0 } },
860 { { 0x0cb5, 0x0ccb, 0x0 },
861 { 0x015f, 0x0067, 0x0 } },
862 { { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
863 { 0x004e, 0x0082, 0x0 } },
864 { { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
865 { 0x0036, 0x0082, 0x0 } },
866 { { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
867 { 0x0036, 0x00c1, 0x0 } },
868 { { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
869 { 0x0050, 0x00a7, 0x0 } },
870
871 { {0}, {0} }
872 };
873
874
875 const ShapeTable *s = shape_table;
876 while (s->unicode[0]) {
877 QVERIFY( shaping(face, s, HB_Script_Kannada) );
878 ++s;
879 }
880
881 FT_Done_Face(face);
882 } else {
883 QSKIP("couln't find Sampige.ttf", SkipAll);
884 }
885 }
886 {
887 FT_Face face = loadFace("tunga.ttf");
888 if (face) {
889 const ShapeTable shape_table [] = {
890 { { 0x0cb7, 0x0cc6, 0x0 },
891 { 0x00b0, 0x006c, 0x0 } },
892 { { 0x0cb7, 0x0ccd, 0x0 },
893 { 0x0163, 0x0 } },
894
895 { {0}, {0} }
896 };
897
898
899 const ShapeTable *s = shape_table;
900 while (s->unicode[0]) {
901 QVERIFY( shaping(face, s, HB_Script_Kannada) );
902 ++s;
903 }
904
905 FT_Done_Face(face);
906 } else {
907 QSKIP("couln't find tunga.ttf", SkipAll);
908 }
909 }
910 }
911
912
913
malayalam()914 void tst_QScriptEngine::malayalam()
915 {
916 {
917 FT_Face face = loadFace("AkrutiMal2Normal.ttf");
918 if (face) {
919 const ShapeTable shape_table [] = {
920 { { 0x0d15, 0x0d46, 0x0 },
921 { 0x005e, 0x0034, 0x0 } },
922 { { 0x0d15, 0x0d47, 0x0 },
923 { 0x005f, 0x0034, 0x0 } },
924 { { 0x0d15, 0x0d4b, 0x0 },
925 { 0x005f, 0x0034, 0x0058, 0x0 } },
926 { { 0x0d15, 0x0d48, 0x0 },
927 { 0x0060, 0x0034, 0x0 } },
928 { { 0x0d15, 0x0d4a, 0x0 },
929 { 0x005e, 0x0034, 0x0058, 0x0 } },
930 { { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
931 { 0x009e, 0x0034, 0x0 } },
932 { { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
933 { 0x0034, 0x007a, 0x0 } },
934 { { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
935 { 0x0034, 0x00a2, 0x0 } },
936 { { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
937 { 0x0069, 0x0 } },
938 { { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
939 { 0x0074, 0x0 } },
940 { { 0x0d30, 0x0d4d, 0x0 },
941 { 0x009e, 0x0 } },
942 { { 0x0d30, 0x0d4d, 0x200c, 0x0 },
943 { 0x009e, 0x0 } },
944 { { 0x0d30, 0x0d4d, 0x200d, 0x0 },
945 { 0x009e, 0x0 } },
946
947
948 { {0}, {0} }
949 };
950
951
952 const ShapeTable *s = shape_table;
953 while (s->unicode[0]) {
954 QVERIFY( shaping(face, s, HB_Script_Malayalam) );
955 ++s;
956 }
957
958 FT_Done_Face(face);
959 } else {
960 QSKIP("couln't find AkrutiMal2Normal.ttf", SkipAll);
961 }
962 }
963 }
964
965
966
khmer()967 void tst_QScriptEngine::khmer()
968 {
969 {
970 FT_Face face = loadFace("KhmerOS.ttf");
971 if (face) {
972 const ShapeTable shape_table [] = {
973 { { 0x179a, 0x17cd, 0x0 },
974 { 0x24c, 0x27f, 0x0 } },
975 { { 0x179f, 0x17c5, 0x0 },
976 { 0x273, 0x203, 0x0 } },
977 { { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
978 { 0x275, 0x242, 0x182, 0x0 } },
979 { { 0x179a, 0x0 },
980 { 0x24c, 0x0 } },
981 { { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
982 { 0x274, 0x233, 0x197, 0x0 } },
983 { { 0x1798, 0x17b6, 0x0 },
984 { 0x1cb, 0x0 } },
985 { { 0x179a, 0x17b8, 0x0 },
986 { 0x24c, 0x26a, 0x0 } },
987 { { 0x1787, 0x17b6, 0x0 },
988 { 0x1ba, 0x0 } },
989 { { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
990 { 0x24a, 0x195, 0x26d, 0x0 } },
991 { {0}, {0} }
992 };
993
994
995 const ShapeTable *s = shape_table;
996 while (s->unicode[0]) {
997 QVERIFY( shaping(face, s, HB_Script_Khmer) );
998 ++s;
999 }
1000
1001 FT_Done_Face(face);
1002 } else {
1003 QSKIP("couln't find KhmerOS.ttf", SkipAll);
1004 }
1005 }
1006 }
1007
linearB()1008 void tst_QScriptEngine::linearB()
1009 {
1010 {
1011 FT_Face face = loadFace("PENUTURE.TTF");
1012 if (face) {
1013 const ShapeTable shape_table [] = {
1014 { { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 },
1015 { 0x5, 0x6, 0x7, 0 } },
1016 { {0}, {0} }
1017 };
1018
1019
1020 const ShapeTable *s = shape_table;
1021 while (s->unicode[0]) {
1022 QVERIFY( shaping(face, s, HB_Script_Common) );
1023 ++s;
1024 }
1025
1026 FT_Done_Face(face);
1027 } else {
1028 QSKIP("couln't find PENUTURE.TTF", SkipAll);
1029 }
1030 }
1031 }
1032
1033
1034 QTEST_MAIN(tst_QScriptEngine)
1035 #include "main.moc"
1036