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