2 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
4 * This is part of HarfBuzz, an OpenType Layout engine library.
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.
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
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.
25 #include <QtTest/QtTest>
28 #include FT_FREETYPE_H
29 #include FT_TRUETYPE_TABLES_H
31 #include <harfbuzz-shaper.h>
32 #include <harfbuzz-global.h>
33 #include <harfbuzz-gpos.h>
35 static FT_Library freetype
;
37 static FT_Face
loadFace(const char *name
)
43 strcat(path
, "/fonts/");
46 if (FT_New_Face(freetype
, path
, /*index*/0, &face
))
51 static HB_UChar32
getChar(const HB_UChar16
*string
, hb_uint32 length
, hb_uint32
&i
)
54 if (HB_IsHighSurrogate(string
[i
])
56 && HB_IsLowSurrogate(string
[i
+ 1])) {
57 ch
= HB_SurrogateToUcs4(string
[i
], string
[i
+ 1]);
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*/)
67 FT_Face face
= (FT_Face
)font
->userData
;
68 if (length
> *numGlyphs
)
72 for (hb_uint32 i
= 0; i
< length
; ++i
) {
73 glyphs
[glyph_pos
] = FT_Get_Char_Index(face
, getChar(string
, length
, i
));
77 *numGlyphs
= glyph_pos
;
82 static void hb_getAdvances(HB_Font
/*font*/, const HB_Glyph
* /*glyphs*/, hb_uint32 numGlyphs
, HB_Fixed
*advances
, int /*flags*/)
84 for (hb_uint32 i
= 0; i
< numGlyphs
; ++i
)
85 advances
[i
] = 0; // ### not tested right now
88 static HB_Bool
hb_canRender(HB_Font font
, const HB_UChar16
*string
, hb_uint32 length
)
90 FT_Face face
= (FT_Face
)font
->userData
;
92 for (hb_uint32 i
= 0; i
< length
; ++i
)
93 if (!FT_Get_Char_Index(face
, getChar(string
, length
, i
)))
99 static HB_Error
hb_getSFntTable(void *font
, HB_Tag tableTag
, HB_Byte
*buffer
, HB_UInt
*length
)
101 FT_Face face
= (FT_Face
)font
;
102 FT_ULong ftlen
= *length
;
105 if (!FT_IS_SFNT(face
))
106 return HB_Err_Invalid_Argument
;
108 error
= FT_Load_Sfnt_Table(face
, tableTag
, 0, buffer
, &ftlen
);
110 return (HB_Error
)error
;
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
)
115 HB_Error error
= HB_Err_Ok
;
116 FT_Face face
= (FT_Face
)font
->userData
;
118 int load_flags
= (flags
& HB_ShaperFlag_UseDesignMetrics
) ? FT_LOAD_NO_HINTING
: FT_LOAD_DEFAULT
;
120 if ((error
= (HB_Error
)FT_Load_Glyph(face
, glyph
, load_flags
)))
123 if (face
->glyph
->format
!= ft_glyph_format_outline
)
124 return (HB_Error
)HB_Err_Invalid_SubTable
;
126 *nPoints
= face
->glyph
->outline
.n_points
;
130 if (point
> *nPoints
)
131 return (HB_Error
)HB_Err_Invalid_SubTable
;
133 *xpos
= face
->glyph
->outline
.points
[point
].x
;
134 *ypos
= face
->glyph
->outline
.points
[point
].y
;
139 void hb_getGlyphMetrics(HB_Font
, HB_Glyph
, HB_GlyphMetrics
*metrics
)
142 metrics
->x
= metrics
->y
= metrics
->width
= metrics
->height
= metrics
->xOffset
= metrics
->yOffset
= 0;
145 HB_Fixed
hb_getFontMetric(HB_Font
, HB_FontMetric
)
150 const HB_FontClass hb_fontClass
= {
151 hb_stringToGlyphs
, hb_getAdvances
, hb_canRender
,
152 hb_getPointInOutline
, hb_getGlyphMetrics
, hb_getFontMetric
157 //TESTED_FILES= gui/text/qscriptengine.cpp
159 class tst_QScriptEngine
: public QObject
165 virtual ~tst_QScriptEngine();
170 void cleanupTestCase();
190 tst_QScriptEngine::tst_QScriptEngine()
194 tst_QScriptEngine::~tst_QScriptEngine()
198 void tst_QScriptEngine::initTestCase()
200 FT_Init_FreeType(&freetype
);
203 void tst_QScriptEngine::cleanupTestCase()
205 FT_Done_FreeType(freetype
);
211 Shaper(FT_Face face
, HB_Script script
, const QString
&str
);
214 HB_ShaperItem shaper_item
;
215 QVarLengthArray
<HB_Glyph
> hb_glyphs
;
216 QVarLengthArray
<HB_GlyphAttributes
> hb_attributes
;
217 QVarLengthArray
<HB_Fixed
> hb_advances
;
218 QVarLengthArray
<HB_FixedPoint
> hb_offsets
;
219 QVarLengthArray
<unsigned short> hb_logClusters
;
223 Shaper::Shaper(FT_Face face
, HB_Script script
, const QString
&str
)
225 HB_Face hbFace
= HB_NewFace(face
, hb_getSFntTable
);
227 hbFont
.klass
= &hb_fontClass
;
228 hbFont
.userData
= face
;
229 hbFont
.x_ppem
= face
->size
->metrics
.x_ppem
;
230 hbFont
.y_ppem
= face
->size
->metrics
.y_ppem
;
231 hbFont
.x_scale
= face
->size
->metrics
.x_scale
;
232 hbFont
.y_scale
= face
->size
->metrics
.y_scale
;
234 shaper_item
.kerning_applied
= false;
235 shaper_item
.string
= reinterpret_cast<const HB_UChar16
*>(str
.constData());
236 shaper_item
.stringLength
= str
.length();
237 shaper_item
.item
.script
= script
;
238 shaper_item
.item
.pos
= 0;
239 shaper_item
.item
.length
= shaper_item
.stringLength
;
240 shaper_item
.item
.bidiLevel
= 0; // ###
241 shaper_item
.shaperFlags
= 0;
242 shaper_item
.font
= &hbFont
;
243 shaper_item
.face
= hbFace
;
244 shaper_item
.num_glyphs
= shaper_item
.item
.length
;
245 shaper_item
.glyphIndicesPresent
= false;
246 shaper_item
.initialGlyphCount
= 0;
250 hb_glyphs
.resize(shaper_item
.num_glyphs
);
251 hb_attributes
.resize(shaper_item
.num_glyphs
);
252 hb_advances
.resize(shaper_item
.num_glyphs
);
253 hb_offsets
.resize(shaper_item
.num_glyphs
);
254 hb_logClusters
.resize(shaper_item
.num_glyphs
);
256 memset(hb_glyphs
.data(), 0, hb_glyphs
.size() * sizeof(HB_Glyph
));
257 memset(hb_attributes
.data(), 0, hb_attributes
.size() * sizeof(HB_GlyphAttributes
));
258 memset(hb_advances
.data(), 0, hb_advances
.size() * sizeof(HB_Fixed
));
259 memset(hb_offsets
.data(), 0, hb_offsets
.size() * sizeof(HB_FixedPoint
));
261 shaper_item
.glyphs
= hb_glyphs
.data();
262 shaper_item
.attributes
= hb_attributes
.data();
263 shaper_item
.advances
= hb_advances
.data();
264 shaper_item
.offsets
= hb_offsets
.data();
265 shaper_item
.log_clusters
= hb_logClusters
.data();
267 if (HB_ShapeItem(&shaper_item
))
275 static bool decomposedShaping(FT_Face face
, HB_Script script
, const QChar
&ch
)
277 QString uc
= QString().append(ch
);
278 Shaper
shaper(face
, script
, uc
);
280 uc
= uc
.normalized(QString::NormalizationForm_D
);
281 Shaper
decomposed(face
, script
, uc
);
283 if( shaper
.shaper_item
.num_glyphs
!= decomposed
.shaper_item
.num_glyphs
)
286 for (unsigned int i
= 0; i
< shaper
.shaper_item
.num_glyphs
; ++i
) {
287 if ((shaper
.shaper_item
.glyphs
[i
]&0xffffff) != (decomposed
.shaper_item
.glyphs
[i
]&0xffffff))
294 while (i
< uc
.length()) {
295 str
+= QString("%1 ").arg(uc
[i
].unicode(), 4, 16);
298 qDebug("%s: decomposedShaping of char %4x failed\n decomposedString: %s\n nglyphs=%d, decomposed nglyphs %d",
300 ch
.unicode(), str
.toLatin1().data(),
301 shaper
.shaper_item
.num_glyphs
,
302 decomposed
.shaper_item
.num_glyphs
);
306 while (i
< shaper
.shaper_item
.num_glyphs
) {
307 str
+= QString("%1 ").arg(shaper
.shaper_item
.glyphs
[i
], 4, 16);
310 qDebug(" composed glyph result = %s", str
.toLatin1().constData());
313 while (i
< decomposed
.shaper_item
.num_glyphs
) {
314 str
+= QString("%1 ").arg(decomposed
.shaper_item
.glyphs
[i
], 4, 16);
317 qDebug(" decomposed glyph result = %s", str
.toLatin1().constData());
322 unsigned short unicode
[16];
323 unsigned short glyphs
[16];
326 static bool shaping(FT_Face face
, const ShapeTable
*s
, HB_Script script
)
328 Shaper
shaper(face
, script
, QString::fromUtf16( s
->unicode
));
330 hb_uint32 nglyphs
= 0;
331 const unsigned short *g
= s
->glyphs
;
337 if( nglyphs
!= shaper
.shaper_item
.num_glyphs
)
340 for (hb_uint32 i
= 0; i
< nglyphs
; ++i
) {
341 if ((shaper
.shaper_item
.glyphs
[i
]&0xffffff) != s
->glyphs
[i
])
347 const unsigned short *uc
= s
->unicode
;
349 str
+= QString("%1 ").arg(*uc
, 4, 16);
352 qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d",
354 str
.toLatin1().constData(),
355 shaper
.shaper_item
.num_glyphs
, nglyphs
);
359 while (i
< shaper
.shaper_item
.num_glyphs
) {
360 str
+= QString("%1 ").arg(shaper
.shaper_item
.glyphs
[i
], 4, 16);
363 qDebug(" glyph result = %s", str
.toLatin1().constData());
368 void tst_QScriptEngine::greek()
370 FT_Face face
= loadFace("DejaVuSans.ttf");
372 for (int uc
= 0x1f00; uc
<= 0x1fff; ++uc
) {
375 if (str
.normalized(QString::NormalizationForm_D
).normalized(QString::NormalizationForm_C
) != str
) {
376 //qDebug() << "skipping" << hex << uc;
379 if (uc
== 0x1fc1 || uc
== 0x1fed)
381 QVERIFY( decomposedShaping(face
, HB_Script_Greek
, QChar(uc
)) );
385 QSKIP("couln't find DejaVu Sans", SkipAll
);
389 face
= loadFace("SBL_grk.ttf");
391 for (int uc
= 0x1f00; uc
<= 0x1fff; ++uc
) {
394 if (str
.normalized(QString::NormalizationForm_D
).normalized(QString::NormalizationForm_C
) != str
) {
395 //qDebug() << "skipping" << hex << uc;
398 if (uc
== 0x1fc1 || uc
== 0x1fed)
400 QVERIFY( decomposedShaping(face
, HB_Script_Greek
, QChar(uc
)) );
404 const ShapeTable shape_table
[] = {
405 { { 0x3b1, 0x300, 0x313, 0x0 },
406 { 0xb8, 0x3d3, 0x3c7, 0x0 } },
407 { { 0x3b1, 0x313, 0x300, 0x0 },
414 const ShapeTable
*s
= shape_table
;
415 while (s
->unicode
[0]) {
416 QVERIFY( shaping(face
, s
, HB_Script_Greek
) );
422 QSKIP("couln't find DejaVu Sans", SkipAll
);
427 void tst_QScriptEngine::devanagari()
430 FT_Face face
= loadFace("raghu.ttf");
432 const ShapeTable shape_table
[] = {
437 { { 0x0915, 0x094d, 0x0 },
438 { 0x0080, 0x0051, 0x0 } },
440 { { 0x0915, 0x094d, 0x0915, 0x0 },
441 { 0x00c8, 0x0080, 0x0 } },
443 { { 0x0915, 0x093f, 0x0 },
444 { 0x01d1, 0x0080, 0x0 } },
446 { { 0x0930, 0x094d, 0x0915, 0x0 },
447 { 0x0080, 0x005b, 0x0 } },
448 // Ra Halant Ka MatraI
449 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
450 { 0x01d1, 0x0080, 0x005b, 0x0 } },
453 { 0x01d4, 0x029c, 0x0 } },
455 { { 0x0915, 0x093c, 0x0 },
458 { { 0x0915, 0x094d, 0x0930, 0x0 },
460 // Ka Halant Ra Halant Ka
461 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
462 { 0x0158, 0x0080, 0x0 } },
463 { { 0x0930, 0x094d, 0x200d, 0x0 },
465 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x200d, 0x0 },
472 const ShapeTable
*s
= shape_table
;
473 while (s
->unicode
[0]) {
474 QVERIFY( shaping(face
, s
, HB_Script_Devanagari
) );
480 QSKIP("couln't find raghu.ttf", SkipAll
);
485 FT_Face face
= loadFace("mangal.ttf");
487 const ShapeTable shape_table
[] = {
492 { { 0x0915, 0x094d, 0x0 },
493 { 0x0080, 0x0051, 0x0 } },
495 { { 0x0915, 0x094d, 0x0915, 0x0 },
496 { 0x00c8, 0x0080, 0x0 } },
498 { { 0x0915, 0x093f, 0x0 },
499 { 0x01d1, 0x0080, 0x0 } },
501 { { 0x0930, 0x094d, 0x0915, 0x0 },
502 { 0x0080, 0x005b, 0x0 } },
503 // Ra Halant Ka MatraI
504 { { 0x0930, 0x094d, 0x0915, 0x093f, 0x0 },
505 { 0x01d1, 0x0080, 0x005b, 0x0 } },
508 { 0x01d4, 0x029c, 0x0 } },
510 { { 0x0915, 0x093c, 0x0 },
513 { { 0x0915, 0x094d, 0x0930, 0x0 },
515 // Ka Halant Ra Halant Ka
516 { { 0x0915, 0x094d, 0x0930, 0x094d, 0x0915, 0x0 },
517 { 0x0158, 0x0080, 0x0 } },
519 { { 0x92b, 0x94d, 0x930, 0x0 },
521 { { 0x92b, 0x93c, 0x94d, 0x930, 0x0 },
526 const ShapeTable
*s
= shape_table
;
527 while (s
->unicode
[0]) {
528 QVERIFY( shaping(face
, s
, HB_Script_Devanagari
) );
534 QSKIP("couldn't find mangal.ttf", SkipAll
);
539 void tst_QScriptEngine::bengali()
542 FT_Face face
= loadFace("AkaashNormal.ttf");
544 const ShapeTable shape_table
[] = {
549 { { 0x0995, 0x09cd, 0x0 },
550 { 0x0151, 0x017d, 0x0 } },
552 { { 0x0995, 0x09cd, 0x0995, 0x0 },
555 { { 0x0995, 0x09bf, 0x0 },
556 { 0x0173, 0x0151, 0x0 } },
558 { { 0x09b0, 0x09cd, 0x0995, 0x0 },
559 { 0x0151, 0x0276, 0x0 } },
560 // Ra Halant Ka MatraI
561 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
562 { 0x0173, 0x0151, 0x0276, 0x0 } },
564 { { 0x0995, 0x09bc, 0x0 },
565 { 0x0151, 0x0171, 0x0 } },
567 { { 0x0995, 0x09cd, 0x09b0, 0x0 },
569 // Ka Halant Ra Halant Ka
570 { { 0x0995, 0x09cd, 0x09b0, 0x09cd, 0x0995, 0x0 },
571 { 0x025c, 0x0276, 0x0151, 0x0 } },
573 { { 0x09af, 0x09cd, 0x0 },
574 { 0x016a, 0x017d, 0x0 } },
575 // Da Halant Ya -> Da Ya-Phala
576 { { 0x09a6, 0x09cd, 0x09af, 0x0 },
578 // A Halant Ya -> A Ya-phala
579 { { 0x0985, 0x09cd, 0x09af, 0x0 },
580 { 0x0145, 0x01cf, 0x0 } },
582 { { 0x09a8, 0x09cd, 0x0995, 0x0 },
583 { 0x026f, 0x0151, 0x0 } },
585 { { 0x09a8, 0x09cd, 0x200c, 0x0995, 0x0 },
586 { 0x0164, 0x017d, 0x0151, 0x0 } },
588 { { 0x09a8, 0x09cd, 0x200d, 0x0995, 0x0 },
589 { 0x026f, 0x0151, 0x0 } },
591 { { 0x0995, 0x09cd, 0x200c, 0x0995, 0x0 },
592 { 0x0151, 0x017d, 0x0151, 0x0 } },
594 { { 0x0995, 0x09cd, 0x200d, 0x0995, 0x0 },
595 { 0x025c, 0x0151, 0x0 } },
597 { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
600 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
601 { 0x0164, 0x017d, 0x016b, 0x0 } },
603 { { 0x09a8, 0x09cd, 0x200d, 0x09b0, 0x0 },
604 { 0x026f, 0x016b, 0x0 } },
606 { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
609 { { 0x09a8, 0x09cd, 0x200c, 0x09ac, 0x0 },
610 { 0x0164, 0x017d, 0x0167, 0x0 } },
612 { { 0x09a8, 0x09cd, 0x200d, 0x09ac, 0x0 },
613 { 0x026f, 0x0167, 0x0 } },
615 { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
617 // Na Halant ZWNJ Dha
618 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
619 { 0x0164, 0x017d, 0x0163, 0x0 } },
621 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
622 { 0x026f, 0x0163, 0x0 } },
623 // Ra Halant Ka MatraAU
624 { { 0x09b0, 0x09cd, 0x0995, 0x09cc, 0x0 },
625 { 0x0179, 0x0151, 0x0276, 0x017e, 0x0 } },
626 // Ra Halant Ba Halant Ba
627 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
628 { 0x0232, 0x0276, 0x0 } },
629 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x982, 0x0 },
630 { 0x151, 0x276, 0x172, 0x143, 0x0 } },
631 { { 0x9b0, 0x9cd, 0x995, 0x9be, 0x983, 0x0 },
632 { 0x151, 0x276, 0x172, 0x144, 0x0 } },
633 // test decomposed two parts matras
634 { { 0x995, 0x9c7, 0x9be, 0x0 },
635 { 0x179, 0x151, 0x172, 0x0 } },
636 { { 0x995, 0x9c7, 0x9d7, 0x0 },
637 { 0x179, 0x151, 0x17e, 0x0 } },
638 { { 0x9b0, 0x9cd, 0x9ad, 0x0 },
639 { 0x168, 0x276, 0x0 } },
640 { { 0x9f0, 0x9cd, 0x9ad, 0x0 },
641 { 0x168, 0x276, 0x0 } },
642 { { 0x9f1, 0x9cd, 0x9ad, 0x0 },
643 { 0x191, 0x17d, 0x168, 0x0 } },
649 const ShapeTable
*s
= shape_table
;
650 while (s
->unicode
[0]) {
651 QVERIFY( shaping(face
, s
, HB_Script_Bengali
) );
657 QSKIP("couln't find AkaashNormal.ttf", SkipAll
);
661 FT_Face face
= loadFace("MuktiNarrow.ttf");
663 const ShapeTable shape_table
[] = {
668 { { 0x0995, 0x09cd, 0x0 },
671 { { 0x0995, 0x09cd, 0x0995, 0x0 },
674 { { 0x0995, 0x09bf, 0x0 },
675 { 0x0095, 0x0073, 0x0 } },
677 { { 0x09b0, 0x09cd, 0x0995, 0x0 },
678 { 0x0073, 0x00e1, 0x0 } },
679 // Ra Halant Ka MatraI
680 { { 0x09b0, 0x09cd, 0x0995, 0x09bf, 0x0 },
681 { 0x0095, 0x0073, 0x00e1, 0x0 } },
684 { 0x0095, 0x01c8, 0x0 } },
686 { { 0x0995, 0x09bc, 0x0 },
687 { 0x0073, 0x0093, 0x0 } },
689 { { 0x0995, 0x09cd, 0x09b0, 0x0 },
691 // Ka Halant Ra Halant Ka
692 { { 0x995, 0x9cd, 0x9b0, 0x9cd, 0x995, 0x0 },
693 { 0x234, 0x24e, 0x73, 0x0 } },
695 { { 0x09af, 0x09cd, 0x0 },
697 // Da Halant Ya -> Da Ya-Phala
698 { { 0x09a6, 0x09cd, 0x09af, 0x0 },
699 { 0x0084, 0x00e2, 0x0 } },
700 // A Halant Ya -> A Ya-phala
701 { { 0x0985, 0x09cd, 0x09af, 0x0 },
702 { 0x0067, 0x00e2, 0x0 } },
704 { { 0x09a8, 0x09cd, 0x0995, 0x0 },
707 { { 0x9a8, 0x9cd, 0x200c, 0x995, 0x0 },
708 { 0xcc, 0x73, 0x0 } },
710 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
711 { 0x247, 0x73, 0x0 } },
713 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
714 { 0x247, 0x73, 0x0 } },
716 { { 0x9a8, 0x9cd, 0x200d, 0x995, 0x0 },
717 { 0x247, 0x73, 0x0 } },
719 { { 0x09a8, 0x09cd, 0x09b0, 0x0 },
722 { { 0x09a8, 0x09cd, 0x200c, 0x09b0, 0x0 },
723 { 0xcc, 0x8d, 0x0 } },
725 { { 0x9a8, 0x9cd, 0x200d, 0x9b0, 0x0 },
726 { 0x247, 0x8d, 0x0 } },
728 { { 0x09a8, 0x09cd, 0x09ac, 0x0 },
731 { { 0x9a8, 0x9cd, 0x200c, 0x9ac, 0x0 },
732 { 0xcc, 0x89, 0x0 } },
734 { { 0x9a8, 0x9cd, 0x200d, 0x9ac, 0x0 },
735 { 0x247, 0x89, 0x0 } },
737 { { 0x09a8, 0x09cd, 0x09a7, 0x0 },
739 // Na Halant ZWNJ Dha
740 { { 0x09a8, 0x09cd, 0x200c, 0x09a7, 0x0 },
741 { 0xcc, 0x85, 0x0 } },
743 { { 0x09a8, 0x09cd, 0x200d, 0x09a7, 0x0 },
744 { 0x247, 0x85, 0x0 } },
745 // Ra Halant Ka MatraAU
746 { { 0x9b0, 0x9cd, 0x995, 0x9cc, 0x0 },
747 { 0x232, 0x73, 0xe1, 0xa0, 0x0 } },
748 // Ra Halant Ba Halant Ba
749 { { 0x09b0, 0x09cd, 0x09ac, 0x09cd, 0x09ac, 0x0 },
750 { 0x013b, 0x00e1, 0x0 } },
756 const ShapeTable
*s
= shape_table
;
757 while (s
->unicode
[0]) {
758 QVERIFY( shaping(face
, s
, HB_Script_Bengali
) );
764 QSKIP("couln't find MuktiNarrow.ttf", SkipAll
);
768 FT_Face face
= loadFace("LikhanNormal.ttf");
770 const ShapeTable shape_table
[] = {
771 { { 0x09a8, 0x09cd, 0x09af, 0x0 },
773 { { 0x09b8, 0x09cd, 0x09af, 0x0 },
775 { { 0x09b6, 0x09cd, 0x09af, 0x0 },
777 { { 0x09b7, 0x09cd, 0x09af, 0x0 },
779 { { 0x09b0, 0x09cd, 0x09a8, 0x09cd, 0x200d, 0x0 },
780 { 0x10b, 0x167, 0x0 } },
781 { { 0x9b0, 0x9cd, 0x9ad, 0x0 },
782 { 0xa1, 0x167, 0x0 } },
783 { { 0x9f0, 0x9cd, 0x9ad, 0x0 },
784 { 0xa1, 0x167, 0x0 } },
785 { { 0x9f1, 0x9cd, 0x9ad, 0x0 },
786 { 0x11c, 0xa1, 0x0 } },
792 const ShapeTable
*s
= shape_table
;
793 while (s
->unicode
[0]) {
794 QVERIFY( shaping(face
, s
, HB_Script_Bengali
) );
800 QSKIP("couln't find LikhanNormal.ttf", SkipAll
);
805 void tst_QScriptEngine::gurmukhi()
808 FT_Face face
= loadFace("lohit_pa.ttf");
810 const ShapeTable shape_table
[] = {
811 { { 0xA15, 0xA4D, 0xa39, 0x0 },
812 { 0x3b, 0x8b, 0x0 } },
817 const ShapeTable
*s
= shape_table
;
818 while (s
->unicode
[0]) {
819 QVERIFY( shaping(face
, s
, HB_Script_Gurmukhi
) );
825 QSKIP("couln't find lohit.punjabi.1.1.ttf", SkipAll
);
830 void tst_QScriptEngine::oriya()
833 FT_Face face
= loadFace("utkalm.ttf");
835 const ShapeTable shape_table
[] = {
836 { { 0xb15, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
837 { 0x150, 0x125, 0x0 } },
838 { { 0xb24, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
839 { 0x151, 0x120, 0x0 } },
840 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
841 { 0x152, 0x120, 0x0 } },
842 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb2c, 0x0 },
843 { 0x152, 0x120, 0x0 } },
844 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
846 { { 0xb38, 0xb4d, 0xb24, 0xb4d, 0xb30, 0x0 },
848 { { 0xb28, 0xb4d, 0xb24, 0xb4d, 0xb30, 0xb4d, 0xb2f, 0x0 },
849 { 0x176, 0x124, 0x0 } },
854 const ShapeTable
*s
= shape_table
;
855 while (s
->unicode
[0]) {
856 QVERIFY( shaping(face
, s
, HB_Script_Oriya
) );
862 QSKIP("couln't find utkalm.ttf", SkipAll
);
868 void tst_QScriptEngine::tamil()
871 FT_Face face
= loadFace("akruti1.ttf");
873 const ShapeTable shape_table
[] = {
874 { { 0x0b95, 0x0bc2, 0x0 },
876 { { 0x0bae, 0x0bc2, 0x0 },
878 { { 0x0b9a, 0x0bc2, 0x0 },
880 { { 0x0b99, 0x0bc2, 0x0 },
882 { { 0x0bb0, 0x0bc2, 0x0 },
884 { { 0x0ba4, 0x0bc2, 0x0 },
886 { { 0x0b9f, 0x0bc2, 0x0 },
888 { { 0x0b95, 0x0bc6, 0x0 },
889 { 0x000a, 0x0031, 0x0 } },
890 { { 0x0b95, 0x0bca, 0x0 },
891 { 0x000a, 0x0031, 0x0007, 0x0 } },
892 { { 0x0b95, 0x0bc6, 0x0bbe, 0x0 },
893 { 0x000a, 0x0031, 0x007, 0x0 } },
894 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0 },
896 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bca, 0x0 },
897 { 0x000a, 0x0049, 0x007, 0x0 } },
898 { { 0x0b95, 0x0bcd, 0x0bb7, 0x0bc6, 0x0bbe, 0x0 },
899 { 0x000a, 0x0049, 0x007, 0x0 } },
900 { { 0x0b9f, 0x0bbf, 0x0 },
902 { { 0x0b9f, 0x0bc0, 0x0 },
904 { { 0x0bb2, 0x0bc0, 0x0 },
906 { { 0x0bb2, 0x0bbf, 0x0 },
908 { { 0x0bb0, 0x0bcd, 0x0 },
910 { { 0x0bb0, 0x0bbf, 0x0 },
912 { { 0x0bb0, 0x0bc0, 0x0 },
916 { { 0x0b83, 0x0b95, 0x0 },
917 { 0x0025, 0x0031, 0x0 } },
923 const ShapeTable
*s
= shape_table
;
924 while (s
->unicode
[0]) {
925 QVERIFY( shaping(face
, s
, HB_Script_Tamil
) );
931 QSKIP("couln't find akruti1.ttf", SkipAll
);
937 void tst_QScriptEngine::telugu()
940 FT_Face face
= loadFace("Pothana2000.ttf");
942 const ShapeTable shape_table
[] = {
943 { { 0xc15, 0xc4d, 0x0 },
945 { { 0xc15, 0xc4d, 0xc37, 0x0 },
947 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0x0 },
949 { { 0xc15, 0xc4d, 0xc37, 0xc4d, 0xc23, 0x0 },
950 { 0x4b, 0x91, 0x0 } },
951 { { 0xc15, 0xc4d, 0xc30, 0x0 },
952 { 0x5a, 0xb2, 0x0 } },
953 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0x0 },
954 { 0xbb, 0xb2, 0x0 } },
955 { { 0xc15, 0xc4d, 0xc30, 0xc4d, 0xc15, 0x0 },
956 { 0x5a, 0xb2, 0x83, 0x0 } },
957 { { 0xc15, 0xc4d, 0xc30, 0xc3f, 0x0 },
958 { 0xe2, 0xb2, 0x0 } },
959 { { 0xc15, 0xc4d, 0xc15, 0xc48, 0x0 },
960 { 0xe6, 0xb3, 0x83, 0x0 } },
961 { { 0xc15, 0xc4d, 0xc30, 0xc48, 0x0 },
962 { 0xe6, 0xb3, 0x9f, 0x0 } },
963 { { 0xc15, 0xc46, 0xc56, 0x0 },
964 { 0xe6, 0xb3, 0x0 } },
968 const ShapeTable
*s
= shape_table
;
969 while (s
->unicode
[0]) {
970 QVERIFY( shaping(face
, s
, HB_Script_Telugu
) );
976 QSKIP("couln't find Pothana2000.ttf", SkipAll
);
982 void tst_QScriptEngine::kannada()
985 FT_Face face
= loadFace("Sampige.ttf");
987 const ShapeTable shape_table
[] = {
988 { { 0x0ca8, 0x0ccd, 0x0ca8, 0x0 },
989 { 0x0049, 0x00ba, 0x0 } },
990 { { 0x0ca8, 0x0ccd, 0x0ca1, 0x0 },
991 { 0x0049, 0x00b3, 0x0 } },
992 { { 0x0caf, 0x0cc2, 0x0 },
993 { 0x004f, 0x005d, 0x0 } },
996 { { 0x0ce6, 0x0ce7, 0x0ce8, 0x0 },
997 { 0x006b, 0x006c, 0x006d, 0x0 } },
998 { { 0x0cb5, 0x0ccb, 0x0 },
999 { 0x015f, 0x0067, 0x0 } },
1000 { { 0x0cb0, 0x0ccd, 0x0cae, 0x0 },
1001 { 0x004e, 0x0082, 0x0 } },
1002 { { 0x0cb0, 0x0ccd, 0x0c95, 0x0 },
1003 { 0x0036, 0x0082, 0x0 } },
1004 { { 0x0c95, 0x0ccd, 0x0cb0, 0x0 },
1005 { 0x0036, 0x00c1, 0x0 } },
1006 { { 0x0cb0, 0x0ccd, 0x200d, 0x0c95, 0x0 },
1007 { 0x0050, 0x00a7, 0x0 } },
1012 const ShapeTable
*s
= shape_table
;
1013 while (s
->unicode
[0]) {
1014 QVERIFY( shaping(face
, s
, HB_Script_Kannada
) );
1020 QSKIP("couln't find Sampige.ttf", SkipAll
);
1024 FT_Face face
= loadFace("tunga.ttf");
1026 const ShapeTable shape_table
[] = {
1027 { { 0x0cb7, 0x0cc6, 0x0 },
1028 { 0x00b0, 0x006c, 0x0 } },
1029 { { 0x0cb7, 0x0ccd, 0x0 },
1031 { { 0xc95, 0xcbf, 0xcd5, 0x0 },
1032 { 0x114, 0x73, 0x0 } },
1033 { { 0xc95, 0xcc6, 0xcd5, 0x0 },
1034 { 0x90, 0x6c, 0x73, 0x0 } },
1035 { { 0xc95, 0xcc6, 0xcd6, 0x0 },
1036 { 0x90, 0x6c, 0x74, 0x0 } },
1037 { { 0xc95, 0xcc6, 0xcc2, 0x0 },
1038 { 0x90, 0x6c, 0x69, 0x0 } },
1039 { { 0xc95, 0xcca, 0xcd5, 0x0 },
1040 { 0x90, 0x6c, 0x69, 0x73, 0x0 } },
1047 const ShapeTable
*s
= shape_table
;
1048 while (s
->unicode
[0]) {
1049 QVERIFY( shaping(face
, s
, HB_Script_Kannada
) );
1055 QSKIP("couln't find tunga.ttf", SkipAll
);
1062 void tst_QScriptEngine::malayalam()
1065 FT_Face face
= loadFace("AkrutiMal2Normal.ttf");
1067 const ShapeTable shape_table
[] = {
1068 { { 0x0d15, 0x0d46, 0x0 },
1069 { 0x005e, 0x0034, 0x0 } },
1070 { { 0x0d15, 0x0d47, 0x0 },
1071 { 0x005f, 0x0034, 0x0 } },
1072 { { 0x0d15, 0x0d4b, 0x0 },
1073 { 0x005f, 0x0034, 0x0058, 0x0 } },
1074 { { 0x0d15, 0x0d48, 0x0 },
1075 { 0x0060, 0x0034, 0x0 } },
1076 { { 0x0d15, 0x0d4a, 0x0 },
1077 { 0x005e, 0x0034, 0x0058, 0x0 } },
1078 { { 0x0d30, 0x0d4d, 0x0d15, 0x0 },
1079 { 0x009e, 0x0034, 0x0 } },
1080 { { 0x0d15, 0x0d4d, 0x0d35, 0x0 },
1081 { 0x0034, 0x007a, 0x0 } },
1082 { { 0x0d15, 0x0d4d, 0x0d2f, 0x0 },
1083 { 0x0034, 0x00a2, 0x0 } },
1084 { { 0x0d1f, 0x0d4d, 0x0d1f, 0x0 },
1086 { { 0x0d26, 0x0d4d, 0x0d26, 0x0 },
1088 { { 0x0d30, 0x0d4d, 0x0 },
1090 { { 0x0d30, 0x0d4d, 0x200c, 0x0 },
1092 { { 0x0d30, 0x0d4d, 0x200d, 0x0 },
1094 { { 0xd15, 0xd46, 0xd3e, 0x0 },
1095 { 0x5e, 0x34, 0x58, 0x0 } },
1096 { { 0xd15, 0xd47, 0xd3e, 0x0 },
1097 { 0x5f, 0x34, 0x58, 0x0 } },
1098 { { 0xd15, 0xd46, 0xd57, 0x0 },
1099 { 0x5e, 0x34, 0x65, 0x0 } },
1100 { { 0xd15, 0xd57, 0x0 },
1101 { 0x34, 0x65, 0x0 } },
1102 { { 0xd1f, 0xd4d, 0xd1f, 0xd41, 0xd4d, 0x0 },
1103 { 0x69, 0x5b, 0x64, 0x0 } },
1109 const ShapeTable
*s
= shape_table
;
1110 while (s
->unicode
[0]) {
1111 QVERIFY( shaping(face
, s
, HB_Script_Malayalam
) );
1117 QSKIP("couln't find AkrutiMal2Normal.ttf", SkipAll
);
1122 FT_Face face
= loadFace("Rachana.ttf");
1124 const ShapeTable shape_table
[] = {
1125 { { 0xd37, 0xd4d, 0xd1f, 0xd4d, 0xd30, 0xd40, 0x0 },
1126 { 0x385, 0xa3, 0x0 } },
1127 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
1129 { { 0xd33, 0xd4d, 0xd33, 0x0 },
1131 { { 0xd2f, 0xd4d, 0xd15, 0xd4d, 0xd15, 0xd41, 0x0 },
1133 { { 0xd30, 0xd4d, 0x200d, 0xd35, 0xd4d, 0xd35, 0x0 },
1134 { 0xf3, 0x350, 0x0 } },
1140 const ShapeTable
*s
= shape_table
;
1141 while (s
->unicode
[0]) {
1142 QVERIFY( shaping(face
, s
, HB_Script_Malayalam
) );
1148 QSKIP("couln't find Rachana.ttf", SkipAll
);
1154 void tst_QScriptEngine::sinhala()
1157 FT_Face face
= loadFace("FM-MalithiUW46.ttf");
1159 const ShapeTable shape_table
[] = {
1160 { { 0xd9a, 0xdd9, 0xdcf, 0x0 },
1161 { 0x4a, 0x61, 0x42, 0x0 } },
1162 { { 0xd9a, 0xdd9, 0xddf, 0x0 },
1163 { 0x4a, 0x61, 0x50, 0x0 } },
1164 { { 0xd9a, 0xdd9, 0xdca, 0x0 },
1165 { 0x4a, 0x62, 0x0 } },
1166 { { 0xd9a, 0xddc, 0xdca, 0x0 },
1167 { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
1168 { { 0xd9a, 0xdda, 0x0 },
1169 { 0x4a, 0x62, 0x0 } },
1170 { { 0xd9a, 0xddd, 0x0 },
1171 { 0x4a, 0x61, 0x42, 0x41, 0x0 } },
1175 const ShapeTable
*s
= shape_table
;
1176 while (s
->unicode
[0]) {
1177 QVERIFY( shaping(face
, s
, HB_Script_Sinhala
) );
1183 QSKIP("couln't find FM-MalithiUW46.ttf", SkipAll
);
1189 void tst_QScriptEngine::khmer()
1192 FT_Face face
= loadFace("KhmerOS.ttf");
1194 const ShapeTable shape_table
[] = {
1195 { { 0x179a, 0x17cd, 0x0 },
1196 { 0x24c, 0x27f, 0x0 } },
1197 { { 0x179f, 0x17c5, 0x0 },
1198 { 0x273, 0x203, 0x0 } },
1199 { { 0x1790, 0x17d2, 0x1784, 0x17c3, 0x0 },
1200 { 0x275, 0x242, 0x182, 0x0 } },
1203 { { 0x1781, 0x17d2, 0x1798, 0x17c2, 0x0 },
1204 { 0x274, 0x233, 0x197, 0x0 } },
1205 { { 0x1798, 0x17b6, 0x0 },
1207 { { 0x179a, 0x17b8, 0x0 },
1208 { 0x24c, 0x26a, 0x0 } },
1209 { { 0x1787, 0x17b6, 0x0 },
1211 { { 0x1798, 0x17d2, 0x1796, 0x17bb, 0x0 },
1212 { 0x24a, 0x195, 0x26d, 0x0 } },
1217 const ShapeTable
*s
= shape_table
;
1218 while (s
->unicode
[0]) {
1219 QVERIFY( shaping(face
, s
, HB_Script_Khmer
) );
1225 QSKIP("couln't find KhmerOS.ttf", SkipAll
);
1230 void tst_QScriptEngine::nko()
1233 FT_Face face
= loadFace("DejaVuSans.ttf");
1235 const ShapeTable shape_table
[] = {
1238 { { 0x7ca, 0x7ca, 0x0 },
1239 { 0x14db, 0x14d9, 0x0 } },
1240 { { 0x7ca, 0x7fa, 0x7ca, 0x0 },
1241 { 0x14db, 0x5ec, 0x14d9, 0x0 } },
1242 { { 0x7ca, 0x7f3, 0x7ca, 0x0 },
1243 { 0x14db, 0x5e7, 0x14d9, 0x0 } },
1244 { { 0x7ca, 0x7f3, 0x7fa, 0x7ca, 0x0 },
1245 { 0x14db, 0x5e7, 0x5ec, 0x14d9, 0x0 } },
1250 const ShapeTable
*s
= shape_table
;
1251 while (s
->unicode
[0]) {
1252 QVERIFY( shaping(face
, s
, HB_Script_Nko
) );
1258 QSKIP("couln't find DejaVuSans.ttf", SkipAll
);
1264 void tst_QScriptEngine::linearB()
1267 FT_Face face
= loadFace("penuture.ttf");
1269 const ShapeTable shape_table
[] = {
1270 { { 0xd800, 0xdc01, 0xd800, 0xdc02, 0xd800, 0xdc03, 0 },
1271 { 0x5, 0x6, 0x7, 0 } },
1276 const ShapeTable
*s
= shape_table
;
1277 while (s
->unicode
[0]) {
1278 QVERIFY( shaping(face
, s
, HB_Script_Common
) );
1284 QSKIP("couln't find PENUTURE.TTF", SkipAll
);
1290 QTEST_MAIN(tst_QScriptEngine
)