1 // Created on: 2013-01-28
2 // Created by: Kirill GAVRILOV
3 // Copyright (c) 2013-2014 OPEN CASCADE SAS
5 // This file is part of Open CASCADE Technology software library.
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
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.
13 // Alternatively, this file may be used under the terms of Open CASCADE
14 // commercial license or contractual agreement.
16 #include <Font_FTFont.hxx>
18 #include <Font_FTLibrary.hxx>
19 #include <Font_FontMgr.hxx>
20 #include <Font_TextFormatter.hxx>
21 #include <Message.hxx>
22 #include <Message_Messenger.hxx>
27 #include FT_FREETYPE_H
29 IMPLEMENT_STANDARD_RTTIEXT(Font_FTFont,Standard_Transient)
31 // =======================================================================
32 // function : Font_FTFont
34 // =======================================================================
35 Font_FTFont::Font_FTFont (const Handle(Font_FTLibrary)& theFTLib)
39 myFontAspect (Font_FontAspect_Regular),
41 myLoadFlags (FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL),
43 myToUseUnicodeSubsetFallback (Font_FontMgr::ToUseUnicodeSubsetFallback())
47 myFTLib = new Font_FTLibrary();
51 // =======================================================================
52 // function : Font_FTFont
54 // =======================================================================
55 Font_FTFont::~Font_FTFont()
60 // =======================================================================
63 // =======================================================================
64 void Font_FTFont::Release()
71 FT_Done_Face (myFTFace);
74 myActiveFTFace = NULL;
78 // =======================================================================
81 // =======================================================================
82 bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData,
83 const TCollection_AsciiString& theFileName,
84 const Font_FTFontParams& theParams)
88 myFontPath = theFileName;
89 myFontParams = theParams;
90 if (!myFTLib->IsValid())
92 Message::DefaultMessenger()->Send ("FreeType library is unavailable", Message_Trace);
97 if (!theData.IsNull())
99 if (FT_New_Memory_Face (myFTLib->Instance(), theData->Data(), (FT_Long )theData->Size(), 0, &myFTFace) != 0)
101 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from memory", Message_Trace);
108 if (FT_New_Face (myFTLib->Instance(), myFontPath.ToCString(), 0, &myFTFace) != 0)
110 //Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' failed to load from file", Message_Trace);
116 if (FT_Select_Charmap (myFTFace, ft_encoding_unicode) != 0)
118 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap", Message_Trace);
122 else if (FT_Set_Char_Size (myFTFace, 0L, toFTPoints (theParams.PointSize), theParams.Resolution, theParams.Resolution) != 0)
124 Message::DefaultMessenger()->Send (TCollection_AsciiString("Font '") + myFontPath + "' doesn't contains Unicode charmap of requested size", Message_Trace);
129 if (theParams.ToSynthesizeItalic)
131 const double THE_SHEAR_ANGLE = 10.0 * M_PI / 180.0;
134 aMat.xx = FT_Fixed (Cos (-THE_SHEAR_ANGLE) * (1 << 16));
139 FT_Fixed aFactor = FT_Fixed (Tan (THE_SHEAR_ANGLE) * (1 << 16));
140 aMat.xy += FT_MulFix (aFactor, aMat.xx);
142 FT_Set_Transform (myFTFace, &aMat, 0);
144 myActiveFTFace = myFTFace;
148 // =======================================================================
149 // function : FindAndCreate
151 // =======================================================================
152 Handle(Font_FTFont) Font_FTFont::FindAndCreate (const TCollection_AsciiString& theFontName,
153 const Font_FontAspect theFontAspect,
154 const Font_FTFontParams& theParams,
155 const Font_StrictLevel theStrictLevel)
157 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
158 Font_FontAspect aFontAspect = theFontAspect;
159 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName, theStrictLevel, aFontAspect))
161 Font_FTFontParams aParams = theParams;
162 if (aRequestedFont->IsSingleStrokeFont())
164 aParams.IsSingleStrokeFont = true;
167 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (aFontAspect, aParams.ToSynthesizeItalic);
168 Handle(Font_FTFont) aFont = new Font_FTFont();
169 if (aFont->Init (aPath, aParams))
171 aFont->myFontAspect = aFontAspect;
175 return Handle(Font_FTFont)();
178 // =======================================================================
179 // function : FindAndInit
181 // =======================================================================
182 bool Font_FTFont::FindAndInit (const TCollection_AsciiString& theFontName,
183 Font_FontAspect theFontAspect,
184 const Font_FTFontParams& theParams,
185 Font_StrictLevel theStrictLevel)
187 Font_FTFontParams aParams = theParams;
188 myFontAspect = theFontAspect;
189 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
190 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName.ToCString(), theStrictLevel, myFontAspect))
192 if (aRequestedFont->IsSingleStrokeFont())
194 aParams.IsSingleStrokeFont = true;
197 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
198 return Init (aPath, aParams);
204 // =======================================================================
205 // function : findAndInitFallback
207 // =======================================================================
208 bool Font_FTFont::findAndInitFallback (Font_UnicodeSubset theSubset)
210 if (!myFallbackFaces[theSubset].IsNull())
212 return myFallbackFaces[theSubset]->IsValid();
215 myFallbackFaces[theSubset] = new Font_FTFont (myFTLib);
216 myFallbackFaces[theSubset]->myToUseUnicodeSubsetFallback = false; // no recursion
218 Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
219 if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFallbackFont (theSubset, myFontAspect))
221 Font_FTFontParams aParams = myFontParams;
222 aParams.IsSingleStrokeFont = aRequestedFont->IsSingleStrokeFont();
224 const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
225 if (myFallbackFaces[theSubset]->Init (aPath, aParams))
227 Message::DefaultMessenger()->Send (TCollection_AsciiString ("Font_FTFont, using fallback font '") + aRequestedFont->FontName() + "'"
228 + " for symbols unsupported by '" + myFTFace->family_name + "'", Message_Trace);
231 return myFallbackFaces[theSubset]->IsValid();
234 // =======================================================================
235 // function : HasSymbol
237 // =======================================================================
238 bool Font_FTFont::HasSymbol (Standard_Utf32Char theUChar) const
240 return FT_Get_Char_Index (myFTFace, theUChar) != 0;
243 // =======================================================================
244 // function : loadGlyph
246 // =======================================================================
247 bool Font_FTFont::loadGlyph (const Standard_Utf32Char theUChar)
249 if (myUChar == theUChar)
256 myActiveFTFace = myFTFace;
262 if (myToUseUnicodeSubsetFallback
263 && !HasSymbol (theUChar))
265 // try using fallback
266 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
267 if (findAndInitFallback (aSubset)
268 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
270 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
274 if (FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags)) != 0
275 || myActiveFTFace->glyph == NULL)
284 // =======================================================================
285 // function : RenderGlyph
287 // =======================================================================
288 bool Font_FTFont::RenderGlyph (const Standard_Utf32Char theUChar)
292 myActiveFTFace = myFTFace;
295 && myToUseUnicodeSubsetFallback
296 && !HasSymbol (theUChar))
298 // try using fallback
299 const Font_UnicodeSubset aSubset = CharSubset (theUChar);
300 if (findAndInitFallback (aSubset)
301 && myFallbackFaces[aSubset]->HasSymbol (theUChar))
303 myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
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)
315 FT_Bitmap aBitmap = myActiveFTFace->glyph->bitmap;
316 if (aBitmap.buffer == NULL || aBitmap.width == 0 || aBitmap.rows == 0)
321 if (aBitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
323 if (!myGlyphImg.InitWrapper (Image_Format_Alpha, aBitmap.buffer,
324 aBitmap.width, aBitmap.rows, Abs (aBitmap.pitch)))
328 myGlyphImg.SetTopDown (aBitmap.pitch > 0);
330 else if (aBitmap.pixel_mode == FT_PIXEL_MODE_MONO)
332 if (!myGlyphImg.InitTrash (Image_Format_Gray, aBitmap.width, aBitmap.rows))
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)
341 for (int aCol = 0; aCol < (int )aBitmap.width; ++aCol)
343 const int aBitOn = aBitmap.buffer[aNumOfBytesInRow * aRow + aCol / 8] & (0x80 >> (aCol % 8));
344 *myGlyphImg.ChangeRawValue (aRow, aCol) = aBitOn ? 255 : 0;
357 // =======================================================================
358 // function : GlyphMaxSizeX
360 // =======================================================================
361 unsigned int Font_FTFont::GlyphMaxSizeX (bool theToIncludeFallback) const
363 if (!theToIncludeFallback)
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);
371 unsigned int aWidth = GlyphMaxSizeX (false);
372 if (theToIncludeFallback)
374 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
376 if (!myFallbackFaces[aFontIter].IsNull()
377 && myFallbackFaces[aFontIter]->IsValid())
379 aWidth = std::max (aWidth, myFallbackFaces[aFontIter]->GlyphMaxSizeX (false));
386 // =======================================================================
387 // function : GlyphMaxSizeY
389 // =======================================================================
390 unsigned int Font_FTFont::GlyphMaxSizeY (bool theToIncludeFallback) const
392 if (!theToIncludeFallback)
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);
400 unsigned int aHeight = GlyphMaxSizeY (false);
401 if (theToIncludeFallback)
403 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
405 if (!myFallbackFaces[aFontIter].IsNull()
406 && myFallbackFaces[aFontIter]->IsValid())
408 aHeight = std::max (aHeight, myFallbackFaces[aFontIter]->GlyphMaxSizeY (false));
415 // =======================================================================
416 // function : Ascender
418 // =======================================================================
419 float Font_FTFont::Ascender() const
421 return float(myFTFace->ascender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
424 // =======================================================================
425 // function : Descender
427 // =======================================================================
428 float Font_FTFont::Descender() const
430 return float(myFTFace->descender) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
433 // =======================================================================
434 // function : LineSpacing
436 // =======================================================================
437 float Font_FTFont::LineSpacing() const
439 return float(myFTFace->height) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM));
442 // =======================================================================
443 // function : AdvanceX
445 // =======================================================================
446 float Font_FTFont::AdvanceX (Standard_Utf32Char theUChar,
447 Standard_Utf32Char theUCharNext)
449 loadGlyph (theUChar);
450 return AdvanceX (theUCharNext);
453 // =======================================================================
454 // function : AdvanceY
456 // =======================================================================
457 float Font_FTFont::AdvanceY (Standard_Utf32Char theUChar,
458 Standard_Utf32Char theUCharNext)
460 loadGlyph (theUChar);
461 return AdvanceY (theUCharNext);
464 // =======================================================================
465 // function : getKerning
467 // =======================================================================
468 bool Font_FTFont::getKerning (FT_Vector& theKern,
469 Standard_Utf32Char theUCharCurr,
470 Standard_Utf32Char theUCharNext) const
474 if (theUCharNext != 0 && FT_HAS_KERNING(myActiveFTFace) != 0)
476 const FT_UInt aCharCurr = FT_Get_Char_Index (myActiveFTFace, theUCharCurr);
477 const FT_UInt aCharNext = FT_Get_Char_Index (myActiveFTFace, theUCharNext);
478 if (aCharCurr == 0 || aCharNext == 0
479 || FT_Get_Kerning (myActiveFTFace, aCharCurr, aCharNext, FT_KERNING_UNFITTED, &theKern) != 0)
490 // =======================================================================
491 // function : AdvanceX
493 // =======================================================================
494 float Font_FTFont::AdvanceX (Standard_Utf32Char theUCharNext) const
502 getKerning (aKern, myUChar, theUCharNext);
503 return myWidthScaling * fromFTPoints<float> (myActiveFTFace->glyph->advance.x + aKern.x);
506 // =======================================================================
507 // function : AdvanceY
509 // =======================================================================
510 float Font_FTFont::AdvanceY (Standard_Utf32Char theUCharNext) const
518 getKerning (aKern, myUChar, theUCharNext);
519 return fromFTPoints<float> (myActiveFTFace->glyph->advance.y + aKern.y);
522 // =======================================================================
523 // function : GlyphsNumber
525 // =======================================================================
526 Standard_Integer Font_FTFont::GlyphsNumber (bool theToIncludeFallback) const
528 Standard_Integer aNbGlyphs = myFTFace->num_glyphs;
529 if (theToIncludeFallback)
531 for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
533 if (!myFallbackFaces[aFontIter].IsNull()
534 && myFallbackFaces[aFontIter]->IsValid())
536 aNbGlyphs += myFallbackFaces[aFontIter]->GlyphsNumber (false);
543 // =======================================================================
544 // function : GlyphRect
546 // =======================================================================
547 void Font_FTFont::GlyphRect (Font_Rect& theRect) const
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);
556 // =======================================================================
557 // function : BoundingBox
559 // =======================================================================
560 Font_Rect Font_FTFont::BoundingBox (const NCollection_String& theString,
561 const Graphic3d_HorizontalTextAlignment theAlignX,
562 const Graphic3d_VerticalTextAlignment theAlignY)
564 Font_TextFormatter aFormatter;
565 aFormatter.SetupAlignment (theAlignX, theAlignY);
568 aFormatter.Append (theString, *this);
572 aFormatter.BndBox (aBndBox);