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