201 void tst_QScriptEngine::cleanupTestCase() |
203 void tst_QScriptEngine::cleanupTestCase() |
202 { |
204 { |
203 FT_Done_FreeType(freetype); |
205 FT_Done_FreeType(freetype); |
204 } |
206 } |
205 |
207 |
206 struct ShapeTable { |
208 class Shaper |
207 unsigned short unicode[16]; |
209 { |
208 unsigned short glyphs[16]; |
210 public: |
|
211 Shaper(FT_Face face, HB_Script script, const QString &str); |
|
212 |
|
213 HB_FontRec hbFont; |
|
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; |
|
220 |
209 }; |
221 }; |
210 |
222 |
211 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) |
223 Shaper::Shaper(FT_Face face, HB_Script script, const QString &str) |
212 { |
224 { |
213 QString str = QString::fromUtf16( s->unicode ); |
|
214 |
|
215 HB_Face hbFace = HB_NewFace(face, hb_getSFntTable); |
225 HB_Face hbFace = HB_NewFace(face, hb_getSFntTable); |
216 |
226 |
217 HB_FontRec hbFont; |
|
218 hbFont.klass = &hb_fontClass; |
227 hbFont.klass = &hb_fontClass; |
219 hbFont.userData = face; |
228 hbFont.userData = face; |
220 hbFont.x_ppem = face->size->metrics.x_ppem; |
229 hbFont.x_ppem = face->size->metrics.x_ppem; |
221 hbFont.y_ppem = face->size->metrics.y_ppem; |
230 hbFont.y_ppem = face->size->metrics.y_ppem; |
222 hbFont.x_scale = face->size->metrics.x_scale; |
231 hbFont.x_scale = face->size->metrics.x_scale; |
223 hbFont.y_scale = face->size->metrics.y_scale; |
232 hbFont.y_scale = face->size->metrics.y_scale; |
224 |
233 |
225 HB_ShaperItem shaper_item; |
|
226 shaper_item.kerning_applied = false; |
234 shaper_item.kerning_applied = false; |
227 shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData()); |
235 shaper_item.string = reinterpret_cast<const HB_UChar16 *>(str.constData()); |
228 shaper_item.stringLength = str.length(); |
236 shaper_item.stringLength = str.length(); |
229 shaper_item.item.script = script; |
237 shaper_item.item.script = script; |
230 shaper_item.item.pos = 0; |
238 shaper_item.item.pos = 0; |
261 shaper_item.offsets = hb_offsets.data(); |
264 shaper_item.offsets = hb_offsets.data(); |
262 shaper_item.log_clusters = hb_logClusters.data(); |
265 shaper_item.log_clusters = hb_logClusters.data(); |
263 |
266 |
264 if (HB_ShapeItem(&shaper_item)) |
267 if (HB_ShapeItem(&shaper_item)) |
265 break; |
268 break; |
266 |
|
267 } |
269 } |
268 |
270 |
269 HB_FreeFace(hbFace); |
271 HB_FreeFace(hbFace); |
|
272 } |
|
273 |
|
274 |
|
275 static bool decomposedShaping(FT_Face face, HB_Script script, const QChar &ch) |
|
276 { |
|
277 QString uc = QString().append(ch); |
|
278 Shaper shaper(face, script, uc); |
|
279 |
|
280 uc = uc.normalized(QString::NormalizationForm_D); |
|
281 Shaper decomposed(face, script, uc); |
|
282 |
|
283 if( shaper.shaper_item.num_glyphs != decomposed.shaper_item.num_glyphs ) |
|
284 goto error; |
|
285 |
|
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)) |
|
288 goto error; |
|
289 } |
|
290 return true; |
|
291 error: |
|
292 QString str = ""; |
|
293 int i = 0; |
|
294 while (i < uc.length()) { |
|
295 str += QString("%1 ").arg(uc[i].unicode(), 4, 16); |
|
296 ++i; |
|
297 } |
|
298 qDebug("%s: decomposedShaping of char %4x failed\n decomposedString: %s\n nglyphs=%d, decomposed nglyphs %d", |
|
299 face->family_name, |
|
300 ch.unicode(), str.toLatin1().data(), |
|
301 shaper.shaper_item.num_glyphs, |
|
302 decomposed.shaper_item.num_glyphs); |
|
303 |
|
304 str = ""; |
|
305 i = 0; |
|
306 while (i < shaper.shaper_item.num_glyphs) { |
|
307 str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16); |
|
308 ++i; |
|
309 } |
|
310 qDebug(" composed glyph result = %s", str.toLatin1().constData()); |
|
311 str = ""; |
|
312 i = 0; |
|
313 while (i < decomposed.shaper_item.num_glyphs) { |
|
314 str += QString("%1 ").arg(decomposed.shaper_item.glyphs[i], 4, 16); |
|
315 ++i; |
|
316 } |
|
317 qDebug(" decomposed glyph result = %s", str.toLatin1().constData()); |
|
318 return false; |
|
319 } |
|
320 |
|
321 struct ShapeTable { |
|
322 unsigned short unicode[16]; |
|
323 unsigned short glyphs[16]; |
|
324 }; |
|
325 |
|
326 static bool shaping(FT_Face face, const ShapeTable *s, HB_Script script) |
|
327 { |
|
328 Shaper shaper(face, script, QString::fromUtf16( s->unicode )); |
270 |
329 |
271 hb_uint32 nglyphs = 0; |
330 hb_uint32 nglyphs = 0; |
272 const unsigned short *g = s->glyphs; |
331 const unsigned short *g = s->glyphs; |
273 while ( *g ) { |
332 while ( *g ) { |
274 nglyphs++; |
333 nglyphs++; |
275 g++; |
334 g++; |
276 } |
335 } |
277 |
336 |
278 if( nglyphs != shaper_item.num_glyphs ) |
337 if( nglyphs != shaper.shaper_item.num_glyphs ) |
279 goto error; |
338 goto error; |
280 |
339 |
281 for (hb_uint32 i = 0; i < nglyphs; ++i) { |
340 for (hb_uint32 i = 0; i < nglyphs; ++i) { |
282 if ((shaper_item.glyphs[i]&0xffffff) != s->glyphs[i]) |
341 if ((shaper.shaper_item.glyphs[i]&0xffffff) != s->glyphs[i]) |
283 goto error; |
342 goto error; |
284 } |
343 } |
285 return true; |
344 return true; |
286 error: |
345 error: |
287 str = ""; |
346 QString str = ""; |
288 const unsigned short *uc = s->unicode; |
347 const unsigned short *uc = s->unicode; |
289 while (*uc) { |
348 while (*uc) { |
290 str += QString("%1 ").arg(*uc, 4, 16); |
349 str += QString("%1 ").arg(*uc, 4, 16); |
291 ++uc; |
350 ++uc; |
292 } |
351 } |
293 qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d", |
352 qDebug("%s: shaping of string %s failed, nglyphs=%d, expected %d", |
294 face->family_name, |
353 face->family_name, |
295 str.toLatin1().constData(), |
354 str.toLatin1().constData(), |
296 shaper_item.num_glyphs, nglyphs); |
355 shaper.shaper_item.num_glyphs, nglyphs); |
297 |
356 |
298 str = ""; |
357 str = ""; |
299 hb_uint32 i = 0; |
358 hb_uint32 i = 0; |
300 while (i < shaper_item.num_glyphs) { |
359 while (i < shaper.shaper_item.num_glyphs) { |
301 str += QString("%1 ").arg(shaper_item.glyphs[i], 4, 16); |
360 str += QString("%1 ").arg(shaper.shaper_item.glyphs[i], 4, 16); |
302 ++i; |
361 ++i; |
303 } |
362 } |
304 qDebug(" glyph result = %s", str.toLatin1().constData()); |
363 qDebug(" glyph result = %s", str.toLatin1().constData()); |
305 return false; |
364 return false; |
306 } |
365 } |
|
366 |
|
367 |
|
368 void tst_QScriptEngine::greek() |
|
369 { |
|
370 FT_Face face = loadFace("DejaVuSans.ttf"); |
|
371 if (face) { |
|
372 for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { |
|
373 QString str; |
|
374 str.append(uc); |
|
375 if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { |
|
376 //qDebug() << "skipping" << hex << uc; |
|
377 continue; |
|
378 } |
|
379 if (uc == 0x1fc1 || uc == 0x1fed) |
|
380 continue; |
|
381 QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) ); |
|
382 } |
|
383 FT_Done_Face(face); |
|
384 } else { |
|
385 QSKIP("couln't find DejaVu Sans", SkipAll); |
|
386 } |
|
387 |
|
388 |
|
389 face = loadFace("SBL_grk.ttf"); |
|
390 if (face) { |
|
391 for (int uc = 0x1f00; uc <= 0x1fff; ++uc) { |
|
392 QString str; |
|
393 str.append(uc); |
|
394 if (str.normalized(QString::NormalizationForm_D).normalized(QString::NormalizationForm_C) != str) { |
|
395 //qDebug() << "skipping" << hex << uc; |
|
396 continue; |
|
397 } |
|
398 if (uc == 0x1fc1 || uc == 0x1fed) |
|
399 continue; |
|
400 QVERIFY( decomposedShaping(face, HB_Script_Greek, QChar(uc)) ); |
|
401 |
|
402 } |
|
403 |
|
404 const ShapeTable shape_table [] = { |
|
405 { { 0x3b1, 0x300, 0x313, 0x0 }, |
|
406 { 0xb8, 0x3d3, 0x3c7, 0x0 } }, |
|
407 { { 0x3b1, 0x313, 0x300, 0x0 }, |
|
408 { 0xd4, 0x0 } }, |
|
409 |
|
410 { {0}, {0} } |
|
411 }; |
|
412 |
|
413 |
|
414 const ShapeTable *s = shape_table; |
|
415 while (s->unicode[0]) { |
|
416 QVERIFY( shaping(face, s, HB_Script_Greek) ); |
|
417 ++s; |
|
418 } |
|
419 |
|
420 FT_Done_Face(face); |
|
421 } else { |
|
422 QSKIP("couln't find DejaVu Sans", SkipAll); |
|
423 } |
|
424 } |
|
425 |
307 |
426 |
308 void tst_QScriptEngine::devanagari() |
427 void tst_QScriptEngine::devanagari() |
309 { |
428 { |
310 { |
429 { |
311 FT_Face face = loadFace("raghu.ttf"); |
430 FT_Face face = loadFace("raghu.ttf"); |