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