0030782: Visualization, Font_FTFont - use predefined fallback fonts for extended...
[occt.git] / src / Font / Font_FTFont.cxx
CommitLineData
a174a3c5 1// Created on: 2013-01-28
2// Created by: Kirill GAVRILOV
d5f74e42 3// Copyright (c) 2013-2014 OPEN CASCADE SAS
a174a3c5 4//
973c2be1 5// This file is part of Open CASCADE Technology software library.
a174a3c5 6//
d5f74e42 7// This library is free software; you can redistribute it and/or modify it under
8// the terms of the GNU Lesser General Public License version 2.1 as published
973c2be1 9// by the Free Software Foundation, with special exception defined in the file
10// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
11// distribution for complete text of the license and disclaimer of any warranty.
a174a3c5 12//
973c2be1 13// Alternatively, this file may be used under the terms of Open CASCADE
14// commercial license or contractual agreement.
a174a3c5 15
16#include <Font_FTFont.hxx>
f9801cf9 17
18#include <Font_FTLibrary.hxx>
b514beda 19#include <Font_FontMgr.hxx>
317d68c9 20#include <Font_TextFormatter.hxx>
1bbd7c79 21#include <Message.hxx>
22#include <Message_Messenger.hxx>
317d68c9 23
912761ea 24#include <algorithm>
25
f9801cf9 26#include <ft2build.h>
27#include FT_FREETYPE_H
a174a3c5 28
92efcf78 29IMPLEMENT_STANDARD_RTTIEXT(Font_FTFont,Standard_Transient)
30
a174a3c5 31// =======================================================================
32// function : Font_FTFont
33// purpose :
34// =======================================================================
35Font_FTFont::Font_FTFont (const Handle(Font_FTLibrary)& theFTLib)
151da08b 36: myFTLib (theFTLib),
37 myFTFace (NULL),
912761ea 38 myActiveFTFace(NULL),
39 myFontAspect (Font_FontAspect_Regular),
151da08b 40 myWidthScaling(1.0),
41 myLoadFlags (FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL),
912761ea 42 myUChar (0U),
43 myToUseUnicodeSubsetFallback (Font_FontMgr::ToUseUnicodeSubsetFallback())
a174a3c5 44{
45 if (myFTLib.IsNull())
46 {
47 myFTLib = new Font_FTLibrary();
48 }
49}
50
51// =======================================================================
52// function : Font_FTFont
53// purpose :
54// =======================================================================
55Font_FTFont::~Font_FTFont()
56{
57 Release();
58}
59
60// =======================================================================
f9801cf9 61// function : Release
a174a3c5 62// purpose :
63// =======================================================================
64void Font_FTFont::Release()
65{
66 myGlyphImg.Clear();
67 myFontPath.Clear();
68 myUChar = 0;
69 if (myFTFace != NULL)
70 {
71 FT_Done_Face (myFTFace);
72 myFTFace = NULL;
73 }
912761ea 74 myActiveFTFace = NULL;
1bbd7c79 75 myBuffer.Nullify();
a174a3c5 76}
77
78// =======================================================================
b514beda 79// function : Init
a174a3c5 80// purpose :
81// =======================================================================
1bbd7c79 82bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData,
83 const TCollection_AsciiString& theFileName,
84 const Font_FTFontParams& theParams)
a174a3c5 85{
86 Release();
1bbd7c79 87 myBuffer = theData;
88 myFontPath = theFileName;
89 myFontParams = theParams;
a174a3c5 90 if (!myFTLib->IsValid())
91 {
1bbd7c79 92 Message::DefaultMessenger()->Send ("FreeType library is unavailable", Message_Trace);
a174a3c5 93 Release();
94 return false;
95 }
96
1bbd7c79 97 if (!theData.IsNull())
a174a3c5 98 {
1bbd7c79 99 if (FT_New_Memory_Face (myFTLib->Instance(), theData->Data(), (FT_Long )theData->Size(), 0, &myFTFace) != 0)
100 {
101 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from memory", Message_Trace);
102 Release();
103 return false;
104 }
a174a3c5 105 }
1bbd7c79 106 else
a174a3c5 107 {
1bbd7c79 108 if (FT_New_Face (myFTLib->Instance(), myFontPath.ToCString(), 0, &myFTFace) != 0)
109 {
110 //Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from file", Message_Trace);
111 Release();
112 return false;
113 }
114 }
115
116 if (FT_Select_Charmap (myFTFace, ft_encoding_unicode) != 0)
117 {
118 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap", Message_Trace);
a174a3c5 119 Release();
120 return false;
121 }
1bbd7c79 122 else if (FT_Set_Char_Size (myFTFace, 0L, toFTPoints (theParams.PointSize), theParams.Resolution, theParams.Resolution) != 0)
a174a3c5 123 {
1bbd7c79 124 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap of requested size", Message_Trace);
a174a3c5 125 Release();
126 return false;
127 }
1bbd7c79 128
129 if (theParams.ToSynthesizeItalic)
130 {
131 const double THE_SHEAR_ANGLE = 10.0 * M_PI / 180.0;
132
133 FT_Matrix aMat;
134 aMat.xx = FT_Fixed (Cos (-THE_SHEAR_ANGLE) * (1 << 16));
135 aMat.xy = 0;
136 aMat.yx = 0;
137 aMat.yy = aMat.xx;
138
139 FT_Fixed aFactor = FT_Fixed (Tan (THE_SHEAR_ANGLE) * (1 << 16));
140 aMat.xy += FT_MulFix (aFactor, aMat.xx);
141
142 FT_Set_Transform (myFTFace, &aMat, 0);
143 }
912761ea 144 myActiveFTFace = myFTFace;
a174a3c5 145 return true;
146}
147
b514beda 148// =======================================================================
1bbd7c79 149// function : FindAndCreate
b514beda 150// purpose :
151// =======================================================================
1bbd7c79 152Handle(Font_FTFont) Font_FTFont::FindAndCreate (const TCollection_AsciiString& theFontName,
153 const Font_FontAspect theFontAspect,
154 const Font_FTFontParams& theParams,
155 const Font_StrictLevel theStrictLevel)
b514beda 156{
157 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
5b377041 158 Font_FontAspect aFontAspect = theFontAspect;
1bbd7c79 159 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName, theStrictLevel, aFontAspect))
e4f0cc46 160 {
1bbd7c79 161 Font_FTFontParams aParams = theParams;
162 if (aRequestedFont->IsSingleStrokeFont())
163 {
164 aParams.IsSingleStrokeFont = true;
165 }
166
167 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (aFontAspect, aParams.ToSynthesizeItalic);
168 Handle(Font_FTFont) aFont = new Font_FTFont();
169 if (aFont->Init (aPath, aParams))
170 {
912761ea 171 aFont->myFontAspect = aFontAspect;
1bbd7c79 172 return aFont;
173 }
e4f0cc46 174 }
1bbd7c79 175 return Handle(Font_FTFont)();
176}
177
178// =======================================================================
179// function : FindAndInit
180// purpose :
181// =======================================================================
182bool Font_FTFont::FindAndInit (const TCollection_AsciiString& theFontName,
183 Font_FontAspect theFontAspect,
184 const Font_FTFontParams& theParams,
185 Font_StrictLevel theStrictLevel)
186{
187 Font_FTFontParams aParams = theParams;
912761ea 188 myFontAspect = theFontAspect;
1bbd7c79 189 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
912761ea 190 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName.ToCString(), theStrictLevel, myFontAspect))
1bbd7c79 191 {
192 if (aRequestedFont->IsSingleStrokeFont())
193 {
194 aParams.IsSingleStrokeFont = true;
195 }
196
912761ea 197 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
1bbd7c79 198 return Init (aPath, aParams);
199 }
200 Release();
e4f0cc46 201 return false;
b514beda 202}
203
912761ea 204// =======================================================================
205// function : findAndInitFallback
206// purpose :
207// =======================================================================
208bool Font_FTFont::findAndInitFallback (Font_UnicodeSubset theSubset)
209{
210 if (!myFallbackFaces[theSubset].IsNull())
211 {
212 return myFallbackFaces[theSubset]->IsValid();
213 }
214
215 myFallbackFaces[theSubset] = new Font_FTFont (myFTLib);
216 myFallbackFaces[theSubset]->myToUseUnicodeSubsetFallback = false; // no recursion
217
218 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
219 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFallbackFont (theSubset, myFontAspect))
220 {
221 Font_FTFontParams aParams = myFontParams;
222 aParams.IsSingleStrokeFont = aRequestedFont->IsSingleStrokeFont();
223
224 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
225 if (myFallbackFaces[theSubset]->Init (aPath, aParams))
226 {
227 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Font_FTFont, using fallback font '") + aRequestedFont->FontName() + "'"
228 + " for symbols unsupported by '" + myFTFace->family_name + "'", Message_Trace);
229 }
230 }
231 return myFallbackFaces[theSubset]->IsValid();
232}
233
234// =======================================================================
235// function : HasSymbol
236// purpose :
237// =======================================================================
238bool Font_FTFont::HasSymbol (Standard_Utf32Char theUChar) const
239{
240 return FT_Get_Char_Index (myFTFace, theUChar) != 0;
241}
242
a174a3c5 243// =======================================================================
244// function : loadGlyph
245// purpose :
246// =======================================================================
247bool Font_FTFont::loadGlyph (const Standard_Utf32Char theUChar)
248{
249 if (myUChar == theUChar)
250 {
82be4141 251 return myUChar != 0;
a174a3c5 252 }
253
254 myGlyphImg.Clear();
255 myUChar = 0;
912761ea 256 myActiveFTFace = myFTFace;
257 if (theUChar == 0)
258 {
259 return false;
260 }
261
262 if (myToUseUnicodeSubsetFallback
263 && !HasSymbol (theUChar))
264 {
265 // try using fallback
266 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
267 if (findAndInitFallback (aSubset)
268 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
269 {
270 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
271 }
272 }
273
274 if (FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags)) != 0
275 || myActiveFTFace->glyph == NULL)
a174a3c5 276 {
277 return false;
278 }
279
280 myUChar = theUChar;
281 return true;
282}
283
284// =======================================================================
285// function : RenderGlyph
286// purpose :
287// =======================================================================
288bool Font_FTFont::RenderGlyph (const Standard_Utf32Char theUChar)
289{
290 myGlyphImg.Clear();
291 myUChar = 0;
912761ea 292 myActiveFTFace = myFTFace;
293
294 if (theUChar != 0
295 && myToUseUnicodeSubsetFallback
296 && !HasSymbol (theUChar))
297 {
298 // try using fallback
299 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
300 if (findAndInitFallback (aSubset)
301 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
302 {
303 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
304 }
305 }
306
a174a3c5 307 if (theUChar == 0
912761ea 308 || FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags | FT_LOAD_RENDER)) != 0
309 || myActiveFTFace->glyph == NULL
310 || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
a174a3c5 311 {
312 return false;
313 }
314
912761ea 315 FT_Bitmap aBitmap = myActiveFTFace->glyph->bitmap;
316 if (aBitmap.buffer == NULL || aBitmap.width == 0 || aBitmap.rows == 0)
a174a3c5 317 {
318 return false;
319 }
912761ea 320
321 if (aBitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
322 {
323 if (!myGlyphImg.InitWrapper (Image_Format_Alpha, aBitmap.buffer,
324 aBitmap.width, aBitmap.rows, Abs (aBitmap.pitch)))
325 {
326 return false;
327 }
328 myGlyphImg.SetTopDown (aBitmap.pitch > 0);
329 }
330 else if (aBitmap.pixel_mode == FT_PIXEL_MODE_MONO)
331 {
332 if (!myGlyphImg.InitTrash (Image_Format_Gray, aBitmap.width, aBitmap.rows))
333 {
334 return false;
335 }
336
337 myGlyphImg.SetTopDown (aBitmap.pitch > 0);
338 const int aNumOfBytesInRow = aBitmap.width / 8 + (aBitmap.width % 8 ? 1 : 0);
339 for (int aRow = 0; aRow < (int )aBitmap.rows; ++aRow)
340 {
341 for (int aCol = 0; aCol < (int )aBitmap.width; ++aCol)
342 {
343 const int aBitOn = aBitmap.buffer[aNumOfBytesInRow * aRow + aCol / 8] & (0x80 >> (aCol % 8));
344 *myGlyphImg.ChangeRawValue (aRow, aCol) = aBitOn ? 255 : 0;
345 }
346 }
347 }
348 else
a174a3c5 349 {
350 return false;
351 }
912761ea 352
a174a3c5 353 myUChar = theUChar;
354 return true;
355}
356
357// =======================================================================
358// function : GlyphMaxSizeX
359// purpose :
360// =======================================================================
912761ea 361unsigned int Font_FTFont::GlyphMaxSizeX (bool theToIncludeFallback) const
a174a3c5 362{
912761ea 363 if (!theToIncludeFallback)
364 {
365 float aWidth = (FT_IS_SCALABLE(myFTFace) != 0)
366 ? float(myFTFace->bbox.xMax - myFTFace->bbox.xMin) * (float(myFTFace->size->metrics.x_ppem) / float(myFTFace->units_per_EM))
367 : fromFTPoints<float> (myFTFace->size->metrics.max_advance);
368 return (unsigned int)(aWidth + 0.5f);
369 }
370
371 unsigned int aWidth = GlyphMaxSizeX (false);
372 if (theToIncludeFallback)
373 {
374 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
375 {
376 if (!myFallbackFaces[aFontIter].IsNull()
377 && myFallbackFaces[aFontIter]->IsValid())
378 {
379 aWidth = std::max (aWidth, myFallbackFaces[aFontIter]->GlyphMaxSizeX (false));
380 }
381 }
382 }
383 return aWidth;
a174a3c5 384}
385
386// =======================================================================
387// function : GlyphMaxSizeY
388// purpose :
389// =======================================================================
912761ea 390unsigned int Font_FTFont::GlyphMaxSizeY (bool theToIncludeFallback) const
a174a3c5 391{
912761ea 392 if (!theToIncludeFallback)
393 {
394 float aHeight = (FT_IS_SCALABLE(myFTFace) != 0)
395 ? float(myFTFace->bbox.yMax - myFTFace->bbox.yMin) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM))
396 : fromFTPoints<float> (myFTFace->size->metrics.height);
397 return (unsigned int)(aHeight + 0.5f);
398 }
399
400 unsigned int aHeight = GlyphMaxSizeY (false);
401 if (theToIncludeFallback)
402 {
403 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
404 {
405 if (!myFallbackFaces[aFontIter].IsNull()
406 && myFallbackFaces[aFontIter]->IsValid())
407 {
408 aHeight = std::max (aHeight, myFallbackFaces[aFontIter]->GlyphMaxSizeY (false));
409 }
410 }
411 }
412 return aHeight;
a174a3c5 413}
414
f9801cf9 415// =======================================================================
416// function : Ascender
417// purpose :
418// =======================================================================
419float Font_FTFont::Ascender() const
420{
421 return float(myFTFace->ascender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
422}
423
424// =======================================================================
425// function : Descender
426// purpose :
427// =======================================================================
428float Font_FTFont::Descender() const
429{
430 return float(myFTFace->descender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
431}
432
433// =======================================================================
434// function : LineSpacing
435// purpose :
436// =======================================================================
437float Font_FTFont::LineSpacing() const
438{
439 return float(myFTFace->height) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
440}
441
a174a3c5 442// =======================================================================
443// function : AdvanceX
444// purpose :
445// =======================================================================
82be4141 446float Font_FTFont::AdvanceX (Standard_Utf32Char theUChar,
447 Standard_Utf32Char theUCharNext)
a174a3c5 448{
449 loadGlyph (theUChar);
450 return AdvanceX (theUCharNext);
451}
452
453// =======================================================================
454// function : AdvanceY
455// purpose :
456// =======================================================================
82be4141 457float Font_FTFont::AdvanceY (Standard_Utf32Char theUChar,
458 Standard_Utf32Char theUCharNext)
a174a3c5 459{
460 loadGlyph (theUChar);
461 return AdvanceY (theUCharNext);
462}
463
912761ea 464// =======================================================================
465// function : getKerning
466// purpose :
467// =======================================================================
82be4141 468bool Font_FTFont::getKerning (FT_Vector& theKern,
469 Standard_Utf32Char theUCharCurr,
470 Standard_Utf32Char theUCharNext) const
471{
472 theKern.x = 0;
473 theKern.y = 0;
912761ea 474 if (theUCharNext != 0 && FT_HAS_KERNING(myActiveFTFace) != 0)
82be4141 475 {
912761ea 476 const FT_UInt aCharCurr = FT_Get_Char_Index (myActiveFTFace, theUCharCurr);
477 const FT_UInt aCharNext = FT_Get_Char_Index (myActiveFTFace, theUCharNext);
82be4141 478 if (aCharCurr == 0 || aCharNext == 0
912761ea 479 || FT_Get_Kerning (myActiveFTFace, aCharCurr, aCharNext, FT_KERNING_UNFITTED, &theKern) != 0)
82be4141 480 {
481 theKern.x = 0;
482 theKern.y = 0;
483 return false;
484 }
485 return true;
486 }
487 return false;
488}
489
a174a3c5 490// =======================================================================
491// function : AdvanceX
492// purpose :
493// =======================================================================
82be4141 494float Font_FTFont::AdvanceX (Standard_Utf32Char theUCharNext) const
a174a3c5 495{
496 if (myUChar == 0)
497 {
498 return 0.0f;
499 }
500
82be4141 501 FT_Vector aKern;
502 getKerning (aKern, myUChar, theUCharNext);
912761ea 503 return myWidthScaling * fromFTPoints<float> (myActiveFTFace->glyph->advance.x + aKern.x);
a174a3c5 504}
505
506// =======================================================================
507// function : AdvanceY
508// purpose :
509// =======================================================================
82be4141 510float Font_FTFont::AdvanceY (Standard_Utf32Char theUCharNext) const
a174a3c5 511{
512 if (myUChar == 0)
513 {
514 return 0.0f;
515 }
516
82be4141 517 FT_Vector aKern;
518 getKerning (aKern, myUChar, theUCharNext);
912761ea 519 return fromFTPoints<float> (myActiveFTFace->glyph->advance.y + aKern.y);
f9801cf9 520}
521
522// =======================================================================
523// function : GlyphsNumber
524// purpose :
525// =======================================================================
912761ea 526Standard_Integer Font_FTFont::GlyphsNumber (bool theToIncludeFallback) const
f9801cf9 527{
912761ea 528 Standard_Integer aNbGlyphs = myFTFace->num_glyphs;
529 if (theToIncludeFallback)
530 {
531 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
532 {
533 if (!myFallbackFaces[aFontIter].IsNull()
534 && myFallbackFaces[aFontIter]->IsValid())
535 {
536 aNbGlyphs += myFallbackFaces[aFontIter]->GlyphsNumber (false);
537 }
538 }
539 }
540 return aNbGlyphs;
f9801cf9 541}
542
543// =======================================================================
912761ea 544// function : GlyphRect
f9801cf9 545// purpose :
546// =======================================================================
547void Font_FTFont::GlyphRect (Font_Rect& theRect) const
548{
912761ea 549 const FT_Bitmap& aBitmap = myActiveFTFace->glyph->bitmap;
550 theRect.Left = float(myActiveFTFace->glyph->bitmap_left);
551 theRect.Top = float(myActiveFTFace->glyph->bitmap_top);
552 theRect.Right = float(myActiveFTFace->glyph->bitmap_left + (int )aBitmap.width);
553 theRect.Bottom = float(myActiveFTFace->glyph->bitmap_top - (int )aBitmap.rows);
a174a3c5 554}
317d68c9 555
556// =======================================================================
557// function : BoundingBox
558// purpose :
559// =======================================================================
d2eddacc 560Font_Rect Font_FTFont::BoundingBox (const NCollection_String& theString,
561 const Graphic3d_HorizontalTextAlignment theAlignX,
562 const Graphic3d_VerticalTextAlignment theAlignY)
317d68c9 563{
564 Font_TextFormatter aFormatter;
565 aFormatter.SetupAlignment (theAlignX, theAlignY);
566 aFormatter.Reset();
567
568 aFormatter.Append (theString, *this);
569 aFormatter.Format();
570
d2eddacc 571 Font_Rect aBndBox;
317d68c9 572 aFormatter.BndBox (aBndBox);
317d68c9 573 return aBndBox;
574}