1 /** \file lvfreetypeface.cpp
2     \brief FreeType font implementation
3 
4     CoolReader Engine
5 
6 
7     (c) Vadim Lopatin, 2000-2006
8     This source code is distributed under the terms of
9     GNU General Public License.
10 
11     See LICENSE file for details.
12 
13 */
14 
15 #include "lvfreetypeface.h"
16 
17 #include "../../include/lvfntman.h"
18 #include "../../include/lvfnt.h"
19 #include "../../include/lvtextfm.h"
20 #include "../../include/crlog.h"
21 #include "lvfontglyphcache.h"
22 #include "lvfontdef.h"
23 #include "lvfontcache.h"
24 
25 
26 #include "../include/gammatbl.h"
27 
28 
29 // fc-lang database
30 #include "fc-lang-cat.h"
31 
32 #if COLOR_BACKBUFFER == 0
33 //#define USE_BITMAP_FONT
34 #endif
35 
36 //DEFINE_NULL_REF( LVFont )
37 
38 #if (USE_FREETYPE == 1)
39 
40 #include <ft2build.h>
41 #include FT_FREETYPE_H
42 #include FT_OUTLINE_H   // for FT_Outline_Embolden()
43 #include FT_SYNTHESIS_H // for FT_GlyphSlot_Embolden()
44 #include FT_GLYPH_H     // for FT_Matrix_Multiply()
45 
46 // Helpers with font metrics (units are 1/64 px)
47 // #define FONT_METRIC_FLOOR(x)    ((x) & -64)
48 // #define FONT_METRIC_CEIL(x)     (((x)+63) & -64)
49 // #define FONT_METRIC_ROUND(x)    (((x)+32) & -64)
50 // #define FONT_METRIC_TRUNC(x)    ((x) >> 6)
51 #define FONT_METRIC_TO_PX(x)    (((x)+32) >> 6) // ROUND + TRUNC
52 // Uncomment to use the former >>6 (trunc) with no rounding (instead of previous one)
53 // #define FONT_METRIC_TO_PX(x)    ((x) >> 6)
54 
55 // Define to add more debug messages
56 //#define DEBUG_DRAW_TEXT
57 //#define DEBUG_MEASURE_TEXT
58 
59 extern int gammaIndex;          // lvfntman.cpp
60 
61 extern lString8 familyName(FT_Face face);
62 
63 
myabs(int n)64 inline int myabs(int n) { return n < 0 ? -n : n; }
65 
getReplacementChar(lUInt32 code,bool * can_be_ignored=NULL)66 static lChar32 getReplacementChar(lUInt32 code, bool * can_be_ignored = NULL) {
67     switch (code) {
68         case UNICODE_SOFT_HYPHEN_CODE:
69             return '-';
70         case 0x0401: // CYRILLIC CAPITAL LETTER IO
71             return 0x0415; //CYRILLIC CAPITAL LETTER IE
72         case 0x0451: // CYRILLIC SMALL LETTER IO
73             return 0x0435; // CYRILLIC SMALL LETTER IE
74         case UNICODE_NO_BREAK_SPACE:
75             return ' ';
76         case UNICODE_WORD_JOINER:
77             if (can_be_ignored)
78                 *can_be_ignored = true;
79             return UNICODE_ZERO_WIDTH_SPACE;
80         case UNICODE_ZERO_WIDTH_SPACE:
81             if (can_be_ignored)
82                 *can_be_ignored = true;
83             // If the font lacks a zero-width breaking space glyph (like
84             // some Kindle built-ins) substitute a different zero-width
85             // character instead of one with width.
86             return UNICODE_ZERO_WIDTH_NO_BREAK_SPACE;
87         case 0x2010:
88         case 0x2011:
89         case 0x2012:
90         case 0x2013:
91         case 0x2014:
92         case 0x2015:
93             return '-';
94         case 0x2018:
95         case 0x2019:
96         case 0x201a:
97         case 0x201b:
98             return '\'';
99         case 0x201c:
100         case 0x201d:
101         case 0x201e:
102         case 0x201f:
103         case 0x00ab:
104         case 0x00bb:
105             return '\"';
106         case 0x2039:
107             return '<';
108         case 0x203A:
109             return '>';
110         case 0x2044:
111             return '/';
112         case 0x2022: // css_lst_disc:
113         case 0x26AB: // css_lst_disc:
114         case 0x2981: // css_lst_disc:
115         case 0x25CF: // css_lst_disc:
116             return '*';
117         case 0x26AA: // css_lst_circle:
118         case 0x25E6: // css_lst_circle:
119         case 0x26AC: // css_lst_circle:
120         case 0x25CB: // css_lst_circle:
121             return 'o';
122         case 0x25A0: // css_lst_square:
123         case 0x25AA: // css_lst_square:
124         case 0x25FE: // css_lst_square:
125             return '-';
126         default:
127             break;
128     }
129     return 0;
130 }
131 
132 #if USE_HARFBUZZ==1
isHBScriptCursive(hb_script_t script)133 bool isHBScriptCursive( hb_script_t script ) {
134     // https://github.com/harfbuzz/harfbuzz/issues/64
135     // From https://android.googlesource.com/platform/frameworks/minikin/
136     //               +/refs/heads/experimental/libs/minikin/Layout.cpp
137     return  script == HB_SCRIPT_ARABIC ||
138             script == HB_SCRIPT_NKO ||
139             script == HB_SCRIPT_PSALTER_PAHLAVI ||
140             script == HB_SCRIPT_MANDAIC ||
141             script == HB_SCRIPT_MONGOLIAN ||
142             script == HB_SCRIPT_PHAGS_PA ||
143             script == HB_SCRIPT_DEVANAGARI ||
144             script == HB_SCRIPT_BENGALI ||
145             script == HB_SCRIPT_GURMUKHI ||
146             script == HB_SCRIPT_MODI ||
147             script == HB_SCRIPT_SHARADA ||
148             script == HB_SCRIPT_SYLOTI_NAGRI ||
149             script == HB_SCRIPT_TIRHUTA ||
150             script == HB_SCRIPT_OGHAM;
151 }
152 #endif
153 
newItem(LVFontLocalGlyphCache * local_cache,lChar32 ch,FT_GlyphSlot slot)154 static LVFontGlyphCacheItem *newItem(LVFontLocalGlyphCache *local_cache, lChar32 ch, FT_GlyphSlot slot) // , bool drawMonochrome
155 {
156     FONT_LOCAL_GLYPH_CACHE_GUARD
157     FT_Bitmap *bitmap = &slot->bitmap;
158     int w = bitmap->width;
159     int h = bitmap->rows;
160     LVFontGlyphCacheItem *item = LVFontGlyphCacheItem::newItem(local_cache, ch, w, h);
161     if (!item)
162         return 0;
163     if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { //drawMonochrome
164         lUInt8 mask = 0x80;
165         const lUInt8 *ptr = (const lUInt8 *) bitmap->buffer;
166         lUInt8 *dst = item->bmp;
167         //int rowsize = ((w + 15) / 16) * 2;
168         for (int y = 0; y < h; y++) {
169             const lUInt8 *row = ptr;
170             mask = 0x80;
171             for (int x = 0; x < w; x++) {
172                 *dst++ = (*row & mask) ? 0xFF : 00;
173                 mask >>= 1;
174                 if (!mask && x != w - 1) {
175                     mask = 0x80;
176                     row++;
177                 }
178             }
179             ptr += bitmap->pitch;//rowsize;
180         }
181     } else {
182 #if 0
183         if ( bitmap->pixel_mode==FT_PIXEL_MODE_MONO ) {
184             memset( item->bmp, 0, w*h );
185             lUInt8 * srcrow = bitmap->buffer;
186             lUInt8 * dstrow = item->bmp;
187             for ( int y=0; y<h; y++ ) {
188                 lUInt8 * src = srcrow;
189                 for ( int x=0; x<w; x++ ) {
190                     dstrow[x] =  ( (*src)&(0x80>>(x&7)) ) ? 255 : 0;
191                     if ((x&7)==7)
192                         src++;
193                 }
194                 srcrow += bitmap->pitch;
195                 dstrow += w;
196             }
197         } else {
198 #endif
199         if (bitmap->buffer && w > 0 && h > 0)
200         {
201             memcpy(item->bmp, bitmap->buffer, w * h);
202             // correct gamma
203             if ( gammaIndex!=GAMMA_NO_CORRECTION_INDEX )
204                 cr_correct_gamma_buf(item->bmp, w * h, gammaIndex);
205         }
206     }
207     item->origin_x = (lInt16) slot->bitmap_left;
208     item->origin_y = (lInt16) slot->bitmap_top;
209     item->advance  = (lUInt16)(FONT_METRIC_TO_PX( myabs(slot->metrics.horiAdvance) ));
210     return item;
211 }
212 
213 #if USE_HARFBUZZ == 1
214 
215 static LVFontGlyphCacheItem *newItem(LVFontLocalGlyphCache *local_cache, lUInt32 index, FT_GlyphSlot slot) {
216     FONT_LOCAL_GLYPH_CACHE_GUARD
217     FT_Bitmap *bitmap = &slot->bitmap;
218     int w = bitmap->width;
219     int h = bitmap->rows;
220     LVFontGlyphCacheItem *item = LVFontGlyphCacheItem::newItem(local_cache, index, w, h);
221     if (!item)
222         return 0;
223     if (bitmap->pixel_mode == FT_PIXEL_MODE_MONO) { //drawMonochrome
224         lUInt8 mask = 0x80;
225         const lUInt8 *ptr = (const lUInt8 *) bitmap->buffer;
226         lUInt8 *dst = item->bmp;
227         //int rowsize = ((w + 15) / 16) * 2;
228         for (int y = 0; y < h; y++) {
229             const lUInt8 *row = ptr;
230             mask = 0x80;
231             for (int x = 0; x < w; x++) {
232                 *dst++ = (*row & mask) ? 0xFF : 00;
233                 mask >>= 1;
234                 if (!mask && x != w - 1) {
235                     mask = 0x80;
236                     row++;
237                 }
238             }
239             ptr += bitmap->pitch;//rowsize;
240         }
241     } else {
242         if (bitmap->buffer && w > 0 && h > 0)
243         {
244             memcpy(item->bmp, bitmap->buffer, w * h);
245             // correct gamma
246             if ( gammaIndex!=GAMMA_NO_CORRECTION_INDEX )
247                 cr_correct_gamma_buf(item->bmp, w * h, gammaIndex);
248         }
249     }
250     item->origin_x = (lInt16) slot->bitmap_left;
251     item->origin_y = (lInt16) slot->bitmap_top;
252     item->advance =    (lUInt16)(FONT_METRIC_TO_PX( myabs(slot->metrics.horiAdvance) ));
253     return item;
254 }
255 
256 #endif
257 
258 // The 2 slots with "LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER" on the 2nd line previously
259 // were: "LCHAR_IS_SPACE | LCHAR_IS_EOL | LCHAR_ALLOW_WRAP_AFTER".
260 // LCHAR_IS_EOL was not used by any code, and has been replaced by LCHAR_IS_CLUSTER_TAIL
261 // (as flags were lUInt8, and the 8 bits were used, one needed to be dropped - they
262 // have since been upgraded to be lUInt16)
263 // (LCHAR_DEPRECATED_WRAP_AFTER for '-' and '/', as they may be used to
264 // separate words.)
265 static lUInt16 char_flags[] = {
266         0, 0, 0, 0, 0, 0, 0, 0, // 0    00
267     0, 0, LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER, 0, // 8    08
268     0, LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER, 0, 0, // 12   0C
269         0, 0, 0, 0, 0, 0, 0, 0, // 16   10
270         0, 0, 0, 0, 0, 0, 0, 0, // 24   18
271         LCHAR_IS_SPACE | LCHAR_ALLOW_WRAP_AFTER, 0, 0, 0, 0, 0, 0, 0, // 32   20
272     0, 0, 0, 0, 0, LCHAR_DEPRECATED_WRAP_AFTER, 0, LCHAR_DEPRECATED_WRAP_AFTER, // 40   28
273         0, 0, 0, 0, 0, 0, 0, 0, // 48   30
274 };
275 
276 // removed, as soft hyphens are now exclusively handled by hyphman:
277 //      (ch==UNICODE_SOFT_HYPHEN_CODE?LCHAR_ALLOW_WRAP_AFTER:
278 #define GET_CHAR_FLAGS(ch) \
279      (ch<48?char_flags[ch]: \
280         (ch==UNICODE_NO_BREAK_SPACE ? LCHAR_IS_SPACE: \
281         (ch==UNICODE_NO_BREAK_HYPHEN ? 0: \
282         (ch>=UNICODE_HYPHEN && ch<=UNICODE_EM_DASH ? LCHAR_DEPRECATED_WRAP_AFTER: \
283         (ch==UNICODE_ARMENIAN_HYPHEN ? LCHAR_DEPRECATED_WRAP_AFTER: \
284         (ch==UNICODE_FIGURE_SPACE ? 0: \
285         (ch>=UNICODE_EN_QUAD && ch<=UNICODE_ZERO_WIDTH_SPACE ? LCHAR_ALLOW_WRAP_AFTER: \
286          0)))))))
287 
288 #if USE_HARFBUZZ == 1
289 // For use with Harfbuzz light
290 struct LVCharTriplet
291 {
292     lChar32 prevChar;
293     lChar32 Char;
294     lChar32 nextChar;
295     bool operator==(const struct LVCharTriplet &other) const {
296         return prevChar == other.prevChar && Char == other.Char && nextChar == other.nextChar;
297     }
298 };
299 
300 struct LVCharPosInfo
301 {
302     int offset;
303     int width;
304 };
305 
306 inline lUInt32 getHash( const struct LVCharTriplet& triplet )
307 {
308     // lUInt32 hash = (((
309     //       getHash((lUInt32)triplet.Char) )*31
310     //     + getHash((lUInt32)triplet.prevChar) )*31
311     //     + getHash((lUInt32)triplet.nextChar) );
312     // Probably less expensive:
313     lUInt32 hash = getHash( (lUInt64)triplet.Char
314                    + (((lUInt64) triplet.prevChar) << 16)
315                    + (((lUInt64) triplet.nextChar) << 32));
316     return hash;
317 }
318 
319 #endif  // USE_HARFBUZZ==1
320 
321 void LVFreeTypeFace::setFallbackFont(LVFontRef font) {
322     _fallbackFont = font;
323     _fallbackFontIsSet = !font.isNull();
324     clearCache();
325 }
326 
327 
328 LVFont *LVFreeTypeFace::getFallbackFont(lUInt32 fallbackPassMask) {
329     int fallback_count = fontMan->GetFallbackFontCount();
330     lUInt32 full_mask = (1 << fallback_count) - 1;
331     if ((full_mask & (fallbackPassMask | _fallback_mask)) == full_mask)
332         return NULL;
333     if (!_fallbackFontIsSet) {
334         if (fallback_count > 0) {
335             // restore this fallback font index
336             int index = -1;
337             if (_fallback_mask != 0) {
338                 lUInt32 mask = _fallback_mask;
339                 index = 0;
340                 while ((mask & 0x01) == 0) {
341                     mask >>= 1;
342                     index++;
343                 }
344             }
345             // and get index for next fallback font
346             index++;
347             if (index == fallback_count)
348                 index = 0;
349             _fallbackFont = fontMan->GetFallbackFont(_size, _weight, _italic, index);
350         }
351         _fallbackFontIsSet = true;
352     }
353     LVFontRef res = _fallbackFont;
354     // Get first unprocessed font
355     if (!res.isNull() ) {
356         if ( res->getFallbackMask() & fallbackPassMask ) {       // already processed
357             if ((fallbackPassMask | _fallback_mask) != fallbackPassMask) {
358                 // (full_mask & fallbackPassMask) != full_mask, then recursion is not endless
359                 res = res->getFallbackFont(fallbackPassMask | _fallback_mask);
360             } else {
361                 CRLog::error("getFallbackFont(): invalid fallback pass mask: fallbackPassMask=0x%04X, _fallback_mask=0x%04X", fallbackPassMask, _fallback_mask);
362                 return NULL;
363             }
364         }
365     }
366     return res.get();
367 }
368 
369 LVFreeTypeFace::LVFreeTypeFace(LVMutex &mutex, FT_Library library,
370                                LVFontGlobalGlyphCache *globalCache)
371         : LVFont(),
372           _mutex(mutex), _fontFamily(css_ff_sans_serif), _library(library), _face(NULL),
373           _size(0), _hyphen_width(0), _baseline(0),
374           _weight(400), _italic(0), _embolden(false), _features(0),
375           _glyph_cache(globalCache),
376           _drawMonochrome(false),
377           _hintingMode(HINTING_MODE_AUTOHINT),
378           _shapingMode(SHAPING_MODE_FREETYPE),
379           _fallbackFontIsSet(false),
380           _fallback_mask(0)
381 #if USE_HARFBUZZ == 1
382         , _glyph_cache2(globalCache),
383           _width_cache2(1024)
384 #endif
385 {
386     _hintingMode = fontMan->GetHintingMode();
387 #if USE_HARFBUZZ == 1
388     _hb_font = 0;
389     _hb_buffer = hb_buffer_create();
390     _hb_features.reserve(22);
391     setupHBFeatures();
392 #endif
393 }
394 
395 LVFreeTypeFace::~LVFreeTypeFace() {
396 #if USE_HARFBUZZ == 1
397     if (_hb_buffer)
398         hb_buffer_destroy(_hb_buffer);
399 #endif
400     Clear();
401 }
402 
403 void LVFreeTypeFace::clearCache() {
404     _glyph_cache.clear();
405     _wcache.clear();
406     _lsbcache.clear();
407     _rsbcache.clear();
408 #if USE_HARFBUZZ == 1
409     _glyph_cache2.clear();
410     _width_cache2.clear();
411 #endif
412 }
413 
414 int LVFreeTypeFace::getHyphenWidth() {
415     FONT_GUARD
416     if (!_hyphen_width) {
417         _hyphen_width = getCharWidth(UNICODE_SOFT_HYPHEN_CODE);
418     }
419     return _hyphen_width;
420 }
421 
422 void LVFreeTypeFace::setKerning(bool kerningEnabled) {
423     _allowKerning = kerningEnabled;
424     _hash = 0; // Force lvstyles.cpp calcHash(font_ref_t) to recompute the hash
425 #if USE_HARFBUZZ == 1
426     if (_allowKerning)
427         setHBFeatureValue("kern", 1);
428     else
429         setHBFeatureValue("kern", 0);
430     // in cache may be found some ligatures, so clear it
431     clearCache();
432 #endif
433 }
434 
435 void LVFreeTypeFace::setHintingMode(hinting_mode_t mode) {
436     if (_hintingMode == mode)
437         return;
438     _hintingMode = mode;
439     _hash = 0; // Force lvstyles.cpp calcHash(font_ref_t) to recompute the hash
440     clearCache();
441     #if USE_HARFBUZZ==1
442     // Also update HB load flags with the updated hinting mode.
443     // We need this destroy/create, as only these will clear some internal HB caches
444     // (ft_font->advance_cache, ft_font->cached_x_scale); hb_ft_font_set_load_flags will not.
445     if (_hb_font)
446         hb_font_destroy(_hb_font);
447     _hb_font = hb_ft_font_create(_face, NULL);
448     if (_hb_font) {
449         // Use the same load flags as we do when using FT directly, to avoid mismatching advances & raster
450         int flags = FT_LOAD_DEFAULT;
451         flags |= (!_drawMonochrome ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_MONO);
452         if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
453             flags |= FT_LOAD_NO_AUTOHINT;
454         }
455         else if (_hintingMode == HINTING_MODE_AUTOHINT) {
456             flags |= FT_LOAD_FORCE_AUTOHINT;
457         }
458         else if (_hintingMode == HINTING_MODE_DISABLED) {
459             flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
460         }
461         hb_ft_font_set_load_flags(_hb_font, flags);
462     }
463     #endif
464 }
465 
466 void LVFreeTypeFace::setShapingMode( shaping_mode_t shapingMode )
467 {
468     _shapingMode = shapingMode;
469     _hash = 0; // Force lvstyles.cpp calcHash(font_ref_t) to recompute the hash
470 #if USE_HARFBUZZ==1
471     setupHBFeatures();
472     // Reset buffer (to have it shrunk if HB full > light that will need only a 3 slots buffer)
473     hb_buffer_reset(_hb_buffer);
474     // in cache may be found some ligatures, so clear it
475     clearCache();
476 #endif
477 }
478 
479 void LVFreeTypeFace::setBitmapMode(bool drawBitmap) {
480     if (_drawMonochrome == drawBitmap)
481         return;
482     _drawMonochrome = drawBitmap;
483     clearCache();
484 }
485 
486 void LVFreeTypeFace::setFeatures(int features)
487 {
488     _features = features;
489     _hash = 0; // Force lvstyles.cpp calcHash(font_ref_t) to recompute the hash
490 }
491 
492 // Synthetized bold on a font that does not come with a bold variant.
493 void LVFreeTypeFace::setEmbolden()
494 {
495     _embolden = true;
496     // A real bold font has weight 700, vs 400 for the regular.
497     // LVFontBoldTransform did +200, so we get 600 (demibold).
498     // Let's do the same (even if I don't see why not +300).
499     _weight = (_weight + 200 > 900) ? 900 : _weight + 200;
500     // And add +1 so we can know it's a fake/synthetized font, so we
501     // can avoid getting it (and get the original regular font instead)
502     // when synthetizing an other variant of that font.
503     _weight += 1;
504     // When not using Harfbuzz, we will simply call FT_GlyphSlot_Embolden()
505     // to get the glyphinfo and glyph with synthetized bold and increased
506     // metrics, and everything should work naturally:
507     //   "Embolden a glyph by a 'reasonable' value (which is highly a matter
508     //   of taste) [...] For emboldened outlines the height, width, and
509     //   advance metrics are increased by the strength of the emboldening".
510     //
511     // When using Harfbuzz, which uses itself the font metrics, that we
512     // can't tweak at all from outside, we'll get positioning based on
513     // the not-bolded font. We can't increase them as that would totally
514     // mess HB work.
515     // We can only do as MuPDF does (source/fitz/font.c): keep the HB
516     // positions, offset and advances, embolden the glyph by some value
517     // of 'strength', and shift left/bottom by 1/2 'strength', so the
518     // boldened glyph is centered on its original: the glyph being a
519     // bit larger, it will blend over its neighbour glyphs, but it
520     // looks quite allright.
521     // Caveat: words in fake bold will be bolder, but not larger than
522     // the same word in the regular font (unlike with a real bold font
523     // were they would be bolder and larger).
524     // We need to compute the strength as done in FT_GlyphSlot_Embolden():
525     //   xstr = FT_MulFix( face->units_per_EM, face->size->metrics.y_scale ) / 24;
526     //   ystr = xstr;
527     //   FT_Outline_EmboldenXY( &slot->outline, xstr, ystr );
528     // and will do as MuPDF does (with some private value of 'strength'):
529     //   FT_Outline_Embolden(&face->glyph->outline, strength);
530     //   FT_Outline_Translate(&face->glyph->outline, -strength/2, -strength/2);
531     // (with strength: 0=no change; 64=1px embolden; 128=2px embolden and 1px x/y translation)
532     // int strength = (_face->units_per_EM * _face->size->metrics.y_scale) / 24;
533     FT_Pos embolden_strength = FT_MulFix(_face->units_per_EM, _face->size->metrics.y_scale) / 24;
534     // Make it slightly less bold than Freetype's bold, as we get less spacing
535     // around glyphs with HarfBuzz, by getting the unbolded advances.
536     embolden_strength = embolden_strength * 3/4; // (*1/2 is fine but a tad too light)
537     _embolden_half_strength = embolden_strength / 2;
538 }
539 
540 bool LVFreeTypeFace::loadFromBuffer(LVByteArrayRef buf, int index, int size,
541                                     css_font_family_t fontFamily, bool monochrome, bool italicize) {
542     FONT_GUARD
543     _hintingMode = fontMan->GetHintingMode();
544     _drawMonochrome = monochrome;
545     _fontFamily = fontFamily;
546     if (_face)
547         FT_Done_Face(_face);
548     int error = FT_New_Memory_Face(_library, buf->get(), buf->length(), index,
549                                    &_face); /* create face object */
550     if (error)
551         return false;
552     if (_fileName.endsWith(".pfb") || _fileName.endsWith(".pfa")) {
553         lString8 kernFile = _fileName.substr(0, _fileName.length() - 4);
554         if (LVFileExists(Utf8ToUnicode(kernFile) + ".afm")) {
555             kernFile += ".afm";
556         } else if (LVFileExists(Utf8ToUnicode(kernFile) + ".pfm")) {
557             kernFile += ".pfm";
558         } else {
559             kernFile.clear();
560         }
561         if (!kernFile.empty())
562             error = FT_Attach_File(_face, kernFile.c_str());
563     }
564     //FT_Face_SetUnpatentedHinting( _face, 1 );
565     _slot = _face->glyph;
566     _faceName = familyName(_face);
567     CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str());
568     //if ( !FT_IS_SCALABLE( _face ) ) {
569     //    Clear();
570     //    return false;
571     // }
572     error = FT_Set_Pixel_Sizes(
573             _face,    /* handle to face object */
574             0,        /* pixel_width           */
575             size);  /* pixel_height          */
576 #if USE_HARFBUZZ == 1
577     if (FT_Err_Ok == error) {
578         if (_hb_font)
579             hb_font_destroy(_hb_font);
580         _hb_font = hb_ft_font_create(_face, 0);
581         if ( _hb_font ) {
582             // Use the same load flags as we do when using FT directly, to avoid mismatching advances & raster
583             int flags = FT_LOAD_DEFAULT;
584             flags |= (!_drawMonochrome ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_MONO);
585             if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
586                 flags |= FT_LOAD_NO_AUTOHINT;
587             }
588             else if (_hintingMode == HINTING_MODE_AUTOHINT) {
589                 flags |= FT_LOAD_FORCE_AUTOHINT;
590             }
591             else if (_hintingMode == HINTING_MODE_DISABLED) {
592                 flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
593             }
594             hb_ft_font_set_load_flags(_hb_font, flags);
595         } else {
596             error = FT_Err_Invalid_Argument;
597         }
598     }
599 
600 #endif
601     if (error) {
602         Clear();
603         return false;
604     }
605 #if 0
606     int nheight = _face->size->metrics.height;
607     int targetheight = size << 6;
608     error = FT_Set_Pixel_Sizes(
609                 _face,    /* handle to face object */
610                 0,        /* pixel_width           */
611                 (size * targetheight + nheight/2)/ nheight );  /* pixel_height          */
612 #endif
613 
614     _height = FONT_METRIC_TO_PX( _face->size->metrics.height );
615     _size = size; //(_face->size->metrics.height >> 6);
616     _baseline = _height + FONT_METRIC_TO_PX( _face->size->metrics.descender );
617     _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400;
618     _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
619 
620     if (!error && italicize && !_italic) {
621         _italic = 2;
622         // We must use the same matrix values as FT_GlyphSlot_Oblique()
623         // (see freetype2/src/base/ftsynth.c)
624         _matrix.xx = 0x10000L;
625         _matrix.yx = 0x00000L;
626         _matrix.xy = 0x0366AL;
627         _matrix.yy = 0x10000L;
628     }
629 
630     if (error) {
631         return false;
632     }
633 
634     // If no unicode charmap, select any symbol charmap.
635     // This is needed with Harfbuzz shaping (with Freetype, we switch charmap
636     // when needed). It might not be needed with a Harfbuzz newer than 2.6.1
637     // that will include https://github.com/harfbuzz/harfbuzz/pull/1948.
638     if (FT_Select_Charmap(_face, FT_ENCODING_UNICODE)) // non-zero means failure
639         // If no unicode charmap found, try symbol charmap
640         FT_Select_Charmap(_face, FT_ENCODING_MS_SYMBOL);
641 
642     return true;
643 }
644 
645 bool
646 LVFreeTypeFace::loadFromFile(const char *fname, int index, int size, css_font_family_t fontFamily,
647                              bool monochrome, bool italicize) {
648     FONT_GUARD
649     _hintingMode = fontMan->GetHintingMode();
650     _drawMonochrome = monochrome;
651     _fontFamily = fontFamily;
652     if (fname)
653         _fileName = fname;
654     if (_fileName.empty())
655         return false;
656     if (_face)
657         FT_Done_Face(_face);
658     int error = FT_New_Face(_library, _fileName.c_str(), index, &_face); /* create face object */
659     if (error)
660         return false;
661     if (_fileName.endsWith(".pfb") || _fileName.endsWith(".pfa")) {
662         lString8 kernFile = _fileName.substr(0, _fileName.length() - 4);
663         if (LVFileExists(Utf8ToUnicode(kernFile) + ".afm")) {
664             kernFile += ".afm";
665         } else if (LVFileExists(Utf8ToUnicode(kernFile) + ".pfm")) {
666             kernFile += ".pfm";
667         } else {
668             kernFile.clear();
669         }
670         if (!kernFile.empty())
671             error = FT_Attach_File(_face, kernFile.c_str());
672     }
673     //FT_Face_SetUnpatentedHinting( _face, 1 );
674     _slot = _face->glyph;
675     _faceName = familyName(_face);
676     CRLog::debug("Loaded font %s [%d]: faceName=%s, ", _fileName.c_str(), index, _faceName.c_str());
677     //if ( !FT_IS_SCALABLE( _face ) ) {
678     //    Clear();
679     //    return false;
680     // }
681     error = FT_Set_Pixel_Sizes(
682             _face,    /* handle to face object */
683             0,        /* pixel_width           */
684             size);  /* pixel_height          */
685 #if USE_HARFBUZZ == 1
686     if (FT_Err_Ok == error) {
687         if (_hb_font)
688             hb_font_destroy(_hb_font);
689         _hb_font = hb_ft_font_create(_face, 0);
690         if (!_hb_font) {
691             error = FT_Err_Invalid_Argument;
692         }
693         else {
694             // Use the same load flags as we do when using FT directly, to avoid mismatching advances & raster
695             int flags = FT_LOAD_DEFAULT;
696             flags |= (!_drawMonochrome ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_MONO);
697             if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
698                 flags |= FT_LOAD_NO_AUTOHINT;
699             }
700             else if (_hintingMode == HINTING_MODE_AUTOHINT) {
701                 flags |= FT_LOAD_FORCE_AUTOHINT;
702             }
703             else if (_hintingMode == HINTING_MODE_DISABLED) {
704                 flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
705             }
706             hb_ft_font_set_load_flags(_hb_font, flags);
707         }
708     }
709 #endif
710     if (error) {
711         Clear();
712         return false;
713     }
714 #if 0
715     int nheight = _face->size->metrics.height;
716     int targetheight = size << 6;
717     error = FT_Set_Pixel_Sizes(
718                 _face,    /* handle to face object */
719                 0,        /* pixel_width           */
720                 (size * targetheight + nheight/2)/ nheight );  /* pixel_height          */
721 #endif
722 
723     _height = FONT_METRIC_TO_PX( _face->size->metrics.height );
724     _size = size; //(_face->size->metrics.height >> 6);
725     _baseline = _height + FONT_METRIC_TO_PX( _face->size->metrics.descender );
726     _weight = _face->style_flags & FT_STYLE_FLAG_BOLD ? 700 : 400;
727     _italic = _face->style_flags & FT_STYLE_FLAG_ITALIC ? 1 : 0;
728 
729     if (!error && italicize && !_italic) {
730         _italic = 2;
731         // We'll use FT_GlyphSlot_Oblique(), with this additional
732         // matrix to fix up fake italic glyph metrics.
733         // We must use the same matrix values as FT_GlyphSlot_Oblique()
734         // (see freetype2/src/base/ftsynth.c)
735         _matrix.xx = 0x10000L;
736         _matrix.yx = 0x00000L;
737         _matrix.xy = 0x0366AL;
738         _matrix.yy = 0x10000L;
739     }
740 
741     if (error) {
742         // error
743         return false;
744     }
745 
746     // If no unicode charmap, select any symbol charmap.
747     // This is needed with Harfbuzz shaping (with Freetype, we switch charmap
748     // when needed). It might not be needed with a Harfbuzz newer than 2.6.1
749     // that will include https://github.com/harfbuzz/harfbuzz/pull/1948.
750     if (FT_Select_Charmap(_face, FT_ENCODING_UNICODE)) // non-zero means failure
751         // If no unicode charmap found, try symbol charmap
752         FT_Select_Charmap(_face, FT_ENCODING_MS_SYMBOL);
753 
754     return true;
755 }
756 
757 #if USE_HARFBUZZ == 1
758 
759 lChar32 LVFreeTypeFace::filterChar(lChar32 code, lChar32 def_char) {
760     if (code == '\t')     // (FreeSerif doesn't have \t, get a space
761         code = ' ';       // rather than a '?')
762     FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code);
763     if (ch_glyph_index != 0) { // found
764         return code;
765     }
766 
767     if ( code >= 0xF000 && code <= 0xF0FF) {
768         // If no glyph found and code is among the private unicode
769         // area classically used by symbol fonts (range U+F020-U+F0FF),
770         // try to switch to FT_ENCODING_MS_SYMBOL
771         if (!FT_Select_Charmap(_face, FT_ENCODING_MS_SYMBOL)) {
772             ch_glyph_index = FT_Get_Char_Index( _face, code );
773             // restore unicode charmap if there is one
774             FT_Select_Charmap(_face, FT_ENCODING_UNICODE);
775             if (ch_glyph_index != 0) { // glyph found: code is valid
776                 return code;
777             }
778         }
779     }
780     lChar32 res = getReplacementChar(code);
781     if (res != 0)
782         return res;
783     if (def_char != 0)
784         return def_char;
785     // If nothing found, let code be
786     return code;
787 }
788 
789 bool LVFreeTypeFace::hbCalcCharWidth(LVCharPosInfo *posInfo, const LVCharTriplet &triplet,
790                                      lChar32 def_char, lUInt32 fallbackPassMask) {
791     if (!posInfo)
792         return false;
793     unsigned int segLen = 0;
794     int cluster;
795     hb_buffer_clear_contents(_hb_buffer);
796     if (0 != triplet.prevChar) {
797         hb_buffer_add(_hb_buffer, (hb_codepoint_t) triplet.prevChar, segLen);
798         segLen++;
799     }
800     hb_buffer_add(_hb_buffer, (hb_codepoint_t) triplet.Char, segLen);
801     cluster = segLen;
802     segLen++;
803     if (0 != triplet.nextChar) {
804         hb_buffer_add(_hb_buffer, (hb_codepoint_t) triplet.nextChar, segLen);
805         segLen++;
806     }
807     hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
808     hb_buffer_guess_segment_properties(_hb_buffer);
809     hb_shape(_hb_font, _hb_buffer, _hb_features.ptr(), _hb_features.length());
810     unsigned int glyph_count = hb_buffer_get_length(_hb_buffer);
811     if (segLen == glyph_count) {
812         hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, &glyph_count);
813         hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, &glyph_count);
814         // Ignore HB measurements when there is a single glyph not found,
815         // as it may be found in a fallback font
816         int codepoint_notfound_nb = 0;
817         for (unsigned int i=0; i<glyph_count; i++) {
818             if ( glyph_info[i].codepoint == 0 )
819                 codepoint_notfound_nb++;
820             // This does not look like it's really needed to ignore
821             // more measurements (I felt it was needed for hebrew with
822             // many diacritics).
823             // if ( glyph_pos[i].x_advance == 0 )
824             //    zero_advance_nb++;
825         }
826         if ( codepoint_notfound_nb == 0 ) {
827             // Be sure HB chosen glyph is the same as freetype chosen glyph,
828             // which will be the one that will be rendered
829             FT_UInt ch_glyph_index = FT_Get_Char_Index( _face, triplet.Char );
830             if ( glyph_info[cluster].codepoint == ch_glyph_index ) {
831                 posInfo->offset = FONT_METRIC_TO_PX(glyph_pos[cluster].x_offset);
832                 posInfo->width = FONT_METRIC_TO_PX(glyph_pos[cluster].x_advance);
833                 return true;
834             }
835         }
836 
837     }
838     // Otherwise, use plain Freetype getGlyphInfo() which will check
839     // again with this font, or the fallback one
840     glyph_info_t glyph;
841     if ( getGlyphInfo(triplet.Char, &glyph, def_char, fallbackPassMask) ) {
842         posInfo->offset = 0;
843         posInfo->width = glyph.width;
844         return true;
845     }
846     return false;
847 }
848 
849 #endif  // USE_HARFBUZZ==1
850 
851 FT_UInt LVFreeTypeFace::getCharIndex(lUInt32 code, lChar32 def_char) {
852     if (code == '\t')
853         code = ' ';
854     FT_UInt ch_glyph_index = FT_Get_Char_Index(_face, code);
855     if ( ch_glyph_index==0 && code >= 0xF000 && code <= 0xF0FF) {
856         // If no glyph found and code is among the private unicode
857         // area classically used by symbol fonts (range U+F020-U+F0FF),
858         // try to switch to FT_ENCODING_MS_SYMBOL
859         if (!FT_Select_Charmap(_face, FT_ENCODING_MS_SYMBOL)) {
860             ch_glyph_index = FT_Get_Char_Index( _face, code );
861             // restore unicode charmap if there is one
862             FT_Select_Charmap(_face, FT_ENCODING_UNICODE);
863         }
864     }
865     if ( ch_glyph_index==0 && def_char != 0 ) {
866         bool can_be_ignored = false;
867         lUInt32 replacement = getReplacementChar( code, &can_be_ignored );
868         if ( replacement )
869             ch_glyph_index = FT_Get_Char_Index( _face, replacement );
870         if ( ch_glyph_index==0 && !can_be_ignored ) {
871             // if neither the index of this character nor the index of the replacement character is found,
872             // and if this character can be safely ignored,
873             // we simply skip it so as not to draw the unnecessary replacement character.
874             ch_glyph_index = FT_Get_Char_Index( _face, def_char );
875         }
876     }
877     return ch_glyph_index;
878 }
879 
880 #if USE_HARFBUZZ == 1
881 bool LVFreeTypeFace::setHBFeatureValue(const char *tag, uint32_t value)
882 {
883     hb_feature_t hb_feature;
884     if (hb_feature_from_string(tag, -1, &hb_feature)) {
885         int idx = -1;
886         int i;
887         for (i = 0; i < _hb_features.length(); i++) {
888             if (_hb_features[i].tag == hb_feature.tag) {
889                 idx = i;
890                 break;
891             }
892         }
893         if (idx < 0) {
894             idx = _hb_features.length();
895             _hb_features.add(hb_feature);
896         }
897         _hb_features[idx].value = value;
898         return true;
899     }
900     return false;
901 }
902 
903 bool LVFreeTypeFace::addHBFeature(const char *tag)
904 {
905     hb_feature_t hb_feature;
906     if (hb_feature_from_string(tag, -1, &hb_feature)) {
907         for (int i = 0; i < _hb_features.length(); i++) {
908             if (_hb_features[i].tag == hb_feature.tag) {
909                 _hb_features[i] = hb_feature;
910                 return true;
911             }
912         }
913         _hb_features.add(hb_feature);
914         return true;
915     }
916     return false;
917 }
918 
919 bool LVFreeTypeFace::delHBFeature(const char *tag)
920 {
921     hb_feature_t hb_feature;
922     if (hb_feature_from_string(tag, -1, &hb_feature)) {
923         for (int i = 0; i < _hb_features.length(); i++) {
924             if (_hb_features[i].tag == hb_feature.tag) {
925                 _hb_features.remove(i);
926                 return true;
927             }
928         }
929     }
930     return false;
931 }
932 
933 void LVFreeTypeFace::setupHBFeatures()
934 {
935     _hb_features.clear();
936     if ( _shapingMode == SHAPING_MODE_HARFBUZZ ) {
937         setHBFeatureValue("kern", _allowKerning ? 1 : 0);
938         addHBFeature("+liga");
939     }
940     else if ( _shapingMode == SHAPING_MODE_HARFBUZZ_LIGHT ) {
941         // HarfBuzz features for lighweight characters width calculating with caching.
942         // We need to disable all the features, enabled by default in Harfbuzz, that
943         // may split a char into more glyphs, or merge chars into one glyph.
944         // (see harfbuzz/src/hb-ot-shape.cc hb_ot_shape_collect_features() )
945         //
946         // We can enable these ones:
947         setHBFeatureValue("kern", _allowKerning ? 1 : 0);
948         addHBFeature("+mark");          // Mark Positioning: Fine positioning of a mark glyph to a base character
949         addHBFeature("+mkmk");          // Mark-to-mark Positioning: Fine positioning of a mark glyph to another mark character
950         addHBFeature("+curs");          // Cursive Positioning: Precise positioning of a letter's connection to an adjacent one
951         addHBFeature("+locl");          // Substitutes character with the preferred form based on script language
952         //
953         // We should disable these ones:
954         addHBFeature("-liga");          // Standard Ligatures: replaces (by default) sequence of characters with a single ligature glyph
955         addHBFeature("-rlig");          // Ligatures required for correct text display (any script, but in cursive) - Arabic, semitic
956         addHBFeature("-clig");          // Applies a second ligature feature based on a match of a character pattern within a context of surrounding patterns
957         addHBFeature("-ccmp");          // Glyph composition/decomposition: either calls a ligature replacement on a sequence of characters or replaces a character with a sequence of glyphs
958         // Provides logic that can for example effectively alter the order of input characters
959         addHBFeature("-calt");          // Contextual Alternates: Applies a second substitution feature based on a match of a character pattern within a context of surrounding patterns
960         addHBFeature("-rclt");          // Required Contextual Alternates: Contextual alternates required for correct text display which differs from the default join for other letters, required especially important by Arabic
961         addHBFeature("-rvrn");          // Required Variation Alternates: Special variants of a single character, which need apply to specific font variation, required by variable fonts
962         addHBFeature("-ltra");          // Left-to-right glyph alternates: Replaces characters with forms befitting left-to-right presentation
963         addHBFeature("-ltrm");          // Left-to-right mirrored forms: Replaces characters with possibly mirrored forms befitting left-to-right presentation
964         addHBFeature("-rtla");          // Right-to-left glyph alternates: Replaces characters with forms befitting right-to-left presentation
965         addHBFeature("-rtlm");          // Right-to-left mirrored forms: Replaces characters with possibly mirrored forms befitting right-to-left presentation
966         addHBFeature("-frac");          // Fractions: Converts figures separated by slash with diagonal fraction
967         addHBFeature("-numr");          // Numerator: Converts to appropriate fraction numerator form, invoked by frac
968         addHBFeature("-dnom");          // Denominator: Converts to appropriate fraction denominator form, invoked by frac
969         addHBFeature("-rand");          // Replaces character with random forms (meant to simulate handwriting)
970         addHBFeature("-trak");          // Tracking (?)
971         addHBFeature("-vert");          // Vertical (?)
972         // Especially needed with FreeSerif and french texts: -ccmp
973         // Especially needed with Fedra Serif and "The", "Thuringe": -calt
974         // These tweaks seem fragile (adding here +smcp to experiment with small caps would break FreeSerif again).
975         // So, when tuning these, please check it still behave well with FreeSerif.
976         //
977         // The way KERNING_MODE_HARFBUZZ_LIGHT is implemented, we'll be drawing the
978         // original codepoints, so there's no much use allowing additional HB features,
979         // even the one-to-one substitutions like small-cap or oldstyle-nums...
980         return;
981     }
982     else { // text shaping not use HarfBuzz features
983         return;
984     }
985     if ( _features != LFNT_OT_FEATURES_NORMAL ) {
986         // Add requested features
987         if ( _features & LFNT_OT_FEATURES_M_LIGA ) { addHBFeature("-liga"); addHBFeature("-clig"); }
988         if ( _features & LFNT_OT_FEATURES_M_CALT ) { addHBFeature("-calt"); }
989         if ( _features & LFNT_OT_FEATURES_P_DLIG ) { addHBFeature("+dlig"); }
990         if ( _features & LFNT_OT_FEATURES_M_DLIG ) { addHBFeature("-dlig"); }
991         if ( _features & LFNT_OT_FEATURES_P_HLIG ) { addHBFeature("+hlig"); }
992         if ( _features & LFNT_OT_FEATURES_M_HLIG ) { addHBFeature("-hlig"); }
993         if ( _features & LFNT_OT_FEATURES_P_HIST ) { addHBFeature("+hist"); }
994         if ( _features & LFNT_OT_FEATURES_P_RUBY ) { addHBFeature("+ruby"); }
995         if ( _features & LFNT_OT_FEATURES_P_SMCP ) { addHBFeature("+smcp"); }
996         if ( _features & LFNT_OT_FEATURES_P_C2SC ) { addHBFeature("+c2sc"); addHBFeature("+smcp"); }
997         if ( _features & LFNT_OT_FEATURES_P_PCAP ) { addHBFeature("+pcap"); }
998         if ( _features & LFNT_OT_FEATURES_P_C2PC ) { addHBFeature("+c2pc"); addHBFeature("+pcap"); }
999         if ( _features & LFNT_OT_FEATURES_P_UNIC ) { addHBFeature("+unic"); }
1000         if ( _features & LFNT_OT_FEATURES_P_TITL ) { addHBFeature("+titl"); }
1001         if ( _features & LFNT_OT_FEATURES_P_SUPS ) { addHBFeature("+sups"); }
1002         if ( _features & LFNT_OT_FEATURES_P_SUBS ) { addHBFeature("+subs"); }
1003         if ( _features & LFNT_OT_FEATURES_P_LNUM ) { addHBFeature("+lnum"); }
1004         if ( _features & LFNT_OT_FEATURES_P_ONUM ) { addHBFeature("+onum"); }
1005         if ( _features & LFNT_OT_FEATURES_P_PNUM ) { addHBFeature("+pnum"); }
1006         if ( _features & LFNT_OT_FEATURES_P_TNUM ) { addHBFeature("+tnum"); }
1007         if ( _features & LFNT_OT_FEATURES_P_ZERO ) { addHBFeature("+zero"); }
1008         if ( _features & LFNT_OT_FEATURES_P_ORDN ) { addHBFeature("+ordn"); }
1009         if ( _features & LFNT_OT_FEATURES_P_FRAC ) { addHBFeature("+frac"); }
1010         if ( _features & LFNT_OT_FEATURES_P_AFRC ) { addHBFeature("+afrc"); }
1011         if ( _features & LFNT_OT_FEATURES_P_SMPL ) { addHBFeature("+smpl"); }
1012         if ( _features & LFNT_OT_FEATURES_P_TRAD ) { addHBFeature("+trad"); }
1013         if ( _features & LFNT_OT_FEATURES_P_FWID ) { addHBFeature("+fwid"); }
1014         if ( _features & LFNT_OT_FEATURES_P_PWID ) { addHBFeature("+pwid"); }
1015         if ( _features & LFNT_OT_FEATURES_P_JP78 ) { addHBFeature("+jp78"); }
1016         if ( _features & LFNT_OT_FEATURES_P_JP83 ) { addHBFeature("+jp83"); }
1017         if ( _features & LFNT_OT_FEATURES_P_JP04 ) { addHBFeature("+jp04"); }
1018     }
1019 }
1020 #endif
1021 
1022 bool LVFreeTypeFace::getGlyphInfo(lUInt32 code, LVFont::glyph_info_t *glyph, lChar32 def_char, lUInt32 fallbackPassMask) {
1023     //FONT_GUARD
1024     int glyph_index = getCharIndex(code, 0);
1025     if (glyph_index == 0) {
1026         LVFont *fallback = getFallbackFont(fallbackPassMask);
1027         if (NULL == fallback) {
1028             // No fallback
1029             glyph_index = getCharIndex(code, def_char);
1030             if (glyph_index == 0)
1031                 return false;
1032         } else {
1033             // Fallback
1034             lUInt32 passMask = fallbackPassMask | _fallback_mask;
1035             return fallback->getGlyphInfo(code, glyph, def_char, passMask);
1036         }
1037     }
1038     int flags = FT_LOAD_DEFAULT;
1039     flags |= (!_drawMonochrome ? FT_LOAD_TARGET_NORMAL : FT_LOAD_TARGET_MONO);
1040     if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
1041         flags |= FT_LOAD_NO_AUTOHINT;
1042     }
1043     else if (_hintingMode == HINTING_MODE_AUTOHINT) {
1044         flags |= FT_LOAD_FORCE_AUTOHINT;
1045     }
1046     else if (_hintingMode == HINTING_MODE_DISABLED) {
1047         flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
1048     }
1049     updateTransform(); // no-op
1050     int error = FT_Load_Glyph(
1051             _face,          /* handle to face object */
1052             glyph_index,   /* glyph index           */
1053             flags);  /* load flags, see below */
1054     if ( error == FT_Err_Execution_Too_Long && _hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR ) {
1055         // Native hinting bytecode may fail with some bad fonts: try again with no hinting
1056         flags |= FT_LOAD_NO_HINTING;
1057         error = FT_Load_Glyph( _face, glyph_index, flags );
1058     }
1059     if (error)
1060         return false;
1061     if (_embolden) { // Embolden so we get the real embolden metrics
1062         // See setEmbolden() for details
1063         FT_GlyphSlot_Embolden(_slot);
1064     }
1065     if (_italic == 2) {
1066         // When the font does not provide italic glyphs (_italic = 2), some fake
1067         // italic/oblique is obtained with FreeType transformation (formerly with
1068         // _matrix.xy and FT_Set_Transform(), now with FT_GlyphSlot_Oblique()).
1069         // freetype.h states about FT_Set_Transform():
1070         //     Note that this also transforms the `face.glyph.advance' field,
1071         //     but *not* the values in `face.glyph.metrics'.
1072         // So, with such fake italic, the values we'll use below are wrong,
1073         // and may cause some wrong glyphs positioning or advance.
1074         FT_GlyphSlot_Oblique(_slot); // This uses FT_Outline_Transform(), see freetype2/src/base/ftsynth.c
1075 
1076         // QT has some code that seem to fix these metrics in transformBoundingBox() at
1077         // https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/text/freetype/qfontengine_ft.cpp#n909
1078         // So let's use it:
1079         if ( _slot->format == FT_GLYPH_FORMAT_OUTLINE ) {
1080             int left   = _slot->metrics.horiBearingX;
1081             int right  = _slot->metrics.horiBearingX + _slot->metrics.width;
1082             int top    = _slot->metrics.horiBearingY;
1083             int bottom = _slot->metrics.horiBearingY - _slot->metrics.height;
1084             int l, r, t, b;
1085             FT_Vector vector;
1086             vector.x = left;
1087             vector.y = top;
1088             FT_Vector_Transform(&vector, &_matrix);
1089             l = r = vector.x;
1090             t = b = vector.y;
1091             vector.x = right;
1092             vector.y = top;
1093             FT_Vector_Transform(&vector, &_matrix);
1094             if (l > vector.x) l = vector.x;
1095             if (r < vector.x) r = vector.x;
1096             if (t < vector.y) t = vector.y;
1097             if (b > vector.y) b = vector.y;
1098             vector.x = right;
1099             vector.y = bottom;
1100             FT_Vector_Transform(&vector, &_matrix);
1101             if (l > vector.x) l = vector.x;
1102             if (r < vector.x) r = vector.x;
1103             if (t < vector.y) t = vector.y;
1104             if (b > vector.y) b = vector.y;
1105             vector.x = left;
1106             vector.y = bottom;
1107             FT_Vector_Transform(&vector, &_matrix);
1108             if (l > vector.x) l = vector.x;
1109             if (r < vector.x) r = vector.x;
1110             if (t < vector.y) t = vector.y;
1111             if (b > vector.y) b = vector.y;
1112             _slot->metrics.horiBearingX = l;
1113             _slot->metrics.horiBearingY = t;
1114             _slot->metrics.width        = r - l;
1115             _slot->metrics.height       = t - b;
1116         }
1117     }
1118 
1119     glyph->blackBoxX = (lUInt16)( FONT_METRIC_TO_PX( _slot->metrics.width ) );
1120     glyph->blackBoxY = (lUInt16)( FONT_METRIC_TO_PX( _slot->metrics.height ) );
1121     glyph->originX =   (lInt16)( FONT_METRIC_TO_PX( _slot->metrics.horiBearingX ) );
1122     glyph->originY =   (lInt16)( FONT_METRIC_TO_PX( _slot->metrics.horiBearingY ) );
1123     glyph->width =     (lUInt16)( FONT_METRIC_TO_PX( myabs(_slot->metrics.horiAdvance )) );
1124     if (glyph->blackBoxX == 0) // If a glyph has no blackbox (a spacing
1125         glyph->rsb =   0;      // character), there is no bearing
1126     else
1127         glyph->rsb =   (lInt16)(FONT_METRIC_TO_PX( (myabs(_slot->metrics.horiAdvance)
1128                                     - _slot->metrics.horiBearingX - _slot->metrics.width) ) );
1129     // printf("%c: %d + %d + %d = %d (y: %d + %d)\n", code, glyph->originX, glyph->blackBoxX,
1130     //                            glyph->rsb, glyph->width, glyph->originY, glyph->blackBoxY);
1131     // (Old) Note: these >>6 on a negative number will floor() it, so we'll get
1132     // a ceil()'ed value when considering negative numbers as some overflow,
1133     // which is good when we're using it for adding some padding.
1134     return true;
1135 }
1136 
1137 bool LVFreeTypeFace::checkFontLangCompat(const lString8 &langCode) {
1138 #define FC_LANG_START_INTERVAL_CODE     2
1139     bool fullSupport = false;
1140     bool partialSupport = false;
1141     struct fc_lang_catalog *lang_ptr = fc_lang_cat;
1142     unsigned int i;
1143     bool found = false;
1144     for (i = 0; i < fc_lang_cat_sz; i++) {
1145         if (langCode.compare(lang_ptr->lang_code) == 0) {
1146             found = true;
1147             break;
1148         }
1149         lang_ptr++;
1150     }
1151     if (found) {
1152         unsigned int codePoint = 0;
1153         unsigned int tmp;
1154         unsigned int first, second = 0;
1155         bool inRange = false;
1156         FT_UInt glyphIndex;
1157         fullSupport = true;
1158         for (i = 0;;) {
1159             // get next codePoint
1160             if (inRange && codePoint < second) {
1161                 codePoint++;
1162             } else {
1163                 if (i >= lang_ptr->char_set_sz)
1164                     break;
1165                 tmp = lang_ptr->char_set[i];
1166                 if (FC_LANG_START_INTERVAL_CODE == tmp)        // code of start interval
1167                 {
1168                     if (i + 2 < lang_ptr->char_set_sz) {
1169                         i++;
1170                         first = lang_ptr->char_set[i];
1171                         i++;
1172                         second = lang_ptr->char_set[i];
1173                         inRange = true;
1174                         codePoint = first;
1175                         i++;
1176                     } else {
1177                         // broken language char set
1178                         //qDebug() << "broken language char set";
1179                         fullSupport = false;
1180                         break;
1181                     }
1182                 } else {
1183                     codePoint = tmp;
1184                     inRange = false;
1185                     i++;
1186                 }
1187             }
1188             // check codePoint in this font
1189             glyphIndex = FT_Get_Char_Index(_face, codePoint);
1190             if (0 == glyphIndex) {
1191                 fullSupport = false;
1192             } else {
1193                 partialSupport = true;
1194             }
1195         }
1196         if (fullSupport)
1197             CRLog::debug("checkFontLangCompat(): Font have full support of language %s",
1198                          langCode.c_str());
1199         else if (partialSupport)
1200             CRLog::debug("checkFontLangCompat(): Font have partial support of language %s",
1201                          langCode.c_str());
1202         else
1203             CRLog::debug("checkFontLangCompat(): Font DON'T have support of language %s",
1204                          langCode.c_str());
1205     } else
1206         CRLog::debug("checkFontLangCompat(): Unsupported language code: %s", langCode.c_str());
1207     return fullSupport;
1208 }
1209 
1210 lUInt16 LVFreeTypeFace::measureText(const lChar32 *text,
1211                                     int len,
1212                                     lUInt16 *widths,
1213                                     lUInt8 *flags,
1214                                     int max_width,
1215                                     lChar32 def_char, TextLangCfg *lang_cfg,
1216                                     int letter_spacing,
1217                                     bool allow_hyphenation,
1218                                     lUInt32 hints, lUInt32 fallbackPassMask) {
1219     FONT_GUARD
1220     if (len <= 0 || _face == NULL)
1221         return 0;
1222     LVFont* fallbackFont = getFallbackFont(fallbackPassMask);
1223     // if this font is fallback font but already processed
1224     // delegate this function to next fallback font (if exist)
1225     if (fallbackFont != NULL && (fallbackPassMask & _fallback_mask)) {
1226         return fallbackFont->measureText(text, len, widths, flags, max_width, def_char, lang_cfg, letter_spacing, allow_hyphenation, hints, fallbackPassMask);
1227     }
1228     if (letter_spacing < 0)
1229         letter_spacing = 0;
1230     else if ( letter_spacing > MAX_LETTER_SPACING ) {
1231         letter_spacing = MAX_LETTER_SPACING;
1232     }
1233 
1234     int i;
1235 
1236     lUInt16 prev_width = 0;
1237     lUInt32 lastFitChar = 0;
1238     updateTransform();  // no-op
1239     // measure character widths
1240 
1241 #if USE_HARFBUZZ == 1
1242     if (_shapingMode == SHAPING_MODE_HARFBUZZ) {
1243         // HarfBuzz full rendering mode
1244         /** from harfbuzz/src/hb-buffer.h
1245          * hb_glyph_info_t:
1246          * @codepoint: either a Unicode code point (before shaping) or a glyph index
1247          *             (after shaping).
1248          * @cluster: the index of the character in the original text that corresponds
1249          *           to this #hb_glyph_info_t, or whatever the client passes to
1250          *           hb_buffer_add(). More than one #hb_glyph_info_t can have the same
1251          *           @cluster value, if they resulted from the same character (e.g. one
1252          *           to many glyph substitution), and when more than one character gets
1253          *           merged in the same glyph (e.g. many to one glyph substitution) the
1254          *           #hb_glyph_info_t will have the smallest cluster value of them.
1255          *           By default some characters are merged into the same cluster
1256          *           (e.g. combining marks have the same cluster as their bases)
1257          *           even if they are separate glyphs, hb_buffer_set_cluster_level()
1258          *           allow selecting more fine-grained cluster handling.
1259          */
1260         unsigned int glyph_count;
1261         hb_glyph_info_t* glyph_info = 0;
1262         hb_glyph_position_t* glyph_pos = 0;
1263         hb_buffer_clear_contents(_hb_buffer);
1264 
1265         // hb_buffer_set_replacement_codepoint(_hb_buffer, def_char);
1266         // /\ This would just set the codepoint to use when parsing
1267         // invalid utf8/16/32. As we provide codepoints, Harfbuzz
1268         // won't use it. This does NOT set the codepoint/glyph that
1269         // would be used when a glyph does not exist in that for that
1270         // codepoint. There is currently no way to specify that, and
1271         // it's always the .notdef/tofu glyph that is measured/drawn.
1272 
1273         // Fill HarfBuzz buffer
1274         // No need to call filterChar() on the input: HarfBuzz seems to do
1275         // the right thing with symbol fonts, and we'd better not replace
1276         // bullets & al unicode chars with generic equivalents, as they
1277         // may be found in the fallback font.
1278         // So, we don't, unless the current font has no fallback font,
1279         // in which case we need to get a replacement, in the worst case
1280         // def_char (?), because the glyph for 0/.notdef (tofu) has so
1281         // many different looks among fonts that it would mess the text.
1282         // We'll then get the '?' glyph of the fallback font only.
1283         // Note: not sure if Harfbuzz is able to be fine by using other
1284         // glyphs when the main codepoint does not exist by itself in
1285         // the font... in which case we'll mess things up.
1286         // todo: (if needed) might need a pre-pass in the fallback case:
1287         // full shaping without filterChar(), and if any .notdef
1288         // codepoint, re-shape with filterChar()...
1289         if ( fallbackFont != NULL ) { // It has a fallback font, add chars as-is
1290             for (i = 0; i < len; i++) {
1291                 hb_buffer_add(_hb_buffer, (hb_codepoint_t)(text[i]), i);
1292             }
1293         }
1294         else { // No fallback font, check codepoint presence or get replacement char
1295             for (i = 0; i < len; i++) {
1296                 hb_buffer_add(_hb_buffer, (hb_codepoint_t)filterChar(text[i], def_char), i);
1297             }
1298         }
1299         // Note: hb_buffer_add_codepoints(_hb_buffer, (hb_codepoint_t*)text, len, 0, len)
1300         // would do the same kind of loop we did above, so no speedup gain using it; and we
1301         // get to be sure of the cluster initial value we set to each of our added chars.
1302         hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
1303 
1304         // If we are provided with direction and hints, let harfbuzz know
1305         if ( hints ) {
1306             if ( hints & LFNT_HINT_DIRECTION_KNOWN ) {
1307                 if ( hints & LFNT_HINT_DIRECTION_IS_RTL )
1308                     hb_buffer_set_direction(_hb_buffer, HB_DIRECTION_RTL);
1309                 else
1310                     hb_buffer_set_direction(_hb_buffer, HB_DIRECTION_LTR);
1311             }
1312             int hb_flags = HB_BUFFER_FLAG_DEFAULT; // (hb_buffer_flags_t won't let us do |= )
1313             if ( hints & LFNT_HINT_BEGINS_PARAGRAPH )
1314                 hb_flags |= HB_BUFFER_FLAG_BOT;
1315             if ( hints & LFNT_HINT_ENDS_PARAGRAPH )
1316                 hb_flags |= HB_BUFFER_FLAG_EOT;
1317             hb_buffer_set_flags(_hb_buffer, (hb_buffer_flags_t)hb_flags);
1318         }
1319         if ( lang_cfg ) {
1320             hb_buffer_set_language(_hb_buffer, lang_cfg->getHBLanguage());
1321         }
1322         // Let HB guess what's not been set (script, direction, language)
1323         hb_buffer_guess_segment_properties(_hb_buffer);
1324 
1325         // Some additional care might need to be taken, see:
1326         //   https://www.w3.org/TR/css-text-3/#letter-spacing-property
1327         if ( letter_spacing > 0 ) {
1328             // Don't apply letter-spacing if the script is cursive
1329             hb_script_t script = hb_buffer_get_script(_hb_buffer);
1330             if ( isHBScriptCursive(script) )
1331                 letter_spacing = 0;
1332         }
1333         // todo: if letter_spacing, ligatures should be disabled (-liga, -clig)
1334         // todo: letter-spacing must not be applied at the beginning or at the end of a line
1335         // todo: it should be applied half-before/half-after each grapheme
1336         // cf in *some* minikin repositories: libs/minikin/Layout.cpp
1337 
1338         // Shape
1339         hb_shape(_hb_font, _hb_buffer, _hb_features.ptr(), _hb_features.length());
1340 
1341         // Harfbuzz has guessed and set a direction even if we did not provide one.
1342         bool is_rtl = false;
1343         if ( hb_buffer_get_direction(_hb_buffer) == HB_DIRECTION_RTL ) {
1344             is_rtl = true;
1345             // "For buffers in the right-to-left (RTL) or bottom-to-top (BTT) text
1346             // flow direction, the directionality of the buffer itself is reversed
1347             // for final output as a matter of design. Therefore, HarfBuzz inverts
1348             // the monotonic property: client programs are guaranteed that
1349             // monotonically increasing initial cluster values will be returned as
1350             // monotonically decreasing final cluster values."
1351             // hb_buffer_reverse_clusters() puts the advance on the last char of a
1352             // cluster, unlike hb_buffer_reverse() which puts it on the first, which
1353             // looks more natural (like it happens when LTR).
1354             // But hb_buffer_reverse_clusters() is required to have the clusters
1355             // ordered as our text indices, so we can map them back to our text.
1356             hb_buffer_reverse_clusters(_hb_buffer);
1357         }
1358 
1359         glyph_count = hb_buffer_get_length(_hb_buffer);
1360         glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0);
1361         glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0);
1362 
1363         #ifdef DEBUG_MEASURE_TEXT
1364             printf("MTHB >>> measureText %x len %d is_rtl=%d [%s]\n", text, len, is_rtl, _faceName.c_str());
1365             for (i = 0; i < (int)glyph_count; i++) {
1366                 char glyphname[32];
1367                 hb_font_get_glyph_name(_hb_font, glyph_info[i].codepoint, glyphname, sizeof(glyphname));
1368                 printf("MTHB g%d c%d(=t:%x) [%x %s]\tadvance=(%d,%d)", i, glyph_info[i].cluster,
1369                             text[glyph_info[i].cluster], glyph_info[i].codepoint, glyphname,
1370                             FONT_METRIC_TO_PX(glyph_pos[i].x_advance), FONT_METRIC_TO_PX(glyph_pos[i].y_advance)
1371                             );
1372                 if (glyph_pos[i].x_offset || glyph_pos[i].y_offset)
1373                     printf("\toffset=(%d,%d)", FONT_METRIC_TO_PX(glyph_pos[i].x_offset),
1374                                                FONT_METRIC_TO_PX(glyph_pos[i].y_offset));
1375                 printf("\n");
1376             }
1377             printf("MTHB ---\n");
1378         #endif
1379 
1380         // We need to set widths and flags on our original text.
1381         // hb_shape has modified buffer to contain glyphs, and text
1382         // and buffer may desync (because of clusters, ligatures...)
1383         // in both directions in a same run.
1384         // Also, a cluster must not be cut, so we want to set the same
1385         // width to all our original text chars that are part of the
1386         // same cluster (so 2nd+ chars in a cluster, will get a 0-width,
1387         // and, when splitting lines, will fit in a word with the first
1388         // char).
1389         // So run along our original text (chars, t), and try to follow
1390         // harfbuzz buffer (glyphs, hg), putting the advance of all
1391         // the glyphs that belong to the same cluster (hcl) on the
1392         // first char that started that cluster (and 0-width on the
1393         // followup chars).
1394         // It looks like Harfbuzz makes a cluster of combined glyphs
1395         // even when the font does not have any or all of the required
1396         // glyphs:
1397         // When meeting a not-found glyph (codepoint=0, name=".notdef"),
1398         // we record the original starting t of that cluster, and
1399         // keep processing (possibly other chars with .notdef glyphs,
1400         // giving them the width of the 'tofu' char), until we meet a char
1401         // with a found glyph. We then hold on on this one, while we go
1402         // measureText() the previous segment of text (that got .notdef
1403         // glyphs) with the fallback font, and update the wrongs width
1404         // and flags.
1405 
1406         int prev_width = 0;
1407         int cur_width = 0;
1408         int cur_cluster = 0;
1409         unsigned int hg = 0;  // index in glyph_info/glyph_pos
1410         int hcl = 0; // cluster glyph at hg
1411         int t_notdef_start = -1;
1412         int t_notdef_end = -1;
1413         for (int t = 0; t < len; t++) {
1414             #ifdef DEBUG_MEASURE_TEXT
1415                 printf("MTHB t%d (=%x) ", t, text[t]);
1416             #endif
1417             // Grab all glyphs that do not belong to a cluster greater that our char position
1418             while ( hg < glyph_count ) {
1419                 hcl = glyph_info[hg].cluster;
1420                 if (hcl <= t) {
1421                     int advance = 0;
1422                     if ( glyph_info[hg].codepoint != 0 ) { // Codepoint found in this font
1423                         #ifdef DEBUG_MEASURE_TEXT
1424                             printf("(found cp=%x) ", glyph_info[hg].codepoint);
1425                         #endif
1426                         // Only process past notdef when the first glyph of a cluster is found.
1427                         // (It could happen that a cluster of 2 glyphs has its 1st one notdef
1428                         // while the 2nd one has a valid codepoint: we'll have to reprocess the
1429                         // whole cluster with the fallback font. If the 1st glyph is found but
1430                         // the 2nd is notdef, we'll process past notdef with the fallback font
1431                         // now, but we'll be processing this whole cluster with the fallback
1432                         // font when a later valid codepoint is found).
1433                         if ( t_notdef_start >= 0 && hcl > cur_cluster ) {
1434                             // We have a segment of previous ".notdef", and this glyph starts a new cluster
1435                             t_notdef_end = t;
1436                             // The code ensures the main fallback font has no fallback font
1437                             if ( fallbackFont != NULL ) {
1438                                 // Let the fallback font replace the wrong values in widths and flags
1439                                 #ifdef DEBUG_MEASURE_TEXT
1440                                     printf("[...]\nMTHB ### measuring past failures with fallback font %d>%d\n",
1441                                                             t_notdef_start, t_notdef_end);
1442                                 #endif
1443                                 // Drop BOT/EOT flags if this segment is not at start/end
1444                                 lUInt32 fb_hints = hints;
1445                                 if ( t_notdef_start > 0 )
1446                                     fb_hints &= ~LFNT_HINT_BEGINS_PARAGRAPH;
1447                                 if ( t_notdef_end < len )
1448                                     fb_hints &= ~LFNT_HINT_ENDS_PARAGRAPH;
1449                                 fallbackFont->measureText( text + t_notdef_start, t_notdef_end - t_notdef_start,
1450                                                 widths + t_notdef_start, flags + t_notdef_start,
1451                                                 max_width, def_char, lang_cfg, letter_spacing, allow_hyphenation,
1452                                                 fb_hints, fallbackPassMask | _fallback_mask );
1453                                 // Fix previous bad measurements
1454                                 int last_good_width = t_notdef_start > 0 ? widths[t_notdef_start-1] : 0;
1455                                 for (int tn = t_notdef_start; tn < t_notdef_end; tn++) {
1456                                     widths[tn] += last_good_width;
1457                                 }
1458                                 // And fix our current width
1459                                 cur_width = widths[t_notdef_end-1];
1460                                 prev_width = cur_width;
1461                                 #ifdef DEBUG_MEASURE_TEXT
1462                                     printf("MTHB ### measured past failures > W= %d\n[...]", cur_width);
1463                                 #endif
1464                             } else {
1465                                 // No fallback font: stay with what's been measured: the notdef/tofu char
1466                                 #ifdef DEBUG_MEASURE_TEXT
1467                                     printf("[...]\nMTHB no fallback font to measure past failures, keeping def_char\nMTHB [...]");
1468                                 #endif
1469                             }
1470                             t_notdef_start = -1;
1471                             // And go on with the found glyph now that we fixed what was before
1472                         }
1473                         // Glyph found in this font
1474                         advance = FONT_METRIC_TO_PX(glyph_pos[hg].x_advance);
1475                     } else {
1476                         #ifdef DEBUG_MEASURE_TEXT
1477                             printf("(glyph not found) ");
1478                         #endif
1479                         // Keep the advance of .notdef/tofu in case there is no fallback font to correct them
1480                         advance = FONT_METRIC_TO_PX(glyph_pos[hg].x_advance);
1481                         if ( t_notdef_start < 0 ) {
1482                             t_notdef_start = t;
1483                         }
1484                     }
1485                     #ifdef DEBUG_MEASURE_TEXT
1486                         printf("c%d+%d ", hcl, advance);
1487                     #endif
1488                     cur_width += advance;
1489                     cur_cluster = hcl;
1490                     hg++;
1491                     continue; // keep grabbing glyphs
1492                 }
1493                 break;
1494             }
1495             // Done grabbing clustered glyphs: they contributed to cur_width.
1496             // All 't' lower than the next cluster will have that same cur_width.
1497             if (cur_cluster < t) {
1498                 // Our char is part of a cluster that started on a previous char
1499                 flags[t] = LCHAR_IS_CLUSTER_TAIL;
1500                 // todo: see at using HB_GLYPH_FLAG_UNSAFE_TO_BREAK to
1501                 // set this flag instead/additionally
1502             }
1503             else {
1504                 // We're either a single char cluster, or the start
1505                 // of a multi chars cluster.
1506                 flags[t] = GET_CHAR_FLAGS(text[t]);
1507                 // It seems each soft-hyphen is in its own cluster, of length 1 and width 0,
1508                 // so HarfBuzz must already deal correctly with soft-hyphens.
1509                 if (cur_width == prev_width) {
1510                     // But if there is no advance (this happens with soft-hyphens),
1511                     // flag it and don't add any letter spacing.
1512                     flags[t] |= LCHAR_IS_CLUSTER_TAIL;
1513                 }
1514                 else {
1515                     cur_width += letter_spacing; // only between clusters/graphemes
1516                 }
1517             }
1518             widths[t] = cur_width;
1519             #ifdef DEBUG_MEASURE_TEXT
1520                 printf("=> %d (flags=%d) => W=%d\n", cur_width - prev_width, flags[t], cur_width);
1521             #endif
1522             prev_width = cur_width;
1523 
1524             // (Not sure about how that max_width limit could play and if it could mess things)
1525             if (cur_width > max_width) {
1526                 if (lastFitChar < hcl + 7)
1527                     break;
1528             }
1529             else {
1530                 lastFitChar = t+1;
1531             }
1532         } // process next char t
1533 
1534         // Process .notdef glyphs at end of text (same logic as above)
1535         if ( t_notdef_start >= 0 ) {
1536             t_notdef_end = len;
1537             if ( fallbackFont != NULL ) {
1538                 #ifdef DEBUG_MEASURE_TEXT
1539                     printf("[...]\nMTHB ### measuring past failures at EOT with fallback font %d>%d\n",
1540                                             t_notdef_start, t_notdef_end);
1541                 #endif
1542                 // Drop BOT flag if this segment is not at start (it is at end)
1543                 lUInt32 fb_hints = hints;
1544                 if ( t_notdef_start > 0 )
1545                     fb_hints &= ~LFNT_HINT_BEGINS_PARAGRAPH;
1546                 int chars_measured = fallbackFont->measureText( text + t_notdef_start, // start
1547                                 t_notdef_end - t_notdef_start, // len
1548                                 widths + t_notdef_start, flags + t_notdef_start,
1549                                 max_width, def_char, lang_cfg, letter_spacing, allow_hyphenation,
1550                                 fb_hints, fallbackPassMask | _fallback_mask );
1551                 lastFitChar = t_notdef_start + chars_measured;
1552                 int last_good_width = t_notdef_start > 0 ? widths[t_notdef_start-1] : 0;
1553                 for (int tn = t_notdef_start; tn < t_notdef_end; tn++) {
1554                     widths[tn] += last_good_width;
1555                 }
1556                 // And add all that to our current width
1557                 cur_width = widths[t_notdef_end-1];
1558                 #ifdef DEBUG_MEASURE_TEXT
1559                     printf("MTHB ### measured past failures at EOT > W= %d\n[...]", cur_width);
1560                 #endif
1561             }
1562             else {
1563                 #ifdef DEBUG_MEASURE_TEXT
1564                     printf("[...]\nMTHB no fallback font to measure past failures at EOT, keeping def_char\nMTHB [...]");
1565                 #endif
1566             }
1567         }
1568 
1569         // i is used below to "fill props for rest of chars", so make it accurate
1570         i = len; // actually make it do nothing
1571 
1572         #ifdef DEBUG_MEASURE_TEXT
1573             printf("MTHB <<< W=%d [%s]\n", cur_width, _faceName.c_str());
1574             printf("MTHB dwidths[]: ");
1575             for (int t = 0; t < len; t++)
1576                 printf("%d:%d ", t, widths[t] - (t>0?widths[t-1]:0));
1577             printf("\n");
1578         #endif
1579     } else if (_shapingMode == SHAPING_MODE_HARFBUZZ_LIGHT) {
1580         // HarfBuzz light rendering mode
1581         struct LVCharTriplet triplet;
1582         struct LVCharPosInfo posInfo;
1583         triplet.Char = 0;
1584         for ( i=0; i<len; i++) {
1585             lChar32 ch = text[i];
1586             bool isHyphen = (ch==UNICODE_SOFT_HYPHEN_CODE);
1587             if (isHyphen) {
1588                 // do just what would be done below if zero width (no change
1589                 // in prev_width), and don't get involved in kerning
1590                 flags[i] = 0; // no LCHAR_ALLOW_WRAP_AFTER, will be dealt with by hyphenate()
1591                 widths[i] = prev_width;
1592                 lastFitChar = i + 1;
1593                 continue;
1594             }
1595             flags[i] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch );
1596             triplet.prevChar = triplet.Char;
1597             triplet.Char = ch;
1598             if (i < len - 1)
1599                 triplet.nextChar = text[i + 1];
1600             else
1601                 triplet.nextChar = 0;
1602             if (!_width_cache2.get(triplet, posInfo)) {
1603                 if (hbCalcCharWidth(&posInfo, triplet, def_char, fallbackPassMask))
1604                     _width_cache2.set(triplet, posInfo);
1605                 else { // (seems this never happens, unlike with kerning disabled)
1606                     widths[i] = prev_width;
1607                     lastFitChar = i + 1;
1608                     continue;  /* ignore errors */
1609                 }
1610             }
1611             widths[i] = prev_width + posInfo.width;
1612             if ( posInfo.width == 0 ) {
1613                 // Assume zero advance means it's a diacritic, and we should not apply
1614                 // any letter spacing on this char (now, and when justifying)
1615                 flags[i] |= LCHAR_IS_CLUSTER_TAIL;
1616             }
1617             else {
1618                 widths[i] += letter_spacing;
1619             }
1620             if ( !isHyphen ) // avoid soft hyphens inside text string
1621                 prev_width = widths[i];
1622             if ( prev_width > max_width ) {
1623                 if ( lastFitChar < i + 7)
1624                     break;
1625             }
1626             else {
1627                 lastFitChar = i + 1;
1628             }
1629         }
1630     } else {
1631 #endif   // USE_HARFBUZZ==1
1632     // Simple FreeType text rendering
1633     FT_UInt previous = 0;
1634     int error;
1635 #if (ALLOW_KERNING==1)
1636     int use_kerning = _allowKerning && FT_HAS_KERNING( _face );
1637 #endif
1638     for ( i=0; i<len; i++) {
1639         lChar32 ch = text[i];
1640         bool isHyphen = (ch==UNICODE_SOFT_HYPHEN_CODE);
1641         if (isHyphen) {
1642             // do just what would be done below if zero width (no change
1643             // in prev_width), and don't get involved in kerning
1644             flags[i] = 0; // no LCHAR_ALLOW_WRAP_AFTER, will be dealt with by hyphenate()
1645             widths[i] = prev_width;
1646             lastFitChar = i + 1;
1647             continue;
1648         }
1649         FT_UInt ch_glyph_index = (FT_UInt)-1;
1650         int kerning = 0;
1651 #if (ALLOW_KERNING==1)
1652         if ( use_kerning && previous>0  ) {
1653             if ( ch_glyph_index==(FT_UInt)-1 )
1654                 ch_glyph_index = getCharIndex( ch, def_char );
1655             if ( ch_glyph_index != 0 ) {
1656                 FT_Vector delta;
1657                 error = FT_Get_Kerning( _face,          /* handle to face object */
1658                                         previous,          /* left glyph index      */
1659                                         ch_glyph_index,         /* right glyph index     */
1660                                         FT_KERNING_DEFAULT,  /* kerning mode          */
1661                                         &delta );    /* target vector         */
1662                 if ( !error )
1663                     kerning = delta.x;
1664             }
1665         }
1666 #endif
1667         flags[i] = GET_CHAR_FLAGS(ch); //calcCharFlags( ch );
1668 
1669         /* load glyph image into the slot (erase previous one) */
1670         int w = _wcache.get(ch);
1671         if ( w == CACHED_UNSIGNED_METRIC_NOT_SET ) {
1672             glyph_info_t glyph;
1673             if ( getGlyphInfo( ch, &glyph, def_char, fallbackPassMask ) ) {
1674                 w = glyph.width;
1675                 _wcache.put(ch, w);
1676             } else {
1677                 widths[i] = prev_width;
1678                 lastFitChar = i + 1;
1679                 continue;  /* ignore errors */
1680             }
1681         }
1682         if ( use_kerning ) {
1683             if ( ch_glyph_index==(FT_UInt)-1 )
1684                 ch_glyph_index = getCharIndex( ch, 0 );
1685             previous = ch_glyph_index;
1686         }
1687         widths[i] = prev_width + w + FONT_METRIC_TO_PX(kerning);
1688         if ( w == 0 ) {
1689             // Assume zero advance means it's a diacritic, and we should not apply
1690             // any letter spacing on this char (now, and when justifying)
1691             flags[i] |= LCHAR_IS_CLUSTER_TAIL;
1692         }
1693         else {
1694             widths[i] += letter_spacing;
1695         }
1696         if ( !isHyphen ) // avoid soft hyphens inside text string
1697             prev_width = widths[i];
1698         if ( prev_width > max_width ) {
1699             if ( lastFitChar < i + 7)
1700                 break;
1701         } else {
1702             lastFitChar = i + 1;
1703         }
1704     }
1705 
1706 #if USE_HARFBUZZ==1
1707     } // else fallback to the non harfbuzz code
1708 #endif
1709 
1710 
1711     // fill props for rest of chars
1712     for (int ii = i; ii < len; ii++) {
1713         flags[ii] = GET_CHAR_FLAGS(text[ii]);
1714     }
1715 
1716     //maxFit = i;
1717 
1718     // find last word
1719     if (allow_hyphenation) {
1720         if (!_hyphen_width)
1721             _hyphen_width = getCharWidth(UNICODE_SOFT_HYPHEN_CODE);
1722         if (lastFitChar > 3) {
1723             int hwStart, hwEnd;
1724             lStr_findWordBounds(text, len, lastFitChar - 1, hwStart, hwEnd);
1725             if (hwStart < (int) (lastFitChar - 1) && hwEnd > hwStart + 3) {
1726                 //int maxw = max_width - (hwStart>0 ? widths[hwStart-1] : 0);
1727                 if ( lang_cfg )
1728                     lang_cfg->getHyphMethod()->hyphenate(text+hwStart, hwEnd-hwStart, widths+hwStart, flags+hwStart, _hyphen_width, max_width);
1729                 else // Use global lang hyph method
1730                     HyphMan::hyphenate(text+hwStart, hwEnd-hwStart, widths+hwStart, flags+hwStart, _hyphen_width, max_width);
1731             }
1732         }
1733     }
1734     return lastFitChar; //i;
1735 }
1736 
1737 lUInt32 LVFreeTypeFace::getTextWidth(const lChar32 *text, int len, TextLangCfg *lang_cfg) {
1738     static lUInt16 widths[MAX_LINE_CHARS + 1];
1739     static lUInt8 flags[MAX_LINE_CHARS + 1];
1740     if (len > MAX_LINE_CHARS)
1741         len = MAX_LINE_CHARS;
1742     if (len <= 0)
1743         return 0;
1744     lUInt16 res = measureText(
1745             text, len,
1746             widths,
1747             flags,
1748             MAX_LINE_WIDTH,
1749             U' ',  // def_char
1750             lang_cfg,
1751             0
1752     );
1753     if (res > 0 && res < MAX_LINE_CHARS)
1754         return widths[res - 1];
1755     return 0;
1756 }
1757 
1758 void LVFreeTypeFace::updateTransform() {
1759     //        static void * transformOwner = NULL;
1760     //        if ( transformOwner!=this ) {
1761     //            FT_Set_Transform(_face, &_matrix, NULL);
1762     //            transformOwner = this;
1763     //        }
1764 }
1765 
1766 LVFontGlyphCacheItem *LVFreeTypeFace::getGlyph(lUInt32 ch, lChar32 def_char, lUInt32 fallbackPassMask) {
1767     //FONT_GUARD
1768     FT_UInt ch_glyph_index = getCharIndex(ch, 0);
1769     if (ch_glyph_index == 0) {
1770         LVFont *fallback = getFallbackFont(fallbackPassMask);
1771         if (NULL == fallback) {
1772             // No fallback
1773             ch_glyph_index = getCharIndex(ch, def_char);
1774             if (ch_glyph_index == 0)
1775                 return NULL;
1776         } else {
1777             // Fallback
1778             return fallback->getGlyph(ch, def_char, fallbackPassMask | _fallback_mask );
1779         }
1780     }
1781     LVFontGlyphCacheItem *item = _glyph_cache.get(ch);
1782     if (!item) {
1783         int rend_flags = FT_LOAD_RENDER | (!_drawMonochrome ? FT_LOAD_TARGET_LIGHT
1784                                                             : (FT_LOAD_TARGET_MONO)); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT
1785         if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
1786             rend_flags |= FT_LOAD_NO_AUTOHINT;
1787         } else if (_hintingMode == HINTING_MODE_AUTOHINT) {
1788             rend_flags |= FT_LOAD_FORCE_AUTOHINT;
1789         } else if (_hintingMode == HINTING_MODE_DISABLED) {
1790             rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
1791         }
1792         if (_embolden || _italic == 2) { // Don't render yet
1793             rend_flags &= ~FT_LOAD_RENDER;
1794             // Also disable any hinting, as it would be wrong after embolden
1795             rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
1796         }
1797         /* load glyph image into the slot (erase previous one) */
1798         updateTransform(); // no-op
1799         int error = FT_Load_Glyph( _face, /* handle to face object */
1800                 ch_glyph_index,           /* glyph index           */
1801                 rend_flags );             /* load flags, see below */
1802         if ( error == FT_Err_Execution_Too_Long && _hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR ) {
1803             // Native hinting bytecode may fail with some bad fonts: try again with no hinting
1804             rend_flags |= FT_LOAD_NO_HINTING;
1805             error = FT_Load_Glyph( _face, ch_glyph_index, rend_flags );
1806         }
1807         if ( error ) {
1808             return NULL;  /* ignore errors */
1809         }
1810 
1811         if (_embolden) {
1812             FT_GlyphSlot_Embolden(_slot); // See setEmbolden() for details
1813         }
1814         if (_italic == 2) {
1815             FT_GlyphSlot_Oblique(_slot);
1816         }
1817         if (_embolden || _italic==2) {
1818             // Render now that transformations are applied
1819             FT_Render_Glyph(_slot, _drawMonochrome?FT_RENDER_MODE_MONO:FT_RENDER_MODE_LIGHT);
1820         }
1821 
1822         item = newItem(&_glyph_cache, (lChar32)ch, _slot); //, _drawMonochrome
1823         if (item)
1824             _glyph_cache.put(item);
1825     }
1826     return item;
1827 }
1828 
1829 #if USE_HARFBUZZ == 1
1830 
1831 LVFontGlyphCacheItem* LVFreeTypeFace::getGlyphByIndex(lUInt32 index) {
1832     //FONT_GUARD
1833     LVFontGlyphCacheItem *item = _glyph_cache2.get(index);
1834     if (!item) {
1835         // glyph not found in cache, rendering...
1836         int rend_flags = FT_LOAD_RENDER | (!_drawMonochrome ? FT_LOAD_TARGET_LIGHT
1837                                                             : (FT_LOAD_TARGET_MONO)); //|FT_LOAD_MONOCHROME|FT_LOAD_FORCE_AUTOHINT
1838         if (_hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR) {
1839             rend_flags |= FT_LOAD_NO_AUTOHINT;
1840         }
1841         else if (_hintingMode == HINTING_MODE_AUTOHINT) {
1842             rend_flags |= FT_LOAD_FORCE_AUTOHINT;
1843         }
1844         else if (_hintingMode == HINTING_MODE_DISABLED) {
1845             rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
1846         }
1847 
1848         if (_embolden || _italic == 2) { // Don't render yet
1849             rend_flags &= ~FT_LOAD_RENDER;
1850             // Also disable any hinting, as it would be wrong after embolden
1851             rend_flags |= FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
1852         }
1853 
1854         /* load glyph image into the slot (erase previous one) */
1855         updateTransform(); // no-op
1856         int error = FT_Load_Glyph( _face, /* handle to face object */
1857                 index,                    /* glyph index           */
1858                 rend_flags );             /* load flags, see below */
1859         if ( error == FT_Err_Execution_Too_Long && _hintingMode == HINTING_MODE_BYTECODE_INTERPRETOR ) {
1860             // Native hinting bytecode may fail with some bad fonts: try again with no hinting
1861             rend_flags |= FT_LOAD_NO_HINTING;
1862             error = FT_Load_Glyph( _face, index, rend_flags );
1863         }
1864         if ( error ) {
1865             return NULL;  /* ignore errors */
1866         }
1867 
1868         if (_embolden) {
1869             if ( _slot->format == FT_GLYPH_FORMAT_OUTLINE ) {
1870                 // See setEmbolden() for details
1871                 FT_Outline_Embolden(&_slot->outline, 2*_embolden_half_strength);
1872                 FT_Outline_Translate(&_slot->outline, -_embolden_half_strength, -_embolden_half_strength);
1873             }
1874         }
1875         if (_italic==2) {
1876             FT_GlyphSlot_Oblique(_slot);
1877         }
1878         if (_embolden || _italic==2) {
1879             // Render now that transformations are applied
1880             FT_Render_Glyph(_slot, _drawMonochrome?FT_RENDER_MODE_MONO:FT_RENDER_MODE_LIGHT);
1881         }
1882 
1883         item = newItem(&_glyph_cache2, index, _slot);
1884         if (item)
1885             _glyph_cache2.put(item);
1886     }
1887     return item;
1888 }
1889 
1890 #endif  // USE_HARFBUZZ==1
1891 
1892 int LVFreeTypeFace::getCharWidth(lChar32 ch, lChar32 def_char) {
1893     lUInt16 w = _wcache.get(ch);
1894     if (w == CACHED_UNSIGNED_METRIC_NOT_SET) {
1895         glyph_info_t glyph;
1896         if (getGlyphInfo(ch, &glyph, def_char)) {
1897             w = glyph.width;
1898         } else {
1899             w = 0;
1900         }
1901         _wcache.put(ch, w);
1902     }
1903     return (int) w;
1904 }
1905 
1906 int LVFreeTypeFace::getLeftSideBearing( lChar32 ch, bool negative_only, bool italic_only )
1907 {
1908     if ( italic_only && !getItalic() )
1909         return 0;
1910     lInt16 b = _lsbcache.get(ch);
1911     if ( b == CACHED_SIGNED_METRIC_NOT_SET ) {
1912         glyph_info_t glyph;
1913         if ( getGlyphInfo( ch, &glyph, '?' ) ) {
1914             b = glyph.originX;
1915         }
1916         else {
1917             b = 0;
1918         }
1919         _lsbcache.put(ch, b);
1920     }
1921     if (negative_only && b >= 0)
1922         return 0;
1923     return (int) b;
1924 }
1925 
1926 int LVFreeTypeFace::getRightSideBearing( lChar32 ch, bool negative_only, bool italic_only )
1927 {
1928     if ( italic_only && !getItalic() )
1929         return 0;
1930     lInt16 b = _rsbcache.get(ch);
1931     if ( b == CACHED_SIGNED_METRIC_NOT_SET ) {
1932         glyph_info_t glyph;
1933         if ( getGlyphInfo( ch, &glyph, '?' ) ) {
1934             b = glyph.rsb;
1935         }
1936         else {
1937             b = 0;
1938         }
1939         _rsbcache.put(ch, b);
1940     }
1941     if (negative_only && b >= 0)
1942         return 0;
1943     return (int) b;
1944 }
1945 
1946 int LVFreeTypeFace::DrawTextString(LVDrawBuf *buf, int x, int y, const lChar32 *text, int len,
1947                                    lChar32 def_char, lUInt32 *palette, bool addHyphen, TextLangCfg *lang_cfg,
1948                                     lUInt32 flags, int letter_spacing, int width, int text_decoration_back_gap, lUInt32 fallbackPassMask) {
1949     FONT_GUARD
1950     if (len <= 0 || _face == NULL)
1951         return 0;
1952     LVFont* fallbackFont = getFallbackFont(fallbackPassMask);
1953     // if this font is fallback font and already processed
1954     // delegate this function to next fallback font (if exist)
1955     if (fallbackFont != NULL && (fallbackPassMask & _fallback_mask))
1956         return fallbackFont->DrawTextString(buf, x, y, text, len, def_char, palette, addHyphen, lang_cfg, flags, letter_spacing, width, text_decoration_back_gap, fallbackPassMask);
1957     if ( letter_spacing < 0 ) {
1958         letter_spacing = 0;
1959     }
1960     else if ( letter_spacing > MAX_LETTER_SPACING ) {
1961         letter_spacing = MAX_LETTER_SPACING;
1962     }
1963     lvRect clip;
1964     buf->GetClipRect(&clip);
1965     updateTransform(); // no-op
1966     if (y + _height < clip.top || y >= clip.bottom)
1967         return 0;
1968 
1969     unsigned int i;
1970     //lUInt16 prev_width = 0;
1971     lChar32 ch;
1972     // measure character widths
1973     bool isHyphen = false;
1974     int x0 = x;
1975 #if USE_HARFBUZZ == 1
1976     if (_shapingMode == SHAPING_MODE_HARFBUZZ) {
1977         // Full HarfBuzz text shaping
1978         int w;
1979         // See measureText() for more comments on how to work with Harfbuzz,
1980         // as we do and must work the same way here.
1981         unsigned int glyph_count;
1982         hb_glyph_info_t *glyph_info = 0;
1983         hb_glyph_position_t *glyph_pos = 0;
1984         hb_buffer_clear_contents(_hb_buffer);
1985         // Fill HarfBuzz buffer
1986         if ( fallbackFont ) { // It has a fallback font, add chars as-is
1987             for (i = 0; i < len; i++) {
1988                 hb_buffer_add(_hb_buffer, (hb_codepoint_t)(text[i]), i);
1989             }
1990         }
1991         else { // No fallback font, check codepoint presence or get replacement char
1992             for (i = 0; i < len; i++) {
1993                 hb_buffer_add(_hb_buffer, (hb_codepoint_t)filterChar(text[i], def_char), i);
1994             }
1995         }
1996         hb_buffer_set_content_type(_hb_buffer, HB_BUFFER_CONTENT_TYPE_UNICODE);
1997 
1998         // If we are provided with direction and hints, let harfbuzz know
1999         if ( flags ) {
2000             if ( flags & LFNT_HINT_DIRECTION_KNOWN ) {
2001                 // Trust direction decided by fribidi: if we made a word containing just '(',
2002                 // harfbuzz wouldn't be able to determine its direction and would render
2003                 // it LTR - while it could be in some RTL text and needs to be mirrored.
2004                 if ( flags & LFNT_HINT_DIRECTION_IS_RTL )
2005                     hb_buffer_set_direction(_hb_buffer, HB_DIRECTION_RTL);
2006                 else
2007                     hb_buffer_set_direction(_hb_buffer, HB_DIRECTION_LTR);
2008             }
2009             int hb_flags = HB_BUFFER_FLAG_DEFAULT; // (hb_buffer_flags_t won't let us do |= )
2010             if ( flags & LFNT_HINT_BEGINS_PARAGRAPH )
2011                 hb_flags |= HB_BUFFER_FLAG_BOT;
2012             if ( flags & LFNT_HINT_ENDS_PARAGRAPH )
2013                 hb_flags |= HB_BUFFER_FLAG_EOT;
2014             hb_buffer_set_flags(_hb_buffer, (hb_buffer_flags_t)hb_flags);
2015         }
2016         if ( lang_cfg )
2017             hb_buffer_set_language(_hb_buffer, lang_cfg->getHBLanguage());
2018         // Let HB guess what's not been set (script, direction, language)
2019         hb_buffer_guess_segment_properties(_hb_buffer);
2020 
2021         // See measureText() for details
2022         if ( letter_spacing > 0 ) {
2023             // Don't apply letter-spacing if the script is cursive
2024             hb_script_t script = hb_buffer_get_script(_hb_buffer);
2025             if ( isHBScriptCursive(script) )
2026                 letter_spacing = 0;
2027         }
2028 
2029         // Shape
2030         hb_shape(_hb_font, _hb_buffer, _hb_features.ptr(), _hb_features.length());
2031 
2032         // If direction is RTL, hb_shape() has reversed the order of the glyphs, so
2033         // they are in visual order and ready to be iterated and drawn. So,
2034         // we do not revert them, unlike in measureText().
2035         bool is_rtl = hb_buffer_get_direction(_hb_buffer) == HB_DIRECTION_RTL;
2036 
2037         glyph_count = hb_buffer_get_length(_hb_buffer);
2038         glyph_info = hb_buffer_get_glyph_infos(_hb_buffer, 0);
2039         glyph_pos = hb_buffer_get_glyph_positions(_hb_buffer, 0);
2040 
2041         #ifdef DEBUG_DRAW_TEXT
2042             printf("DTHB >>> drawTextString %x len %d is_rtl=%d [%s]\n", text, len, is_rtl, _faceName.c_str());
2043             for (i = 0; i < (int)glyph_count; i++) {
2044                 char glyphname[32];
2045                 hb_font_get_glyph_name(_hb_font, glyph_info[i].codepoint, glyphname, sizeof(glyphname));
2046                 printf("DTHB g%d c%d(=t:%x) [%x %s]\tadvance=(%d,%d)", i, glyph_info[i].cluster,
2047                             text[glyph_info[i].cluster], glyph_info[i].codepoint, glyphname,
2048                             FONT_METRIC_TO_PX(glyph_pos[i].x_advance), FONT_METRIC_TO_PX(glyph_pos[i].y_advance));
2049                 if (glyph_pos[i].x_offset || glyph_pos[i].y_offset)
2050                     printf("\toffset=(%d,%d)", FONT_METRIC_TO_PX(glyph_pos[i].x_offset),
2051                                                FONT_METRIC_TO_PX(glyph_pos[i].y_offset));
2052                 printf("\n");
2053             }
2054             printf("DTHB ---\n");
2055         #endif
2056 
2057         // We want to do just like in measureText(): drawing found glyphs with
2058         // this font, and .notdef glyphs with the fallback font, as a single segment,
2059         // once a defined glyph is found, before drawing that defined glyph.
2060         // The code is different from in measureText(), as the glyphs might be
2061         // inverted for RTL drawing, and we can't uninvert them. We also loop
2062         // thru glyphs here rather than chars.
2063 
2064         // Cluster numbers may increase or decrease (if RTL) while we walk the glyphs.
2065         // We'll update fallback drawing text indices as we walk glyphs and cluster
2066         // (cluster numbers are boundaries in text indices, but it's quite tricky
2067         // to get right).
2068         int fb_t_start = 0;
2069         int fb_t_end = len;
2070         int hg = 0;  // index in glyph_info/glyph_pos
2071         while (hg < glyph_count) { // hg is the start of a new cluster at this point
2072             bool draw_with_fallback = false;
2073             int hcl = glyph_info[hg].cluster;
2074             fb_t_start = hcl; // if fb drawing needed from this glyph: t[hcl:..]
2075                 // /\ Logical if !is_rtl, but also needed if is_rtl and immediately
2076                 // followed by a found glyph (so, a single glyph to draw with the
2077                 // fallback font): = hclbad
2078             #ifdef DEBUG_DRAW_TEXT
2079                 printf("DTHB g%d c%d: ", hg, hcl);
2080             #endif
2081             int hg2 = hg;
2082             while ( hg2 < glyph_count ) {
2083                 int hcl2 = glyph_info[hg2].cluster;
2084                 if ( hcl2 != hcl ) { // New cluster starts at hg2: we can draw hg > hg2-1
2085                     #ifdef DEBUG_DRAW_TEXT
2086                         printf("all found, ");
2087                     #endif
2088                     if (is_rtl)
2089                         fb_t_end = hcl; // if fb drawing needed from next glyph: t[..:hcl]
2090                     break;
2091                 }
2092                 if ( glyph_info[hg2].codepoint != 0 || !fallbackFont ) {
2093                     // Glyph found in this font, or not but we have no
2094                     // fallback font: we will draw the .notdef/tofu chars.
2095                     hg2++;
2096                     continue;
2097                 }
2098                 #ifdef DEBUG_DRAW_TEXT
2099                     printf("g%d c%d notdef, ", hg2, hcl2);
2100                 #endif
2101                 // Glyph notdef but we have a fallback font
2102                 // Go look ahead for a complete cluster, or segment of notdef,
2103                 // so we can draw it all with the fallback using harfbuzz
2104                 draw_with_fallback = true;
2105                 // We will update hg2 and hcl2 to be the last glyph of
2106                 // a cluster/segment with notdef
2107                 int hclbad = hcl2;
2108                 int hclgood = -1;
2109                 int hg3 = hg2+1;
2110                 while ( hg3 < glyph_count ) {
2111                     int hcl3 = glyph_info[hg3].cluster;
2112                     if ( hclgood >=0 && hcl3 != hclgood ) {
2113                         // Found a complete cluster
2114                         // We can draw hg > hg2-1 with fallback font
2115                         #ifdef DEBUG_DRAW_TEXT
2116                             printf("c%d complete, need redraw up to g%d", hclgood, hg2);
2117                         #endif
2118                         if (!is_rtl)
2119                             fb_t_end = hclgood; // fb drawing t[..:hclgood]
2120                         hg2 += 1; // set hg2 to the first ok glyph
2121                         break;
2122                     }
2123                     if ( glyph_info[hg3].codepoint == 0 || hcl3 == hclbad) {
2124                         #ifdef DEBUG_DRAW_TEXT
2125                             printf("g%d c%d -, ", hg3, hcl3);
2126                         #endif
2127                         // notdef, or def but part of uncomplete previous cluster
2128                         hcl2 = hcl3;
2129                         hg2 = hg3; // move hg2 to this bad glyph
2130                         hclgood = -1; // un'good found good cluster
2131                         hclbad = hcl3;
2132                         if (is_rtl)
2133                             fb_t_start = hclbad; // fb drawing t[hclbad::..]
2134                         hg3++;
2135                         continue;
2136                     }
2137                     // Codepoint found, and we're not part of an uncomplete cluster
2138                     #ifdef DEBUG_DRAW_TEXT
2139                         printf("g%d c%d +, ", hg3, hcl3);
2140                     #endif
2141                     hclgood = hcl3;
2142                     hg3++;
2143                 }
2144                 if ( hg3 == glyph_count && hclgood >=0 ) { // last glyph was a good cluster
2145                     if (!is_rtl)
2146                         fb_t_end = hclgood; // fb drawing t[..:hclgood]
2147                     hg2 += 1; // set hg2 to the first ok glyph (so, the single last one)
2148                     break;
2149                 }
2150                 if ( hg3 == glyph_count ) { // no good cluster met till end of text
2151                     hg2 = glyph_count; // get out of hg2 loop
2152                     if (is_rtl)
2153                         fb_t_start = 0;
2154                     else
2155                         fb_t_end = len;
2156                 }
2157                 break;
2158             }
2159             // Draw glyphs from hg to hg2 excluded
2160             if (draw_with_fallback) {
2161                 #ifdef DEBUG_DRAW_TEXT
2162                     printf("[...]\nDTHB ### drawing past notdef with fallback font %d>%d ", hg, hg2);
2163                     printf(" => %d > %d\n", fb_t_start, fb_t_end);
2164                 #endif
2165                 // Adjust DrawTextString() params for fallback drawing
2166                 lUInt32 fb_flags = flags;
2167                 fb_flags &= ~LFNT_DRAW_DECORATION_MASK; // main font will do text decoration
2168                 // We must keep direction, but we should drop BOT/EOT flags
2169                 // if this segment is not at start/end (this might be bogus
2170                 // if the char at start or end is a space that could be drawn
2171                 // with the main font).
2172                 if (fb_t_start > 0)
2173                     fb_flags &= ~LFNT_HINT_BEGINS_PARAGRAPH;
2174                 if (fb_t_end < len)
2175                     fb_flags &= ~LFNT_HINT_ENDS_PARAGRAPH;
2176                 // Adjust fallback y so baselines of both fonts match
2177                 int fb_y = y + _baseline - fallbackFont->getBaseline();
2178                 bool fb_addHyphen = false; // will be added by main font
2179                 const lChar32 * fb_text = text + fb_t_start;
2180                 int fb_len = fb_t_end - fb_t_start;
2181                 // (width and text_decoration_back_gap are only used for
2182                 // text decoration, that we dropped: no update needed)
2183                 int fb_advance = fallbackFont->DrawTextString( buf, x, fb_y,
2184                    fb_text, fb_len,
2185                    def_char, palette, fb_addHyphen, lang_cfg, fb_flags, letter_spacing,
2186                    width, text_decoration_back_gap, fallbackPassMask | _fallback_mask );
2187                 x += fb_advance;
2188                 #ifdef DEBUG_DRAW_TEXT
2189                     printf("DTHB ### drawn past notdef > X+= %d\n[...]", fb_advance);
2190                 #endif
2191             }
2192             else {
2193                 #ifdef DEBUG_DRAW_TEXT
2194                     printf("regular g%d>%d: ", hg, hg2);
2195                 #endif
2196                 // Draw glyphs of this same cluster
2197                 int prev_x = x;
2198                 for (i = hg; i < hg2; i++) {
2199                     LVFontGlyphCacheItem *item = getGlyphByIndex(glyph_info[i].codepoint);
2200                     if (item) {
2201                         int w = FONT_METRIC_TO_PX(glyph_pos[i].x_advance);
2202                         #ifdef DEBUG_DRAW_TEXT
2203                             printf("%x(x=%d+%d,w=%d) ", glyph_info[i].codepoint, x,
2204                                     item->origin_x + FONT_METRIC_TO_PX(glyph_pos[i].x_offset), w);
2205                         #endif
2206                         buf->Draw(x + item->origin_x + FONT_METRIC_TO_PX(glyph_pos[i].x_offset),
2207                                   y + _baseline - item->origin_y - FONT_METRIC_TO_PX(glyph_pos[i].y_offset),
2208                                   item->bmp,
2209                                   item->bmp_width,
2210                                   item->bmp_height,
2211                                   palette);
2212                         x += w;
2213                     }
2214                     #ifdef DEBUG_DRAW_TEXT
2215                     else
2216                         printf("SKIPPED %x", glyph_info[i].codepoint);
2217                     #endif
2218                 }
2219                 // Whole cluster drawn: add letter spacing
2220                 if ( x > prev_x ) {
2221                     // But only if this cluster has some advance
2222                     // (e.g. a soft-hyphen makes its own cluster, that
2223                     // draws a space glyph, but with no advance)
2224                     x += letter_spacing;
2225                 }
2226             }
2227             hg = hg2;
2228             #ifdef DEBUG_DRAW_TEXT
2229                 printf("\n");
2230             #endif
2231         }
2232 
2233         // Wondered if item->origin_x and glyph_pos[hg].x_offset must really
2234         // be added (harfbuzz' x_offset correcting Freetype's origin_x),
2235         // or are the same thing (harfbuzz' x_offset=0 replacing and
2236         // cancelling FreeType's origin_x) ?
2237         // Comparing screenshots seems to indicate they must be added.
2238 
2239         if (addHyphen) {
2240             ch = UNICODE_SOFT_HYPHEN_CODE;
2241             LVFontGlyphCacheItem *item = getGlyph(ch, def_char);
2242             if (item) {
2243                 w = item->advance;
2244                 buf->Draw( x + item->origin_x,
2245                            y + _baseline - item->origin_y,
2246                            item->bmp,
2247                            item->bmp_width,
2248                            item->bmp_height,
2249                            palette);
2250                 x  += w; // + letter_spacing; (let's not add any letter-spacing after hyphen)
2251             }
2252         }
2253     } else if (_shapingMode == SHAPING_MODE_HARFBUZZ_LIGHT) {
2254         // HarfBuzz light rendering mode
2255         struct LVCharTriplet triplet;
2256         struct LVCharPosInfo posInfo;
2257         triplet.Char = 0;
2258         bool is_rtl = (flags & LFNT_HINT_DIRECTION_KNOWN) && (flags & LFNT_HINT_DIRECTION_IS_RTL);
2259         for ( i=0; i<=(unsigned int)len; i++) {
2260             if ( i==len && !addHyphen )
2261                 break;
2262             if ( i<len ) {
2263                 // If RTL, draw glyphs starting from the of the node text
2264                 ch = is_rtl ? text[len-1-i] : text[i];
2265                 if ( ch=='\t' )
2266                     ch = ' ';
2267                 // don't draw any soft hyphens inside text string
2268                 isHyphen = (ch==UNICODE_SOFT_HYPHEN_CODE);
2269             }
2270             else {
2271                 ch = UNICODE_SOFT_HYPHEN_CODE;
2272                 isHyphen = false; // an hyphen, but not one to not draw
2273             }
2274             LVFontGlyphCacheItem * item = getGlyph(ch, def_char, fallbackPassMask);
2275             if ( !item )
2276                 continue;
2277             if ( (item && !isHyphen) || i==len ) { // only draw soft hyphens at end of string
2278                 triplet.prevChar = triplet.Char;
2279                 triplet.Char = ch;
2280                 if (i < (unsigned int)(len - 1))
2281                     triplet.nextChar = is_rtl ? text[len-1-i-1] : text[i + 1];
2282                 else
2283                     triplet.nextChar = 0;
2284                 if (!_width_cache2.get(triplet, posInfo)) {
2285                     if (!hbCalcCharWidth(&posInfo, triplet, def_char, fallbackPassMask)) {
2286                         posInfo.offset = 0;
2287                         posInfo.width = item->advance;
2288                     }
2289                     _width_cache2.set(triplet, posInfo);
2290                 }
2291                 buf->Draw(x + item->origin_x + posInfo.offset,
2292                     y + _baseline - item->origin_y,
2293                     item->bmp,
2294                     item->bmp_width,
2295                     item->bmp_height,
2296                     palette);
2297 
2298                 x += posInfo.width + letter_spacing;
2299             }
2300         }
2301     } else {
2302 #endif // USE_HARFBUZZ
2303     // Simple FreeType rendering mode
2304     FT_UInt previous = 0;
2305     int error;
2306 #if (ALLOW_KERNING==1)
2307     int use_kerning = _allowKerning && FT_HAS_KERNING( _face );
2308 #endif
2309     for ( i=0; i<=len; i++) {
2310         if ( i==len && (!addHyphen || isHyphen) )
2311             break;
2312         if ( i<len ) {
2313             ch = text[i];
2314             if ( ch=='\t' )
2315                 ch = ' ';
2316             isHyphen = (ch==UNICODE_SOFT_HYPHEN_CODE) && (i<len-1);
2317         } else {
2318             ch = UNICODE_SOFT_HYPHEN_CODE;
2319             isHyphen = 0;
2320         }
2321         FT_UInt ch_glyph_index = getCharIndex( ch, def_char );
2322         int kerning = 0;
2323 #if (ALLOW_KERNING==1)
2324         if ( use_kerning && previous>0 && ch_glyph_index>0 ) {
2325             FT_Vector delta;
2326             error = FT_Get_Kerning( _face,          /* handle to face object */
2327                                     previous,          /* left glyph index      */
2328                                     ch_glyph_index,         /* right glyph index     */
2329                                     FT_KERNING_DEFAULT,  /* kerning mode          */
2330                                     &delta );    /* target vector         */
2331             if ( !error )
2332                 kerning = delta.x;
2333         }
2334 #endif
2335         LVFontGlyphCacheItem * item = getGlyph(ch, def_char, fallbackPassMask);
2336         if ( !item )
2337             continue;
2338         if ( (item && !isHyphen) || i>=len-1 ) { // avoid soft hyphens inside text string
2339             int w = item->advance + (kerning >> 6);
2340             buf->Draw( x + (kerning>>6) + item->origin_x,
2341                        y + _baseline - item->origin_y,
2342                        item->bmp,
2343                        item->bmp_width,
2344                        item->bmp_height,
2345                        palette);
2346 
2347             x  += w + letter_spacing;
2348             previous = ch_glyph_index;
2349         }
2350     }
2351 
2352 #if USE_HARFBUZZ==1
2353     } // else fallback to the non harfbuzz code
2354 #endif
2355 
2356     int advance = x - x0;
2357     if ( flags & LFNT_DRAW_DECORATION_MASK ) {
2358         // text decoration: underline, etc.
2359         // Don't overflow the provided width (which may be lower than our
2360         // pen x if last glyph was a space not accounted in word width)
2361         if ( width >= 0 && x > x0 + width)
2362             x = x0 + width;
2363         // And start the decoration before x0 if it is continued
2364         // from previous word
2365         x0 -= text_decoration_back_gap;
2366         int h = _size > 30 ? 2 : 1;
2367         lUInt32 cl = buf->GetTextColor();
2368         if ( flags & LFNT_DRAW_UNDERLINE ) {
2369             int liney = y + _baseline + h;
2370             buf->FillRect( x0, liney, x, liney+h, cl );
2371         }
2372         if ( flags & LFNT_DRAW_OVERLINE ) {
2373             int liney = y + h;
2374             buf->FillRect( x0, liney, x, liney+h, cl );
2375         }
2376         if ( flags & LFNT_DRAW_LINE_THROUGH ) {
2377             // int liney = y + _baseline - _size/4 - h/2;
2378             int liney = y + _baseline - _size*2/7;
2379             buf->FillRect( x0, liney, x, liney+h, cl );
2380         }
2381     }
2382     return advance;
2383 }
2384 
2385 void LVFreeTypeFace::Clear() {
2386     LVLock lock(_mutex);
2387     clearCache();
2388 #if USE_HARFBUZZ == 1
2389     if (_hb_font) {
2390         hb_font_destroy(_hb_font);
2391         _hb_font = 0;
2392     }
2393     _hb_features.clear();
2394 #endif
2395     if (_face) {
2396         FT_Done_Face(_face);
2397         _face = NULL;
2398     }
2399 }
2400 
2401 #endif  // (USE_FREETYPE==1)
2402