1 /***************************************************************************
2  *   Copyright (C) 2007 by Pierre Marchand   *
3  *   pierre@oep-h.com   *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 
22 #include "fmotf.h"
23 #include "fmaltcontext.h"
24 
25 #include <QDebug>
26 #include <QLibrary>
27 #include <QTextCodec>
28 
29 QList<int> FMOtf::altGlyphs;
30 
31 
32 /// HB Externals //////////////////////////////////////////////////////////////////////////////////////////////
33 
HB_GetLineBreakClass(HB_UChar32 ch)34 HB_LineBreakClass HB_GetLineBreakClass ( HB_UChar32 ch )
35 {
36 	return ( HB_LineBreakClass ) 0;
37 }
38 
HB_GetUnicodeCharProperties(HB_UChar32 ch,HB_CharCategory * category,int * combiningClass)39 void HB_GetUnicodeCharProperties ( HB_UChar32 ch, HB_CharCategory *category, int *combiningClass )
40 {
41 	*category = ( HB_CharCategory ) QChar::Category ( ch );
42 	*combiningClass = QChar::CombiningClass ( ch );
43 }
44 
HB_GetUnicodeCharCategory(HB_UChar32 ch)45 HB_CharCategory HB_GetUnicodeCharCategory ( HB_UChar32 ch )
46 {
47 	return ( HB_CharCategory ) QChar::Category ( ch );
48 }
49 
HB_GetUnicodeCharCombiningClass(HB_UChar32 ch)50 int HB_GetUnicodeCharCombiningClass ( HB_UChar32 ch )
51 {
52 	return QChar::CombiningClass ( ch );
53 }
54 
HB_GetMirroredChar(HB_UChar16 ch)55 HB_UChar16 HB_GetMirroredChar ( HB_UChar16 ch )
56 {
57 	return QChar ( ch ).mirroredChar().unicode();
58 }
59 
HB_GetGraphemeClass(HB_UChar32 ch)60 HB_GraphemeClass HB_GetGraphemeClass(HB_UChar32 ch)
61 {
62 	return (HB_GraphemeClass) 0;
63 }
HB_GetWordClass(HB_UChar32 ch)64 HB_WordClass HB_GetWordClass(HB_UChar32 ch)
65 {
66 	return (HB_WordClass) 0;
67 }
HB_GetSentenceClass(HB_UChar32 ch)68 HB_SentenceClass HB_GetSentenceClass(HB_UChar32 ch)
69 {
70 	return (HB_SentenceClass) 0;
71 }
72 
HB_GetGraphemeAndLineBreakClass(HB_UChar32 ch,HB_GraphemeClass * grapheme,HB_LineBreakClass * lineBreak)73 void HB_GetGraphemeAndLineBreakClass(HB_UChar32 ch, HB_GraphemeClass *grapheme, HB_LineBreakClass *lineBreak)
74 {
75 	//###
76 }
77 
getChar(const HB_UChar16 * string,hb_uint32 length,hb_uint32 & i)78 HB_UChar32 getChar ( const HB_UChar16 *string, hb_uint32 length, hb_uint32 &i )
79 {
80 // 	qDebug() << "HB_UChar32 getChar";
81 	HB_UChar32 ch;
82 // HB_SurrogateToUcs4 expands in HB_UChar32 without prefix, i have to expand manually
83 	if ( ( ( string[i] & 0xfc00 ) == 0xd800 ) //  HB_IsHighSurrogate
84 	        && i < length - 1
85 	        && ( ( string[i + 1] & 0xfc00 ) == 0xdc00 ) ) // HB_IsLowSurrogate
86 	{
87 		ch = ( ( ( HB_UChar32 ) string[i] ) <<10 ) + ( string[i + 1] ) - 0x35fdc00;// HB_SurrogateToUcs4
88 		++i;
89 	}
90 	else
91 	{
92 		ch = string[i];
93 	}
94 	return ch;
95 }
96 
hb_stringToGlyphs(HB_Font font,const HB_UChar16 * string,hb_uint32 length,HB_Glyph * glyphs,hb_uint32 * numGlyphs,HB_Bool)97 HB_Bool hb_stringToGlyphs ( HB_Font font, const HB_UChar16 *string, hb_uint32 length, HB_Glyph *glyphs, hb_uint32 *numGlyphs, HB_Bool /*rightToLeft*/ )
98 {
99 // 	qDebug() << "HB_Bool hb_stringToGlyphs";
100 	FT_Face face = ( FT_Face ) font->userData;
101 	if ( length > *numGlyphs )
102 		return false;
103 
104 	int glyph_pos = 0;
105 	for ( hb_uint32 i = 0; i < length; ++i )
106 	{
107 		glyphs[glyph_pos] = FT_Get_Char_Index ( face, getChar ( string, length, i ) );
108 		++glyph_pos;
109 	}
110 
111 	*numGlyphs = glyph_pos;
112 
113 	return true;
114 }
115 
hb_getAdvances(HB_Font font,const HB_Glyph * glyphs,hb_uint32 numGlyphs,HB_Fixed * advances,int flags)116 void hb_getAdvances ( HB_Font font, const HB_Glyph * glyphs, hb_uint32 numGlyphs, HB_Fixed *advances, int flags )
117 {
118 // 	qDebug() << "void hb_getAdvances with flag("<<QString::number ( flags, 16 ) <<")";
119 	FT_Face face = ( FT_Face ) font->userData;
120 	for ( hb_uint32 i = 0; i < numGlyphs; ++i )
121 	{
122 // 		qDebug() << "\tLoad index "<< i;
123 // 		qDebug() << "\tWhich is glyph "<<glyphs[i];
124 		FT_Load_Glyph ( face, glyphs[i],FT_LOAD_NO_SCALE );
125 // 		qDebug() << "ADV("<< glyphs[i] <<")("<< face->glyph->metrics.horiAdvance <<")";
126 		advances[i] = face->glyph->metrics.horiAdvance;
127 	}
128 }
129 
hb_canRender(HB_Font font,const HB_UChar16 * string,hb_uint32 length)130 HB_Bool hb_canRender ( HB_Font font, const HB_UChar16 *string, hb_uint32 length )
131 {
132 // 	qDebug() << "HB_Bool hb_canRender";
133 	FT_Face face = ( FT_Face ) font->userData;
134 
135 	for ( hb_uint32 i = 0; i < length; ++i )
136 		if ( !FT_Get_Char_Index ( face, getChar ( string, length, i ) ) )
137 			return false;
138 
139 	return true;
140 }
141 
hb_getSFntTable(void * font,HB_Tag tableTag,HB_Byte * buffer,HB_UInt * length)142 HB_Error hb_getSFntTable ( void *font, HB_Tag tableTag, HB_Byte *buffer, HB_UInt *length )
143 {
144 // 	qDebug() << "HB_Error hb_getSFntTable";
145 	FT_Face face = ( FT_Face ) font;
146 	FT_ULong ftlen = *length;
147 	FT_Error error = 0;
148 
149 	if ( !FT_IS_SFNT ( face ) )
150 		return HB_Err_Invalid_Argument;
151 
152 	error = FT_Load_Sfnt_Table ( face, tableTag, 0, buffer, &ftlen );
153 	*length = ftlen;
154 	return ( HB_Error ) error;
155 }
156 
hb_getPointInOutline(HB_Font font,HB_Glyph glyph,int flags,hb_uint32 point,HB_Fixed * xpos,HB_Fixed * ypos,hb_uint32 * nPoints)157 HB_Error hb_getPointInOutline ( HB_Font font, HB_Glyph glyph, int flags, hb_uint32 point, HB_Fixed *xpos, HB_Fixed *ypos, hb_uint32 *nPoints )
158 {
159 // 	qDebug() << "HB_Error hb_getPointInOutline";
160 	HB_Error error = HB_Err_Ok;
161 	FT_Face face = ( FT_Face ) font->userData;
162 
163 	int load_flags = ( flags & HB_ShaperFlag_UseDesignMetrics ) ? FT_LOAD_NO_HINTING : FT_LOAD_DEFAULT;
164 
165 	if ( ( error = ( HB_Error ) FT_Load_Glyph ( face, glyph, load_flags ) ) )
166 		return error;
167 
168 	if ( face->glyph->format != ft_glyph_format_outline )
169 		return ( HB_Error ) HB_Err_Invalid_SubTable;//HB_Err_Invalid_GPOS_SubTable;
170 
171 	*nPoints = face->glyph->outline.n_points;
172 	if ( ! ( *nPoints ) )
173 		return HB_Err_Ok;
174 
175 	if ( point > *nPoints )
176 		return ( HB_Error ) HB_Err_Invalid_SubTable;//HB_Err_Invalid_GPOS_SubTable;
177 
178 	*xpos = face->glyph->outline.points[point].x;
179 	*ypos = face->glyph->outline.points[point].y;
180 
181 	return HB_Err_Ok;
182 }
183 
hb_getGlyphMetrics(HB_Font font,HB_Glyph glyph,HB_GlyphMetrics * metrics)184 void hb_getGlyphMetrics ( HB_Font font, HB_Glyph glyph, HB_GlyphMetrics *metrics )
185 {
186 	qDebug() << "void hb_getGlyphMetrics";
187 	// ###
188 	metrics->x = metrics->y = metrics->width = metrics->height = metrics->xOffset = metrics->yOffset = 0;
189 }
190 
hb_getFontMetric(HB_Font font,HB_FontMetric metric)191 HB_Fixed hb_getFontMetric ( HB_Font font, HB_FontMetric metric )
192 {
193 	qDebug() << "HB_Fixed hb_getFontMetric";
194 	return 0; // ####
195 }
196 
HB_Library_Resolve(const char * library,const char * symbol)197 void *HB_Library_Resolve(const char *library, const char *symbol)
198 {
199 	return (void*)QLibrary::resolve(library, symbol); // Not very clean cast
200 }
201 
HB_TextCodecForMib(int mib)202 void *HB_TextCodecForMib(int mib)
203 {
204 	return QTextCodec::codecForMib(mib);
205 }
206 
HB_TextCodec_ConvertFromUnicode(void * codec,const HB_UChar16 * unicode,hb_uint32 length,hb_uint32 * outputLength)207 char *HB_TextCodec_ConvertFromUnicode(void *codec, const HB_UChar16 *unicode, hb_uint32 length, hb_uint32 *outputLength)
208 {
209 	QByteArray data = reinterpret_cast<QTextCodec *>(codec)->fromUnicode((const QChar *)unicode, length);
210     // ### suboptimal
211 	char *output = (char *)malloc(data.length() + 1);
212 	memcpy(output, data.constData(), data.length() + 1);
213 	if (outputLength)
214 		*outputLength = data.length();
215 	return output;
216 }
217 
HB_TextCodec_FreeResult(char * string)218 void HB_TextCodec_FreeResult(char *string)
219 {
220 	free(string);
221 }
222 /// END OF HB Externals //////////////////////////////////////////////////////////////////////////////////////
223 
224 
225 const HB_FontClass hb_fontClass =
226 {
227 	hb_stringToGlyphs,
228 	hb_getAdvances,
229 	hb_canRender,
230 	hb_getPointInOutline,
231 	hb_getGlyphMetrics,
232 	hb_getFontMetric
233 };
234 
235 QString
OTF_tag_name(HB_UInt tag)236 OTF_tag_name ( HB_UInt tag )
237 {
238 	QString name;
239 	name[0] = ( char ) ( tag >> 24 );
240 	name[1] = ( char ) ( ( tag >> 16 ) & 0xFF );
241 	name[2] = ( char ) ( ( tag >> 8 ) & 0xFF );
242 	name[3] = ( char ) ( tag & 0xFF );
243 //   qDebug(QString("OTF_tag_name (%1) -> %2").arg(tag).arg(name));
244 	return name;
245 }
246 
247 HB_UInt
OTF_name_tag(QString s)248 OTF_name_tag ( QString s )
249 {
250 
251 	HB_UInt ret = FT_MAKE_TAG ( s[0].unicode (), s[1].unicode (), ( s[2].isNull() ? ' ' :s[2].unicode () ), ( s[3].isNull() ? ' ' :s[3].unicode () ) );
252 //   qDebug(QString("OTF_name_tag (%1) -> %2").arg(s).arg(ret));
253 	return ret;
254 }
255 
256 //#define DFLT 0xFFFF
257 
258 
get_glyph(int index)259 int FMOtf::get_glyph ( int index )
260 {
261 	return _buffer->in_string[index].gindex;
262 }
263 
264 
FMOtf(FT_Face f,double scale)265 FMOtf::FMOtf ( FT_Face f , double scale )
266 {
267 
268 	_face = f;
269 	_buffer = 0;
270 
271 	hbFont.klass = &hb_fontClass;
272 	hbFont.userData = _face ;
273 	hbFont.x_ppem = _face->size->metrics.x_ppem;
274 	hbFont.y_ppem = _face->size->metrics.y_ppem;
275 // 	if(scale == 0.0)
276 // 	{
277 // 		hbFont.x_scale = _face->size->metrics.x_scale;
278 // 		hbFont.y_scale = _face->size->metrics.y_scale;
279 // 	}
280 // 	else
281 	{
282 		hbFont.x_scale = static_cast<uint32_t>(scale);
283 		hbFont.y_scale = static_cast<uint32_t>(scale);
284 	}
285 
286 	glyphAlloc = false;
287 	FT_ULong length = 0;
288 
289 	if ( !FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GDEF" ), 0, NULL, &length ) )
290 	{
291 // 		qDebug() << QString ( "length of GDEF table is %1" ).arg ( length ) ;
292 		if ( length > 0 )
293 		{
294 			_memgdef.resize ( length );
295 			FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GDEF" ), 0,
296 			                     ( FT_Byte * ) _memgdef.data (), &length );
297 			gdefstream = new ( HB_StreamRec );
298 			gdefstream->base = ( HB_Byte * ) _memgdef.data ();
299 			gdefstream->size = _memgdef.size ();
300 			gdefstream->pos = 0;
301 
302 
303 			HB_New_GDEF_Table ( &_gdef );
304 			if ( !HB_Load_GDEF_Table ( gdefstream, &_gdef ) )
305 				GDEF = 1;
306 			else
307 				GDEF = 0;
308 		}
309 
310 		else
311 			GDEF = 0;
312 	}
313 	else
314 		GDEF = 0;
315 	length = 0;
316 	if ( !FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GSUB" ), 0, NULL, &length ) )
317 	{
318 // 		qDebug()<< QString ( "length of GSUB table is %1" ).arg ( length ) ;
319 		if ( length > 32 ) //Some font files seem to have a fake table that is just 32 words long and make harbuzz confused
320 		{
321 			_memgsub.resize ( length );
322 			FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GSUB" ), 0,
323 			                     ( FT_Byte * ) _memgsub.data (), &length );
324 			gsubstream = new ( HB_StreamRec );
325 			gsubstream->base = ( HB_Byte * ) _memgsub.data ();
326 			gsubstream->size = _memgsub.size ();
327 			gsubstream->pos = 0;
328 
329 			if ( GDEF ? !HB_Load_GSUB_Table ( gsubstream, &_gsub, _gdef, gdefstream ) :
330 			        !HB_Load_GSUB_Table ( gsubstream, &_gsub, NULL, NULL ) )
331 			{
332 				GSUB = 1;
333 				qDebug()<<"REGISTER alternate substitutions callback";
334 				HB_GSUB_Register_Alternate_Function( _gsub, manageAlternates ,0);
335 			}
336 			else
337 				GSUB = 0;
338 		}
339 		else
340 			GSUB = 0;
341 	}
342 	else
343 		GSUB = 0;
344 
345 	length = 0;
346 	if ( !FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GPOS" ), 0, NULL, &length ) )
347 	{
348 // 		qDebug () << QString ( "length of GPOS table is %1" ).arg ( length  );
349 		if ( length > 32 )
350 		{
351 			_memgpos.resize ( length );
352 			FT_Load_Sfnt_Table ( _face, OTF_name_tag ( "GPOS" ), 0,
353 			                     ( FT_Byte * ) _memgpos.data (), &length );
354 			gposstream = new ( HB_StreamRec );
355 			gposstream->base = ( HB_Byte * ) _memgpos.data ();
356 			gposstream->size = _memgpos.size ();
357 			gposstream->pos = 0;
358 
359 			if ( GDEF ? !HB_Load_GPOS_Table ( gposstream, &_gpos, _gdef, gdefstream ) :
360 			        !HB_Load_GPOS_Table ( gposstream, &_gpos, NULL, NULL ) )
361 				GPOS = 1;
362 			else
363 				GPOS = 0;
364 		}
365 		else
366 			GPOS = 0;
367 	}
368 	else
369 		GPOS = 0;
370 // 	if ( hb_buffer_new ( &_buffer ) )
371 // 		qDebug ( "unable to get _buffer" );
372 }
373 
374 
~FMOtf()375 FMOtf::~FMOtf ()
376 {
377 
378 	/// All those free lead to segfault in Harfbuzz, we’ll see later,
379 	/// now, we really want to be able to remove a font.
380 	if ( _buffer )
381 		hb_buffer_free ( _buffer );
382 	if ( GDEF )
383 		HB_Done_GDEF_Table ( _gdef );
384 	if ( GSUB )
385 		HB_Done_GSUB_Table ( _gsub );
386 	if ( GPOS )
387 		HB_Done_GPOS_Table ( _gpos );
388 
389 }
390 
391 
procstring(QString s,OTFSet set)392 QList<RenderedGlyph> FMOtf::procstring ( QString s, OTFSet set )
393 {
394 	curString = s;
395 	altGlyphs.clear();
396 	if ( hb_buffer_new ( &_buffer ) != HB_Err_Ok)
397 	{
398 		qDebug ( ) << "Unable to get _buffer("<< _buffer <<")";
399 		return QList<RenderedGlyph>();
400 	}
401 	int numR = procstring ( s, set.script, set.lang, set.gsub_features, set.gpos_features );
402 
403 	QList<RenderedGlyph> ret = get_position();
404 
405 	// We need to attach CHAR infos to GLYPHS
406 	int sCount(ret.count());
407 	for(int si(0);si < sCount; ++si)
408 	{
409 		ret[si].lChar = s.at(ret[si].log).unicode();
410 	}
411 
412 	hb_buffer_free ( _buffer );
413 	_buffer = 0;
414 
415 	return ret;
416 }
417 
418 
procstring(QList<Character> shaped,QString script)419 QList< RenderedGlyph > FMOtf::procstring( QList<Character> shaped , QString script )
420 {
421 // 	QString script = "latn";
422 	curString.clear();
423 	QString lang = "dflt";
424 // 	regAltGlyphs.clear();
425 	if ( hb_buffer_new ( &_buffer ) != HB_Err_Ok)
426 	{
427 		qDebug ( ) << "Unable to get _buffer("<< _buffer <<")";
428 		return QList<RenderedGlyph>();
429 	}
430 	hb_buffer_clear ( _buffer );
431 	int n = shaped.count ();
432 	HB_Error           error;
433 	uint all = 0x1;
434 	QMap<QString,uint> props;
435 	QStringList orderedFeatures;
436 
437 	//First we collect properties
438 	for( int i = 0; i < n; i++ )
439 	{
440 		foreach(QString cProp, shaped[i].CustomProperties)
441 		{
442 			if(!props.contains(cProp))
443 			{
444 				orderedFeatures << cProp;
445 				props[cProp] = all;
446 				all *= 2;
447 // 				qDebug()<< "Feature " << cProp << " has prop mask"<<  QString::number(props[cProp],2) ;
448 			}
449 		}
450 	}
451 	for ( int i = 0; i < n; i++ )
452 	{
453 		uint prop = 0;
454 // 		prop |= all;
455 		foreach(QString cProp, shaped[i].CustomProperties)
456 		{
457 			prop |= (props[cProp]);
458 		}
459 // 		if(!prop)
460 // 		{
461 // 			prop |= ~0 ;
462 // 		}
463 		error = hb_buffer_add_glyph ( _buffer, FT_Get_Char_Index ( _face, shaped[i].unicode() ), ~prop, i );
464 
465 		curString += QChar(shaped[i].unicode()) ;
466 
467 // 		qDebug() << "Adding "<< QString::number(shaped[i].unicode(),16) << "["<< QString::number( ~prop, 2 )<<"]";
468 		if ( error !=  HB_Err_Ok )
469 			qDebug() << "hb_buffer_add_glyph () failed";
470 
471 	}
472 
473 	if ( GSUB )
474 	{
475 
476 		HB_GSUB_Clear_Features ( _gsub );
477 
478 		set_table ( "GSUB" );
479 		set_script ( script );
480 		set_lang ( lang );
481 
482 		for ( int fCur(0); fCur < orderedFeatures.count() ; ++fCur )
483 		{
484 			QString feature(orderedFeatures[fCur]);
485 			HB_UShort fidx;
486 			error = HB_GSUB_Select_Feature ( _gsub, OTF_name_tag ( feature ), curScript, curLang, &fidx );
487 			if ( !error )
488 			{
489 				HB_GSUB_Add_Feature ( _gsub, fidx, props[feature] );
490 				qDebug() << "GSUB_ADD "<< feature <<" => "<<QString::number( props[feature], 2 );
491 			}
492 			else
493 				qDebug() << QString ( "adding gsub feature [%1] failed : %2" ).arg ( feature ).arg ( error );
494 		}
495 
496 		error = HB_GSUB_Apply_String ( _gsub, _buffer );
497 		if ( error && error != HB_Err_Not_Covered )
498 			qDebug () << QString ( "applying gsub features to string  returned %2" ).arg ( error );
499 
500 	}
501 	if ( GPOS )
502 	{
503 		HB_GPOS_Clear_Features ( _gpos );
504 		set_table ( "GPOS" );
505 		set_script ( script );
506 		set_lang ( lang );
507 		for ( int fCur(0); fCur < orderedFeatures.count() ; ++fCur )
508 		{
509 			QString feature(orderedFeatures[fCur]);
510 			HB_UShort fidx;
511 			error = HB_GPOS_Select_Feature ( _gpos, OTF_name_tag ( feature ), curScript, curLang, &fidx );
512 			if ( !error )
513 			{
514 				HB_GPOS_Add_Feature ( _gpos, fidx,  props[feature]  );
515 			}
516 			else
517 				qDebug() << QString ( "adding gpos feature [%1] failed : %2" ).arg ( feature ).arg ( error );
518 		}
519 
520 		error = HB_GPOS_Apply_String ( &hbFont, _gpos, FT_LOAD_NO_SCALE, _buffer,
521 		        /*while dvi is true font klass is not used */ true,
522 		        /*r2l */ true );
523 		if ( error && error != HB_Err_Not_Covered )
524 			qDebug () << QString ( "applying gpos features to string returned %2" ).arg ( error ) ;
525 
526 	}
527 
528 
529 	QList<RenderedGlyph> ret = get_position();
530 	hb_buffer_free ( _buffer );
531 	_buffer = 0;
532 
533 	// We need to attach CHAR infos to GLYPHS
534 	int sCount(ret.count());
535 	for(int si(0);si < sCount; ++si)
536 	{
537 		ret[si].lChar = shaped.at(ret[si].log).unicode();
538 	}
539 
540 	return ret;
541 
542 }
543 
544 
procstring(QList<unsigned int> glyList,QString script,QString lang,QStringList gsub,QStringList gpos)545 QList< RenderedGlyph > FMOtf::procstring(QList< unsigned int > glyList, QString script, QString lang, QStringList gsub, QStringList gpos)
546 {
547 	hb_buffer_clear ( _buffer );
548 	int n = glyList.count();
549 	HB_Error           error;
550 	uint all = 0x1;
551 	uint prop;
552 	for ( int i = 0; i < n; i++ )
553 	{
554 		prop = 0;
555 		prop |= all;
556 		error = hb_buffer_add_glyph ( _buffer,
557 				glyList[i],
558 						prop,
559       i );
560 		if ( error !=  HB_Err_Ok )
561 			qDebug() << "hb_buffer_add_glyph ("<< glyList[i] <<") failed";
562 	}
563 
564 	if ( ! gsub.isEmpty() )
565 	{
566 		HB_GSUB_Clear_Features ( _gsub );
567 		set_table ( "GSUB" );
568 		set_script ( script );
569 		set_lang ( lang );
570 
571 		for ( QStringList::iterator ife = gsub.begin (); ife != gsub.end (); ife++ )
572 		{
573 			HB_UShort fidx;
574 			error = HB_GSUB_Select_Feature ( _gsub,
575 					OTF_name_tag ( *ife ),
576 							curScript, curLang, &fidx );
577 			if ( !error )
578 			{
579 				HB_GSUB_Add_Feature ( _gsub, fidx, ~all );
580 			}
581 			else
582 				qDebug() << QString ( "adding gsub feature [%1] failed : %2" ).arg ( *ife ).arg ( error );
583 		}
584 		error = HB_GSUB_Apply_String ( _gsub, _buffer );
585 		if ( error && error != HB_Err_Not_Covered )
586 			qDebug () << QString ( "applying gsub features returned %1" ).arg ( error );
587 
588 	}
589 	if ( !gpos.isEmpty() )
590 	{
591 		HB_GPOS_Clear_Features ( _gpos );
592 		set_table ( "GPOS" );
593 		set_script ( script );
594 		set_lang ( lang );
595 
596 		for ( QStringList::iterator ife = gpos.begin (); ife != gpos.end ();
597 				    ife++ )
598 		{
599 			uint fprop = 0xffff;
600 			HB_UShort fidx;
601 			error = HB_GPOS_Select_Feature ( _gpos,
602 					OTF_name_tag ( *ife ),
603 							curScript, curLang, &fidx );
604 			if ( !error )
605 			{
606 				HB_GPOS_Add_Feature ( _gpos, fidx,fprop );
607 			}
608 			else
609 				qDebug () << QString ( "adding gsub feature [%1] failed : %2" ).arg ( *ife ).arg ( error ) ;
610 		}
611 		error = HB_GPOS_Apply_String ( &hbFont, _gpos, FT_LOAD_NO_SCALE, _buffer,
612 		        /*while dvi is true font klass is not used */ true,
613 		        /*r2l */ true );
614 		if ( error && error != HB_Err_Not_Covered )
615 			qDebug () << QString ( "applying gpos features  returned %1 ").arg  ( error ) ;
616 
617 	}
618 
619 	return get_position();
620 
621 }
622 
procstring(QString s,QString script,QString lang,QStringList gsub,QStringList gpos)623 int FMOtf::procstring( QString s, QString script, QString lang, QStringList gsub, QStringList gpos )
624 {
625 	hb_buffer_clear ( _buffer );
626 	int n = s.length ();
627 	HB_Error           error;
628 	uint all = 0x1;
629 	uint prop;
630 	for ( int i = 0; i < n; i++ )
631 	{
632 		prop = 0;
633 		prop |= all;
634 		error = hb_buffer_add_glyph ( _buffer,
635 		                                        FT_Get_Char_Index ( _face, s[i].unicode() ),
636 		                                        prop,
637 		                                        i );
638 		if ( error !=  HB_Err_Ok )
639 			qDebug() << "hb_buffer_add_glyph ("<< s[i] <<") failed";
640 // 		else
641 // 			qDebug() << "hb_buffer_add_glyph ("<< s[i] <<") success";
642 
643 	}
644 
645 // 	if ( _buffer->in_length > 0 )
646 // 	{
647 // 		qDebug() << "_buffer->in_length = " <<_buffer->in_length;
648 //
649 // 	}
650 // 	else
651 // 		qDebug() << "_buffer->in_length = " <<_buffer->in_length;
652 //
653 
654 
655 	if ( ! gsub.isEmpty() )
656 	{
657 // 		qDebug() <<"Process GSUB";
658 
659 		HB_GSUB_Clear_Features ( _gsub );
660 
661 // 		qDebug() <<"Set GSUB";
662 		set_table ( "GSUB" );
663 		set_script ( script );
664 		set_lang ( lang );
665 // 		qDebug() <<"GSUB set";
666 
667 		for ( QStringList::iterator ife = gsub.begin (); ife != gsub.end (); ife++ )
668 		{
669 			HB_UShort fidx;
670 			error = HB_GSUB_Select_Feature ( _gsub,
671 			        OTF_name_tag ( *ife ),
672 			        curScript, curLang, &fidx );
673 			if ( !error )
674 			{
675 				HB_GSUB_Add_Feature ( _gsub, fidx, ~all );
676 // 				qDebug()<< QString("adding gsub feature [%1] success : %2").arg(*ife).arg(fidx );
677 			}
678 			else
679 				qDebug() << QString ( "adding gsub feature [%1] failed : %2" ).arg ( *ife ).arg ( error );
680 		}
681 
682 // 		qDebug() << "APPLY";
683 		error = HB_GSUB_Apply_String ( _gsub, _buffer );
684 // 		qDebug() << "YLPPA";
685 //
686 		if ( error && error != HB_Err_Not_Covered )
687 			qDebug () << QString ( "applying gsub features to string \"%1\" returned %2" ).arg ( s ).arg ( error );
688 
689 	}
690 	if ( !gpos.isEmpty() )
691 	{
692 // 		qDebug() <<"Process GPOS";
693 
694 		HB_GPOS_Clear_Features ( _gpos );
695 		set_table ( "GPOS" );
696 		set_script ( script );
697 		set_lang ( lang );
698 
699 		for ( QStringList::iterator ife = gpos.begin (); ife != gpos.end ();
700 		        ife++ )
701 		{
702 			uint fprop = 0xffff;
703 // 			fprop |= all | init | fina;
704 			HB_UShort fidx;
705 			error = HB_GPOS_Select_Feature ( _gpos,
706 			        OTF_name_tag ( *ife ),
707 			        curScript, curLang, &fidx );
708 			if ( !error )
709 			{
710 				HB_GPOS_Add_Feature ( _gpos, fidx,fprop );
711 // 				qDebug()<< QString("GPOS [%2] feature.lookupcount = %1").arg(_gpos->FeatureList.FeatureRecord[fidx].Feature.LookupListCount).arg(*ife);
712 			}
713 			else
714 				qDebug () << QString ( "adding gsub feature [%1] failed : %2" ).arg ( *ife ).arg ( error ) ;
715 		}
716 		error = HB_GPOS_Apply_String ( &hbFont, _gpos, FT_LOAD_NO_SCALE, _buffer,
717 		         true,/*while dvi is true font klass is not used */
718 		        true /*r2l  */);
719 		if ( error && error != HB_Err_Not_Covered )
720 			qDebug () << QString ( "applying gpos features to string \"%1\" returned %2" ).arg ( s ).arg ( error ) ;
721 
722 	}
723 
724 //  	qDebug() << "END_PROCSTRING";
725 	int nret = _buffer->in_length;
726 // 	hb_buffer_free ( _buffer );
727 	return nret;
728 }
729 
730 
731 
732 
733 QStringList
get_tables()734 FMOtf::get_tables ()
735 {
736 	QStringList ret;
737 
738 	//   if (GDEF)scName()
739 	//     ret.insert (ret.end (), "GDEF");
740 	if ( GPOS )
741 		ret.insert ( ret.end (), "GPOS" );
742 	if ( GSUB )
743 		ret.insert ( ret.end (), "GSUB" );
744 
745 	return ret;
746 }
747 
748 void
set_table(QString s)749 FMOtf::set_table ( QString s )
750 {
751 	curTable = s;
752 }
753 
754 QStringList
get_scripts()755 FMOtf::get_scripts ()
756 {
757 //	qDebug("FMOtf::get_scripts ()");
758 	QStringList ret;
759 
760 	if ( curTable == "GSUB" && GSUB )
761 	{
762 		HB_UInt *taglist;
763 		if ( HB_GSUB_Query_Scripts ( _gsub, &taglist ) )
764 			qDebug ( "error HB_GSUB_Query_Scripts" );
765 		while ( *taglist )
766 		{
767 // 			qDebug ( QString ( "script [%1]" ).arg ( OTF_tag_name ( *taglist ) ) );
768 			ret.append ( OTF_tag_name ( *taglist ) );
769 			++taglist;
770 		}
771 
772 	}
773 	if ( curTable == "GPOS" && GPOS )
774 	{
775 
776 		HB_UInt *taglist;
777 		if ( HB_GPOS_Query_Scripts ( _gpos, &taglist ) )
778 			qDebug ( "error HB_GPOS_Query_Scripts" );
779 		while ( *taglist )
780 		{
781 			ret.append ( OTF_tag_name ( *taglist ) );
782 			++taglist;
783 		}
784 	}
785 	return ret;
786 
787 }
788 
789 void
set_script(QString s)790 FMOtf::set_script ( QString s )
791 {
792 //	qDebug(QString("set_script (%1)").arg(s));
793 	curScriptName = s;
794 	if ( curTable == "GSUB" && GSUB )
795 	{
796 		if ( HB_GSUB_Select_Script
797 		        ( _gsub, OTF_name_tag ( curScriptName ), &curScript ) )
798 			qDebug ( "Unable to set script index " );
799 	}
800 	if ( curTable == "GPOS" && GPOS )
801 	{
802 		if ( HB_GPOS_Select_Script
803 		        ( _gpos, OTF_name_tag ( curScriptName ), &curScript ) )
804 			qDebug ( "Unable to set script index" );
805 	}
806 }
807 
808 
809 QStringList
get_langs()810 FMOtf::get_langs ()
811 {
812 	QStringList ret;
813 
814 	ret << "dflt";
815 	if ( curTable == "GSUB" && GSUB )
816 	{
817 
818 		HB_UInt *taglist;
819 		if ( HB_GSUB_Query_Languages ( _gsub, curScript, &taglist ) )
820 			qDebug ( "error HB_GSUB_Query_Langs" );
821 		while ( *taglist )
822 		{
823 // 			qDebug ( QString ( "lang [%1]" ).arg ( OTF_tag_name ( *taglist ) ) );
824 			ret.append ( OTF_tag_name ( *taglist ) );
825 			++taglist;
826 		}
827 
828 	}
829 	if ( curTable == "GPOS" && GPOS )
830 	{
831 
832 		HB_UInt *taglist;
833 		if ( HB_GPOS_Query_Languages ( _gpos, curScript, &taglist ) )
834 			qDebug ( "error HB_GPOS_Query_Langs" );
835 		while ( *taglist )
836 		{
837 			ret.append ( OTF_tag_name ( *taglist ) );
838 			++taglist;
839 		}
840 
841 	}
842 
843 	return ret;
844 }
845 
846 void
set_lang(QString s)847 FMOtf::set_lang ( QString s )
848 {
849 //	qDebug(QString("set_lang (%1)").arg(s));
850 	if ( s == "default" || s == "dflt" || s.isEmpty () )
851 	{
852 		curLangName = "dflt";
853 		curLang = 0xFFFF;// HB_DEFAULT_LANGUAGE;
854 		return;
855 	}
856 	HB_Error           error;
857 	curLangName = s;
858 	if ( curTable == "GSUB" && GSUB )
859 	{
860 		error = HB_GSUB_Select_Language ( _gsub,
861 		        OTF_name_tag ( curLangName ),
862 		        curScript,
863 		        &curLang,
864 		        &curLangReq );
865 // 		if ( error )
866 // 			qDebug ( QString ( "Unable to set lang index due to error %1" ).arg ( error ) );
867 	}
868 	if ( curTable == "GPOS" && GPOS )
869 	{
870 		error = HB_GPOS_Select_Language ( _gpos, OTF_name_tag ( curLangName ),curScript, &curLang, &curLangReq );
871 // 		if ( error )
872 // 			qDebug ( QString ( "Unable to set lang index due to error %1" ).arg ( error ) );
873 	}
874 
875 }
876 
877 
878 QStringList
get_features(bool required)879 FMOtf::get_features ( bool required )
880 {
881 	QStringList ret;
882 
883 	if ( curTable == "GSUB" && GSUB )
884 	{
885 
886 		HB_UInt *taglist;
887 		required ? HB_GSUB_Query_Features ( _gsub, curScript, curLangReq,
888 		        &taglist ) :
889 		HB_GSUB_Query_Features ( _gsub, curScript, curLang, &taglist );
890 		while ( *taglist )
891 		{
892 			ret.append ( OTF_tag_name ( *taglist ) );
893 			++taglist;
894 		}
895 
896 
897 	}
898 	if ( curTable == "GPOS" && GPOS )
899 	{
900 
901 		HB_UInt *taglist;
902 		required ? HB_GPOS_Query_Features ( _gpos, curScript, curLangReq,
903 		        &taglist ) :
904 		HB_GPOS_Query_Features ( _gpos, curScript, curLang, &taglist );
905 		while ( *taglist )
906 		{
907 			ret.append ( OTF_tag_name ( *taglist ) );
908 			++taglist;
909 		}
910 
911 	}
912 	return ret;
913 }
914 
915 void
set_features(QStringList ls)916 FMOtf::set_features ( QStringList ls )
917 {
918 	curFeatures = ls;
919 }
920 
921 // void dump_pos(HB_PositionRec p)
922 // {
923 // 	qDebug(QString("xpos = %1 | ypos = %2 | xadv = %3 | yadv = %4 | %5 | back = %6 ").arg(p.x_pos).arg(p.y_pos).arg(p.x_advance).arg(p.y_advance).arg(p.new_advance ? "NEW" : "NOT_NEW").arg(p.back));
924 // }
925 
926 /**
927 Dump all the "uneasy" HB_Buffer into a user-friendy QList :)
928 */
get_position(HB_Buffer abuffer)929 GlyphList FMOtf::get_position ( HB_Buffer abuffer )
930 {
931 // 	qDebug() << "FMOtf::get_position () for buffer : " << _buffer;
932 	HB_Buffer bak_buf = _buffer ;
933 	if ( abuffer )
934 	{
935 		_buffer = abuffer;
936 	}
937 	GlyphList renderedString;
938 	bool wantPos = true;
939 	for ( uint bIndex = 0 ; bIndex < _buffer->in_length; ++bIndex )
940 	{
941 // 		qDebug() << "bIndex = "<< bIndex;
942 		RenderedGlyph gl;
943 
944 		gl.glyph = _buffer->in_string[bIndex].gindex;
945 		if ( gl.glyph == 0 )
946 		{
947 // 			qDebug() << "glyph skipped";
948 			// Here we just continue but in the case of an actual lyout engine
949 			// we should keep track of empty glyphs positions too.
950 			continue;
951 		}
952 		gl.log = _buffer->in_string[bIndex].cluster;
953 		HB_Position p = 0;
954 		if ( wantPos && GPOS )
955 		{
956 			p = &_buffer->positions[bIndex] ;
957 // 			qDebug() << "p = "<< p;
958 			if ( !p )
959 				wantPos = false;
960 		}
961 
962 		if ( !p ) // applyGPOS has not been called?
963 		{
964 			FT_GlyphSlot slot = _face->glyph;
965 			if ( !FT_Load_Glyph ( _face, gl.glyph , FT_LOAD_NO_SCALE ) )
966 			{
967 				gl.xadvance = ( double ) slot->metrics.horiAdvance/* ( slot->advance.x )*/;
968 				gl.yadvance = ( double ) slot->metrics.vertAdvance/* ( slot->advance.y )*/;
969 				gl.xoffset = 0;
970 				gl.yoffset = 0;
971 			}
972 		}
973 		else
974 		{
975 // 			qDebug() << p->back;
976 			double backBonus = 0.0;
977 			for ( int bb = 0; bb < p->back; ++bb )
978 			{
979 				backBonus -= renderedString[bIndex - ( bb + 1 ) ].xadvance;
980 			}
981 			if ( p->new_advance )
982 			{
983 // 				qDebug() << "P_NEW_ADV  bb = "<<backBonus<<" ; adv = "<<p->x_advance<<"; offs = "<< p->x_pos;
984 				gl.xadvance = p->x_advance ;
985 				gl.yadvance = p->y_advance;
986 				gl.xoffset = p->x_pos + backBonus;
987 				gl.yoffset = p->y_pos;
988 			}
989 			else
990 			{
991 				FT_GlyphSlot slot = _face->glyph;
992 				if ( !FT_Load_Glyph ( _face, gl.glyph , FT_LOAD_NO_SCALE ) )
993 				{
994 // 					qDebug() << "P_ADV  bb = "<<backBonus<<" ; adv = "<<slot->metrics.horiAdvance<<"; Xoffs = "<< p->x_pos<<"; Yoffs = "<<p->y_pos;
995 					gl.xadvance = ( double ) slot->metrics.horiAdvance/* ( slot->advance.x )*/   + p->x_advance ;
996 					gl.yadvance = ( double ) slot->metrics.vertAdvance/* ( slot->advance.y )*/;
997 					gl.xoffset = p->x_pos + backBonus;
998 					gl.yoffset = p->y_pos;
999 				}
1000 			}
1001 		}
1002 
1003 
1004 		renderedString << gl;
1005 // 		qDebug() << gl.dump();
1006 	}
1007 
1008 	_buffer = bak_buf;
1009 	return renderedString;
1010 }
1011 
manageAlternates(HB_UInt pos,HB_UShort glyphID,HB_UShort num_alternates,HB_UShort * alternates,void * data)1012 HB_UShort FMOtf::manageAlternates(HB_UInt pos, HB_UShort glyphID, HB_UShort num_alternates, HB_UShort * alternates, void * data)
1013 {
1014 	// ALTERNATES
1015 //	FMAltContext * actx(FMAltContextLib::GetCurrentContext());
1016 //	if(actx)
1017 //	{
1018 //		if((!actx->control(pos) != glyphID))
1019 //		{
1020 //			actx->setControl( pos, glyphID);
1021 //			actx->setSelect( pos, 0);
1022 ////			actx->addAlt(pos, glyphID);
1023 //			for(int i(0); i < num_alternates; ++i)
1024 //			{
1025 //				actx->addAlt(pos, alternates[i]);
1026 //			}
1027 ////			qDebug()<<"F"<<actx->chunkString()<< pos << glyphID << actx->alts(pos);
1028 //			return (HB_UShort) 0;
1029 //		}
1030 //		else
1031 //		{
1032 //			return (HB_UShort) actx->select(pos);
1033 //		}
1034 //	}
1035 	return (HB_UShort) 0;
1036 }
1037 
1038 
1039 
1040 
1041