Font_FTFont now uses fallback fonts for characters from unsupported Unicode subsets,
managed by Font_FTFont::ToUseUnicodeSubsetFallback()
and Font_FontMgr::ToUseUnicodeSubsetFallback() option, enabled by default.
The fallback list includes common font families for Chinese, Korean and Japanese languages.
Font_FTFont::RenderGlyph() now supports FT_PIXEL_MODE_MONO input format used by some CJK fonts.
OpenGl_Font::createTexture() now limits single texture size to circa 4096 glyphs.
test/testgrid now expects test scripts being in UTF-8 encoding in sync with "DRAWEXE -f script.tcl".
AIS::InitFaceLength() - fixed usage of uninitialized result.
Font_FTFont::RenderGlyph() backport bitmap
BRepAdaptor_Surface surf1( aFace );
Handle( Adaptor3d_HSurface ) surf2;
Standard_Boolean isOffset = Standard_False;
+ Offset = 0.0;
if (surf1.GetType() == GeomAbs_OffsetSurface)
{
{
aPlane = surf2->Plane();
aSurfType = AIS_KOS_Plane;
- Offset = 0.;
Result = Standard_True;
}
gp_Pln thePlane( LinePos, LineDir ^ ExtrusionDir);
aPlane = thePlane;
aSurfType = AIS_KOS_Plane;
- Offset = 0.;
Result = Standard_True;
}
}
{
aSurf = (Handle( Geom_OffsetSurface )::DownCast( aSurf ))->Surface();
aPlane = (Handle( Geom_Plane )::DownCast( aSurf ))->Pln();
- Offset = 0.0e0;
}
if (Result == Standard_False)
{
TheType == STANDARD_TYPE(Geom_ToroidalSurface))
{
aSurf = (Handle( Geom_OffsetSurface )::DownCast( aSurf ))->Surface();
- Offset = 0.0e0;
}
else
{
//function : InitFaceLength
//purpose :
//=======================================================================
-void AIS::InitFaceLength (const TopoDS_Face& aFace,
- gp_Pln & aPlane,
- Handle(Geom_Surface) & aSurface,
- AIS_KindOfSurface & aSurfaceType,
- Standard_Real & anOffset)
+void AIS::InitFaceLength (const TopoDS_Face& theFace,
+ gp_Pln& thePlane,
+ Handle(Geom_Surface)& theSurface,
+ AIS_KindOfSurface& theSurfaceType,
+ Standard_Real& theOffset)
{
- AIS::GetPlaneFromFace( aFace, aPlane, aSurface, aSurfaceType, anOffset );
-
- if (Abs( anOffset ) > Precision::Confusion())
- {
- aSurface = new Geom_OffsetSurface( aSurface, anOffset );
- anOffset = 0.0e0;
- }
-
+ if (AIS::GetPlaneFromFace (theFace, thePlane, theSurface, theSurfaceType, theOffset)
+ && Abs (theOffset) > Precision::Confusion())
+ {
+ theSurface = new Geom_OffsetSurface (theSurface, theOffset);
+ theOffset = 0.0e0;
+ }
}
//=======================================================================
# execute test scripts
if { [file exists $scriptsdir/$group/begin] } {
puts "Executing $scriptsdir/$group/begin..."; flush stdout
- uplevel source $scriptsdir/$group/begin
+ uplevel source -encoding utf-8 $scriptsdir/$group/begin
}
if { [file exists $scriptsdir/$group/$gridname/begin] } {
puts "Executing $scriptsdir/$group/$gridname/begin..."; flush stdout
- uplevel source $scriptsdir/$group/$gridname/begin
+ uplevel source -encoding utf-8 $scriptsdir/$group/$gridname/begin
}
puts "Executing $casefile..."; flush stdout
- uplevel source $casefile
+ uplevel source -encoding utf-8 $casefile
if { [file exists $scriptsdir/$group/$gridname/end] } {
puts "Executing $scriptsdir/$group/$gridname/end..."; flush stdout
- uplevel source $scriptsdir/$group/$gridname/end
+ uplevel source -encoding utf-8 $scriptsdir/$group/$gridname/end
}
if { [file exists $scriptsdir/$group/end] } {
puts "Executing $scriptsdir/$group/end..."; flush stdout
- uplevel source $scriptsdir/$group/end
+ uplevel source -encoding utf-8 $scriptsdir/$group/end
}
} res] {
puts "Tcl Exception: $res"
Font_NameOfFont.hxx
Font_TextFormatter.hxx
Font_TextFormatter.cxx
+Font_UnicodeSubset.hxx
{
theShape.Nullify();
if (!loadGlyph (theChar)
- || myFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
+ || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
{
return Standard_False;
}
return !theShape.IsNull();
}
- FT_Outline& anOutline = myFTFace->glyph->outline;
-
+ const FT_Outline& anOutline = myActiveFTFace->glyph->outline;
if (!anOutline.n_contours)
return Standard_False;
#include <Message.hxx>
#include <Message_Messenger.hxx>
+#include <algorithm>
+
#include <ft2build.h>
#include FT_FREETYPE_H
Font_FTFont::Font_FTFont (const Handle(Font_FTLibrary)& theFTLib)
: myFTLib (theFTLib),
myFTFace (NULL),
+ myActiveFTFace(NULL),
+ myFontAspect (Font_FontAspect_Regular),
myWidthScaling(1.0),
myLoadFlags (FT_LOAD_NO_HINTING | FT_LOAD_TARGET_NORMAL),
- myUChar (0U)
+ myUChar (0U),
+ myToUseUnicodeSubsetFallback (Font_FontMgr::ToUseUnicodeSubsetFallback())
{
if (myFTLib.IsNull())
{
FT_Done_Face (myFTFace);
myFTFace = NULL;
}
+ myActiveFTFace = NULL;
myBuffer.Nullify();
}
FT_Set_Transform (myFTFace, &aMat, 0);
}
+ myActiveFTFace = myFTFace;
return true;
}
Handle(Font_FTFont) aFont = new Font_FTFont();
if (aFont->Init (aPath, aParams))
{
+ aFont->myFontAspect = aFontAspect;
return aFont;
}
}
Font_StrictLevel theStrictLevel)
{
Font_FTFontParams aParams = theParams;
- Font_FontAspect aFontAspect = theFontAspect;
+ myFontAspect = theFontAspect;
Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
- if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName.ToCString(), theStrictLevel, aFontAspect))
+ if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFont (theFontName.ToCString(), theStrictLevel, myFontAspect))
{
if (aRequestedFont->IsSingleStrokeFont())
{
aParams.IsSingleStrokeFont = true;
}
- const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (aFontAspect, aParams.ToSynthesizeItalic);
+ const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
return Init (aPath, aParams);
}
Release();
return false;
}
+// =======================================================================
+// function : findAndInitFallback
+// purpose :
+// =======================================================================
+bool Font_FTFont::findAndInitFallback (Font_UnicodeSubset theSubset)
+{
+ if (!myFallbackFaces[theSubset].IsNull())
+ {
+ return myFallbackFaces[theSubset]->IsValid();
+ }
+
+ myFallbackFaces[theSubset] = new Font_FTFont (myFTLib);
+ myFallbackFaces[theSubset]->myToUseUnicodeSubsetFallback = false; // no recursion
+
+ Handle(Font_FontMgr) aFontMgr = Font_FontMgr::GetInstance();
+ if (Handle(Font_SystemFont) aRequestedFont = aFontMgr->FindFallbackFont (theSubset, myFontAspect))
+ {
+ Font_FTFontParams aParams = myFontParams;
+ aParams.IsSingleStrokeFont = aRequestedFont->IsSingleStrokeFont();
+
+ const TCollection_AsciiString& aPath = aRequestedFont->FontPathAny (myFontAspect, aParams.ToSynthesizeItalic);
+ if (myFallbackFaces[theSubset]->Init (aPath, aParams))
+ {
+ Message::DefaultMessenger()->Send (TCollection_AsciiString ("Font_FTFont, using fallback font '") + aRequestedFont->FontName() + "'"
+ + " for symbols unsupported by '" + myFTFace->family_name + "'", Message_Trace);
+ }
+ }
+ return myFallbackFaces[theSubset]->IsValid();
+}
+
+// =======================================================================
+// function : HasSymbol
+// purpose :
+// =======================================================================
+bool Font_FTFont::HasSymbol (Standard_Utf32Char theUChar) const
+{
+ return FT_Get_Char_Index (myFTFace, theUChar) != 0;
+}
+
// =======================================================================
// function : loadGlyph
// purpose :
myGlyphImg.Clear();
myUChar = 0;
- if (theUChar == 0
- || FT_Load_Char (myFTFace, theUChar, FT_Int32(myLoadFlags)) != 0
- || myFTFace->glyph == NULL)
+ myActiveFTFace = myFTFace;
+ if (theUChar == 0)
+ {
+ return false;
+ }
+
+ if (myToUseUnicodeSubsetFallback
+ && !HasSymbol (theUChar))
+ {
+ // try using fallback
+ const Font_UnicodeSubset aSubset = CharSubset (theUChar);
+ if (findAndInitFallback (aSubset)
+ && myFallbackFaces[aSubset]->HasSymbol (theUChar))
+ {
+ myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
+ }
+ }
+
+ if (FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags)) != 0
+ || myActiveFTFace->glyph == NULL)
{
return false;
}
{
myGlyphImg.Clear();
myUChar = 0;
+ myActiveFTFace = myFTFace;
+
+ if (theUChar != 0
+ && myToUseUnicodeSubsetFallback
+ && !HasSymbol (theUChar))
+ {
+ // try using fallback
+ const Font_UnicodeSubset aSubset = CharSubset (theUChar);
+ if (findAndInitFallback (aSubset)
+ && myFallbackFaces[aSubset]->HasSymbol (theUChar))
+ {
+ myActiveFTFace = myFallbackFaces[aSubset]->myFTFace;
+ }
+ }
+
if (theUChar == 0
- || FT_Load_Char (myFTFace, theUChar, FT_Int32(myLoadFlags | FT_LOAD_RENDER)) != 0
- || myFTFace->glyph == NULL
- || myFTFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
+ || FT_Load_Char (myActiveFTFace, theUChar, FT_Int32(myLoadFlags | FT_LOAD_RENDER)) != 0
+ || myActiveFTFace->glyph == NULL
+ || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_BITMAP)
{
return false;
}
- FT_Bitmap aBitmap = myFTFace->glyph->bitmap;
- if (aBitmap.pixel_mode != FT_PIXEL_MODE_GRAY
- || aBitmap.buffer == NULL || aBitmap.width == 0 || aBitmap.rows == 0)
+ FT_Bitmap aBitmap = myActiveFTFace->glyph->bitmap;
+ if (aBitmap.buffer == NULL || aBitmap.width == 0 || aBitmap.rows == 0)
{
return false;
}
- if (!myGlyphImg.InitWrapper (Image_Format_Alpha, aBitmap.buffer,
- aBitmap.width, aBitmap.rows, Abs (aBitmap.pitch)))
+
+ if (aBitmap.pixel_mode == FT_PIXEL_MODE_GRAY)
+ {
+ if (!myGlyphImg.InitWrapper (Image_Format_Alpha, aBitmap.buffer,
+ aBitmap.width, aBitmap.rows, Abs (aBitmap.pitch)))
+ {
+ return false;
+ }
+ myGlyphImg.SetTopDown (aBitmap.pitch > 0);
+ }
+ else if (aBitmap.pixel_mode == FT_PIXEL_MODE_MONO)
+ {
+ if (!myGlyphImg.InitTrash (Image_Format_Gray, aBitmap.width, aBitmap.rows))
+ {
+ return false;
+ }
+
+ myGlyphImg.SetTopDown (aBitmap.pitch > 0);
+ const int aNumOfBytesInRow = aBitmap.width / 8 + (aBitmap.width % 8 ? 1 : 0);
+ for (int aRow = 0; aRow < (int )aBitmap.rows; ++aRow)
+ {
+ for (int aCol = 0; aCol < (int )aBitmap.width; ++aCol)
+ {
+ const int aBitOn = aBitmap.buffer[aNumOfBytesInRow * aRow + aCol / 8] & (0x80 >> (aCol % 8));
+ //*myGlyphImg.ChangeRawValue (aRow, aCol) = aBitOn ? 255 : 0;
+ myGlyphImg.ChangeRow (aRow)[aCol] = aBitOn ? 255 : 0;
+ }
+ }
+ }
+ else
{
return false;
}
- myGlyphImg.SetTopDown (aBitmap.pitch > 0);
+
myUChar = theUChar;
return true;
}
// function : GlyphMaxSizeX
// purpose :
// =======================================================================
-unsigned int Font_FTFont::GlyphMaxSizeX() const
+unsigned int Font_FTFont::GlyphMaxSizeX (bool theToIncludeFallback) const
{
- float aWidth = (FT_IS_SCALABLE(myFTFace) != 0)
- ? float(myFTFace->bbox.xMax - myFTFace->bbox.xMin) * (float(myFTFace->size->metrics.x_ppem) / float(myFTFace->units_per_EM))
- : fromFTPoints<float> (myFTFace->size->metrics.max_advance);
- return (unsigned int)(aWidth + 0.5f);
+ if (!theToIncludeFallback)
+ {
+ float aWidth = (FT_IS_SCALABLE(myFTFace) != 0)
+ ? float(myFTFace->bbox.xMax - myFTFace->bbox.xMin) * (float(myFTFace->size->metrics.x_ppem) / float(myFTFace->units_per_EM))
+ : fromFTPoints<float> (myFTFace->size->metrics.max_advance);
+ return (unsigned int)(aWidth + 0.5f);
+ }
+
+ unsigned int aWidth = GlyphMaxSizeX (false);
+ if (theToIncludeFallback)
+ {
+ for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
+ {
+ if (!myFallbackFaces[aFontIter].IsNull()
+ && myFallbackFaces[aFontIter]->IsValid())
+ {
+ aWidth = std::max (aWidth, myFallbackFaces[aFontIter]->GlyphMaxSizeX (false));
+ }
+ }
+ }
+ return aWidth;
}
// =======================================================================
// function : GlyphMaxSizeY
// purpose :
// =======================================================================
-unsigned int Font_FTFont::GlyphMaxSizeY() const
+unsigned int Font_FTFont::GlyphMaxSizeY (bool theToIncludeFallback) const
{
- float aHeight = (FT_IS_SCALABLE(myFTFace) != 0)
- ? float(myFTFace->bbox.yMax - myFTFace->bbox.yMin) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM))
- : fromFTPoints<float> (myFTFace->size->metrics.height);
- return (unsigned int)(aHeight + 0.5f);
+ if (!theToIncludeFallback)
+ {
+ float aHeight = (FT_IS_SCALABLE(myFTFace) != 0)
+ ? float(myFTFace->bbox.yMax - myFTFace->bbox.yMin) * (float(myFTFace->size->metrics.y_ppem) / float(myFTFace->units_per_EM))
+ : fromFTPoints<float> (myFTFace->size->metrics.height);
+ return (unsigned int)(aHeight + 0.5f);
+ }
+
+ unsigned int aHeight = GlyphMaxSizeY (false);
+ if (theToIncludeFallback)
+ {
+ for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
+ {
+ if (!myFallbackFaces[aFontIter].IsNull()
+ && myFallbackFaces[aFontIter]->IsValid())
+ {
+ aHeight = std::max (aHeight, myFallbackFaces[aFontIter]->GlyphMaxSizeY (false));
+ }
+ }
+ }
+ return aHeight;
}
// =======================================================================
return AdvanceY (theUCharNext);
}
+// =======================================================================
+// function : getKerning
+// purpose :
+// =======================================================================
bool Font_FTFont::getKerning (FT_Vector& theKern,
Standard_Utf32Char theUCharCurr,
Standard_Utf32Char theUCharNext) const
{
theKern.x = 0;
theKern.y = 0;
- if (theUCharNext != 0 && FT_HAS_KERNING(myFTFace) != 0)
+ if (theUCharNext != 0 && FT_HAS_KERNING(myActiveFTFace) != 0)
{
- const FT_UInt aCharCurr = FT_Get_Char_Index (myFTFace, theUCharCurr);
- const FT_UInt aCharNext = FT_Get_Char_Index (myFTFace, theUCharNext);
+ const FT_UInt aCharCurr = FT_Get_Char_Index (myActiveFTFace, theUCharCurr);
+ const FT_UInt aCharNext = FT_Get_Char_Index (myActiveFTFace, theUCharNext);
if (aCharCurr == 0 || aCharNext == 0
- || FT_Get_Kerning (myFTFace, aCharCurr, aCharNext, FT_KERNING_UNFITTED, &theKern) != 0)
+ || FT_Get_Kerning (myActiveFTFace, aCharCurr, aCharNext, FT_KERNING_UNFITTED, &theKern) != 0)
{
theKern.x = 0;
theKern.y = 0;
FT_Vector aKern;
getKerning (aKern, myUChar, theUCharNext);
- return myWidthScaling * fromFTPoints<float> (myFTFace->glyph->advance.x + aKern.x);
+ return myWidthScaling * fromFTPoints<float> (myActiveFTFace->glyph->advance.x + aKern.x);
}
// =======================================================================
FT_Vector aKern;
getKerning (aKern, myUChar, theUCharNext);
- return fromFTPoints<float> (myFTFace->glyph->advance.y + aKern.y);
+ return fromFTPoints<float> (myActiveFTFace->glyph->advance.y + aKern.y);
}
// =======================================================================
// function : GlyphsNumber
// purpose :
// =======================================================================
-Standard_Integer Font_FTFont::GlyphsNumber() const
+Standard_Integer Font_FTFont::GlyphsNumber (bool theToIncludeFallback) const
{
- return myFTFace->num_glyphs;
+ Standard_Integer aNbGlyphs = myFTFace->num_glyphs;
+ if (theToIncludeFallback)
+ {
+ for (Standard_Integer aFontIter = 0; aFontIter < Font_UnicodeSubset_NB; ++aFontIter)
+ {
+ if (!myFallbackFaces[aFontIter].IsNull()
+ && myFallbackFaces[aFontIter]->IsValid())
+ {
+ aNbGlyphs += myFallbackFaces[aFontIter]->GlyphsNumber (false);
+ }
+ }
+ }
+ return aNbGlyphs;
}
// =======================================================================
-// function : theRect
+// function : GlyphRect
// purpose :
// =======================================================================
void Font_FTFont::GlyphRect (Font_Rect& theRect) const
{
- const FT_Bitmap& aBitmap = myFTFace->glyph->bitmap;
- theRect.Left = float(myFTFace->glyph->bitmap_left);
- theRect.Top = float(myFTFace->glyph->bitmap_top);
- theRect.Right = float(myFTFace->glyph->bitmap_left + (int )aBitmap.width);
- theRect.Bottom = float(myFTFace->glyph->bitmap_top - (int )aBitmap.rows);
+ const FT_Bitmap& aBitmap = myActiveFTFace->glyph->bitmap;
+ theRect.Left = float(myActiveFTFace->glyph->bitmap_left);
+ theRect.Top = float(myActiveFTFace->glyph->bitmap_top);
+ theRect.Right = float(myActiveFTFace->glyph->bitmap_left + (int )aBitmap.width);
+ theRect.Bottom = float(myActiveFTFace->glyph->bitmap_top - (int )aBitmap.rows);
}
// =======================================================================
#include <Font_FontAspect.hxx>
#include <Font_Rect.hxx>
#include <Font_StrictLevel.hxx>
+#include <Font_UnicodeSubset.hxx>
#include <Graphic3d_HorizontalTextAlignment.hxx>
#include <Graphic3d_VerticalTextAlignment.hxx>
#include <Image_PixMap.hxx>
//! Font initialization parameters.
struct Font_FTFontParams
{
- unsigned int PointSize; //!< face size in points (1/72 inch)
- unsigned int Resolution; //!< resolution of the target device in dpi for FT_Set_Char_Size()
- bool ToSynthesizeItalic; //!< generate italic style (e.g. for font family having no italic style); FALSE by default
- bool IsSingleStrokeFont; //!< single-stroke (one-line) font, FALSE by default
+ unsigned int PointSize; //!< face size in points (1/72 inch)
+ unsigned int Resolution; //!< resolution of the target device in dpi for FT_Set_Char_Size()
+ bool ToSynthesizeItalic; //!< generate italic style (e.g. for font family having no italic style); FALSE by default
+ bool IsSingleStrokeFont; //!< single-stroke (one-line) font, FALSE by default
//! Empty constructor.
Font_FTFontParams() : PointSize (0), Resolution (72u), ToSynthesizeItalic (false), IsSingleStrokeFont (false) {}
const Font_FTFontParams& theParams,
const Font_StrictLevel theStrictLevel = Font_StrictLevel_Any);
+ //! Return TRUE if specified character is within subset of modern CJK characters.
+ static bool IsCharFromCJK (Standard_Utf32Char theUChar)
+ {
+ return (theUChar >= 0x03400 && theUChar <= 0x04DFF)
+ || (theUChar >= 0x04E00 && theUChar <= 0x09FFF)
+ || (theUChar >= 0x0F900 && theUChar <= 0x0FAFF)
+ || (theUChar >= 0x20000 && theUChar <= 0x2A6DF)
+ || (theUChar >= 0x2F800 && theUChar <= 0x2FA1F)
+ // Hiragana and Katakana (Japanese) are NOT part of CJK, but CJK fonts usually include these symbols
+ || IsCharFromHiragana (theUChar)
+ || IsCharFromKatakana (theUChar);
+ }
+
+ //! Return TRUE if specified character is within subset of Hiragana (Japanese).
+ static bool IsCharFromHiragana (Standard_Utf32Char theUChar)
+ {
+ return (theUChar >= 0x03040 && theUChar <= 0x0309F);
+ }
+
+ //! Return TRUE if specified character is within subset of Katakana (Japanese).
+ static bool IsCharFromKatakana (Standard_Utf32Char theUChar)
+ {
+ return (theUChar >= 0x030A0 && theUChar <= 0x030FF);
+ }
+
+ //! Return TRUE if specified character is within subset of modern Korean characters (Hangul).
+ static bool IsCharFromKorean (Standard_Utf32Char theUChar)
+ {
+ return (theUChar >= 0x01100 && theUChar <= 0x011FF)
+ || (theUChar >= 0x03130 && theUChar <= 0x0318F)
+ || (theUChar >= 0x0AC00 && theUChar <= 0x0D7A3);
+ }
+
+ //! Return TRUE if specified character is within subset of Arabic characters.
+ static bool IsCharFromArabic (Standard_Utf32Char theUChar)
+ {
+ return (theUChar >= 0x00600 && theUChar <= 0x006FF);
+ }
+
+ //! Return TRUE if specified character should be displayed in Right-to-Left order.
+ static bool IsCharRightToLeft (Standard_Utf32Char theUChar)
+ {
+ return IsCharFromArabic(theUChar);
+ }
+
+ //! Determine Unicode subset for specified character
+ static Font_UnicodeSubset CharSubset (Standard_Utf32Char theUChar)
+ {
+ if (IsCharFromCJK (theUChar))
+ {
+ return Font_UnicodeSubset_CJK;
+ }
+ else if (IsCharFromKorean (theUChar))
+ {
+ return Font_UnicodeSubset_Korean;
+ }
+ else if (IsCharFromArabic (theUChar))
+ {
+ return Font_UnicodeSubset_Arabic;
+ }
+ return Font_UnicodeSubset_Western;
+ }
+
public:
//! Create uninitialized instance.
const Font_FTFontParams& theParams,
Font_StrictLevel theStrictLevel = Font_StrictLevel_Any);
+ //! Return flag to use fallback fonts in case if used font does not include symbols from specific Unicode subset; TRUE by default.
+ //! @sa Font_FontMgr::ToUseUnicodeSubsetFallback()
+ Standard_Boolean ToUseUnicodeSubsetFallback() const { return myToUseUnicodeSubsetFallback; }
+
+ //! Set if fallback fonts should be used in case if used font does not include symbols from specific Unicode subset.
+ void SetUseUnicodeSubsetFallback (Standard_Boolean theToFallback) { myToUseUnicodeSubsetFallback = theToFallback; }
+
//! Return TRUE if this is single-stroke (one-line) font, FALSE by default.
//! Such fonts define single-line glyphs instead of closed contours, so that they are rendered incorrectly by normal software.
bool IsSingleStrokeFont() const { return myFontParams.IsSingleStrokeFont; }
Standard_EXPORT bool RenderGlyph (const Standard_Utf32Char theChar);
//! @return maximal glyph width in pixels (rendered to bitmap).
- Standard_EXPORT unsigned int GlyphMaxSizeX() const;
+ Standard_EXPORT unsigned int GlyphMaxSizeX (bool theToIncludeFallback = false) const;
//! @return maximal glyph height in pixels (rendered to bitmap).
- Standard_EXPORT unsigned int GlyphMaxSizeY() const;
+ Standard_EXPORT unsigned int GlyphMaxSizeY (bool theToIncludeFallback = false) const;
//! @return vertical distance from the horizontal baseline to the highest character coordinate.
Standard_EXPORT float Ascender() const;
myWidthScaling = theScaleFactor;
}
+ //! Return TRUE if font contains specified symbol (excluding fallback list).
+ Standard_EXPORT bool HasSymbol (Standard_Utf32Char theUChar) const;
+
//! Compute horizontal advance to the next character with kerning applied when applicable.
//! Assuming text rendered horizontally.
//! @param theUCharNext the next character to compute advance from current one
Standard_EXPORT float AdvanceY (Standard_Utf32Char theUChar,
Standard_Utf32Char theUCharNext);
- //! @return glyphs number in this font.
- Standard_EXPORT Standard_Integer GlyphsNumber() const;
+ //! Return glyphs number in this font.
+ //! @param theToIncludeFallback if TRUE then the number will include fallback list
+ Standard_EXPORT Standard_Integer GlyphsNumber (bool theToIncludeFallback = false) const;
//! Retrieve glyph bitmap rectangle
Standard_EXPORT void GlyphRect (Font_Rect& theRect) const;
Standard_Utf32Char theUCharCurr,
Standard_Utf32Char theUCharNext) const;
+ //! Initialize fallback font.
+ Standard_EXPORT bool findAndInitFallback (Font_UnicodeSubset theSubset);
+
protected:
Handle(Font_FTLibrary) myFTLib; //!< handle to the FT library object
Handle(NCollection_Buffer) myBuffer; //!< memory buffer
+ Handle(Font_FTFont) myFallbackFaces[Font_UnicodeSubset_NB]; //!< fallback fonts
FT_Face myFTFace; //!< FT face object
+ FT_Face myActiveFTFace; //!< active FT face object (the main of fallback)
TCollection_AsciiString myFontPath; //!< font path
Font_FTFontParams myFontParams; //!< font initialization parameters
+ Font_FontAspect myFontAspect; //!< font initialization aspect
float myWidthScaling; //!< scale glyphs along X-axis
int32_t myLoadFlags; //!< default load flags
Image_PixMap myGlyphImg; //!< cached glyph plane
Standard_Utf32Char myUChar; //!< currently loaded unicode character
+ Standard_Boolean myToUseUnicodeSubsetFallback; //!< use default fallback fonts for extended Unicode sub-sets (Korean, CJK, etc.)
};
return _mgr;
}
+// =======================================================================
+// function : ToUseUnicodeSubsetFallback
+// purpose :
+// =======================================================================
+Standard_Boolean& Font_FontMgr::ToUseUnicodeSubsetFallback()
+{
+ static Standard_Boolean TheToUseUnicodeSubsetFallback = true;
+ return TheToUseUnicodeSubsetFallback;
+}
+
// =======================================================================
// function : addFontAlias
// purpose :
Handle(Font_FontAliasSequence) anIris = new Font_FontAliasSequence();
Handle(Font_FontAliasSequence) aCJK = new Font_FontAliasSequence();
Handle(Font_FontAliasSequence) aKorean = new Font_FontAliasSequence();
+ Handle(Font_FontAliasSequence) anArab = new Font_FontAliasSequence();
// best matches - pre-installed on Windows, some of them are pre-installed on macOS,
// and sometimes them can be found installed on other systems (by user)
aKorean->Append (Font_FontAlias ("noto serif cjk jp")); // Linux
aKorean->Append (Font_FontAlias ("noto sans cjk jp")); // Linux
+#if defined(_WIN32)
+ anArab->Append (Font_FontAlias ("times new roman"));
+#elif defined(__APPLE__)
+ anArab->Append (Font_FontAlias ("decotype naskh"));
+#elif defined(__ANDROID__)
+ anArab->Append (Font_FontAlias ("droid arabic naskh"));
+ anArab->Append (Font_FontAlias ("noto naskh arabic"));
+#endif
+
addFontAlias ("mono", aMono);
addFontAlias ("courier", aMono); // Font_NOF_ASCII_MONO
addFontAlias ("monospace", aMono); // Font_NOF_MONOSPACE
addFontAlias ("korean", aKorean); // Font_NOF_KOREAN
addFontAlias ("cjk", aCJK); // Font_NOF_CJK
addFontAlias ("nsimsun", aCJK);
+ addFontAlias ("arabic", anArab); // Font_NOF_ARABIC
addFontAlias (Font_NOF_SYMBOL_MONO, aWinDin);
addFontAlias (Font_NOF_ASCII_SCRIPT_SIMPLEX, aScript);
return myFontMap.Find (theFontName);
}
+// =======================================================================
+// function : FindFallbackFont
+// purpose :
+// =======================================================================
+Handle(Font_SystemFont) Font_FontMgr::FindFallbackFont (Font_UnicodeSubset theSubset,
+ Font_FontAspect theFontAspect) const
+{
+ Font_FontAspect aFontAspect = theFontAspect;
+ switch (theSubset)
+ {
+ case Font_UnicodeSubset_Western: return FindFont (Font_NOF_SANS_SERIF, Font_StrictLevel_Aliases, aFontAspect);
+ case Font_UnicodeSubset_Korean: return FindFont (Font_NOF_KOREAN, Font_StrictLevel_Aliases, aFontAspect);
+ case Font_UnicodeSubset_CJK: return FindFont (Font_NOF_CJK, Font_StrictLevel_Aliases, aFontAspect);
+ case Font_UnicodeSubset_Arabic: return FindFont (Font_NOF_ARABIC, Font_StrictLevel_Aliases, aFontAspect);
+ }
+ return Handle(Font_SystemFont)();
+}
+
// =======================================================================
// function : FindFont
// purpose :
}
}
- if (aFont.IsNull())
+ if (aFont.IsNull()
+ && theStrictLevel == Font_StrictLevel_Any)
{
// try finding ANY font in case if even default fallback alias myFallbackAlias cannot be found
aFont = myFontMap.Find (TCollection_AsciiString());
#include <Font_FontAspect.hxx>
#include <Font_NListOfSystemFont.hxx>
#include <Font_StrictLevel.hxx>
+#include <Font_UnicodeSubset.hxx>
#include <NCollection_DataMap.hxx>
#include <NCollection_IndexedMap.hxx>
#include <NCollection_Shared.hxx>
return "invalid";
}
+ //! Return flag to use fallback fonts in case if used font does not include symbols from specific Unicode subset; TRUE by default.
+ Standard_EXPORT static Standard_Boolean& ToUseUnicodeSubsetFallback();
+
public:
//! Return the list of available fonts.
{
return FindFont (theFontName, Font_StrictLevel_Any, theFontAspect);
}
-
+
+ //! Tries to find fallback font for specified Unicode subset.
+ //! Returns NULL in case when fallback font is not found in the system.
+ //! @param theSubset [in] Unicode subset
+ //! @param theFontAspect [in] font aspect to find
+ Standard_EXPORT Handle(Font_SystemFont) FindFallbackFont (Font_UnicodeSubset theSubset,
+ Font_FontAspect theFontAspect) const;
+
//! Read font file and retrieve information from it.
Standard_EXPORT Handle(Font_SystemFont) CheckFont (const Standard_CString theFontPath) const;
#define Font_NOF_MONOSPACE "monospace"
#define Font_NOF_SERIF "serif"
#define Font_NOF_SANS_SERIF "sans-serif"
-#define Font_NOF_CJK "cjk"
-#define Font_NOF_KOREAN "korean"
+#define Font_NOF_CJK "cjk" // Font_UnicodeSubset_CJK
+#define Font_NOF_KOREAN "korean" // Font_UnicodeSubset_Korean
+#define Font_NOF_ARABIC "arabic" // Font_UnicodeSubset_Arabic
#define Font_NOF_ASCII_MONO "Courier"
#define Font_NOF_ASCII_SIMPLEX "Times-Roman"
--- /dev/null
+// Copyright (c) 2019 OPEN CASCADE SAS
+//
+// This file is part of Open CASCADE Technology software library.
+//
+// This library is free software; you can redistribute it and/or modify it under
+// the terms of the GNU Lesser General Public License version 2.1 as published
+// by the Free Software Foundation, with special exception defined in the file
+// OCCT_LGPL_EXCEPTION.txt. Consult the file LICENSE_LGPL_21.txt included in OCCT
+// distribution for complete text of the license and disclaimer of any warranty.
+//
+// Alternatively, this file may be used under the terms of Open CASCADE
+// commercial license or contractual agreement.
+
+#ifndef _Font_UnicodeSubset_HeaderFile
+#define _Font_UnicodeSubset_HeaderFile
+
+//! Enumeration defining Unicode subsets.
+enum Font_UnicodeSubset
+{
+ Font_UnicodeSubset_Western, //!< western letters
+ Font_UnicodeSubset_Korean, //!< modern Korean letters
+ Font_UnicodeSubset_CJK, //!< Chinese characters (Chinese, Japanese, Korean and Vietnam)
+ Font_UnicodeSubset_Arabic, //!< Arabic characters
+};
+
+enum { Font_UnicodeSubset_NB = Font_UnicodeSubset_Arabic };
+
+#endif // _Font_UnicodeSubset_HeaderFile
#include <Standard_Assert.hxx>
#include <TCollection_ExtendedString.hxx>
-
IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Font,OpenGl_Resource)
// =======================================================================
myFont (theFont),
myAscender (0.0f),
myDescender (0.0f),
- myLineSpacing (0.0f),
- myTileSizeX (0),
myTileSizeY (0),
myLastTileId (-1),
myTextureFormat (GL_ALPHA)
return false;
}
- myAscender = myFont->Ascender();
- myDescender = myFont->Descender();
- myLineSpacing = myFont->LineSpacing();
- myTileSizeX = myFont->GlyphMaxSizeX();
- myTileSizeY = myFont->GlyphMaxSizeY();
+ myAscender = myFont->Ascender();
+ myDescender = myFont->Descender();
+ myTileSizeY = myFont->GlyphMaxSizeY (true);
myLastTileId = -1;
if (!createTexture (theCtx))
// =======================================================================
bool OpenGl_Font::createTexture (const Handle(OpenGl_Context)& theCtx)
{
- const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
-
- Standard_Integer aGlyphsNb = myFont->GlyphsNumber() - myLastTileId + 1;
-
- const Standard_Integer aTextureSizeX = OpenGl_Context::GetPowerOfTwo (aGlyphsNb * myTileSizeX, aMaxSize);
- const Standard_Integer aTilesPerRow = aTextureSizeX / myTileSizeX;
+ // Single font might define very wide range of symbols, with very few of them actually used in text.
+ // Limit single texture with circa 4096 glyphs.
+ static const Standard_Integer THE_MAX_GLYPHS_PER_TEXTURE = 4096;
+
+ myTileSizeY = myFont->GlyphMaxSizeY (true);
+ const Standard_Integer aGlyphsNb = Min (THE_MAX_GLYPHS_PER_TEXTURE, myFont->GlyphsNumber (true) - myLastTileId + 1);
+ const Standard_Integer aMaxTileSizeX = myFont->GlyphMaxSizeX (true);
+ const Standard_Integer aMaxSize = theCtx->MaxTextureSize();
+ const Standard_Integer aTextureSizeX = OpenGl_Context::GetPowerOfTwo (aGlyphsNb * aMaxTileSizeX, aMaxSize);
+ const Standard_Integer aTilesPerRow = aTextureSizeX / aMaxTileSizeX;
const Standard_Integer aTextureSizeY = OpenGl_Context::GetPowerOfTwo (GLint((aGlyphsNb / aTilesPerRow) + 1) * myTileSizeY, aMaxSize);
memset (&myLastTilePx, 0, sizeof(myLastTilePx));
const Standard_Integer aTileId = myLastTileId + 1;
myLastTilePx.Left = myLastTilePx.Right + 3;
myLastTilePx.Right = myLastTilePx.Left + (Standard_Integer )anImg.SizeX();
- if (myLastTilePx.Right >= aTexture->SizeX())
+ if (myLastTilePx.Right > aTexture->SizeX()
+ || (Standard_Integer )anImg.SizeY() > myTileSizeY)
{
+ myTileSizeY = myFont->GlyphMaxSizeY (true);
+
myLastTilePx.Left = 0;
myLastTilePx.Right = (Standard_Integer )anImg.SizeX();
myLastTilePx.Top += myTileSizeY;
myLastTilePx.Bottom += myTileSizeY;
- if (myLastTilePx.Bottom >= aTexture->SizeY())
+ if (myLastTilePx.Bottom > aTexture->SizeY()
+ || myLastTilePx.Right > aTexture->SizeX())
{
if (!createTexture (theCtx))
{
return myDescender;
}
- //! @return default line spacing (the baseline-to-baseline distance)
- inline float LineSpacing() const
- {
- return myLineSpacing;
- }
-
//! Render glyph to texture if not already.
//! @param theCtx active context
//! @param theUChar unicode symbol to render
Handle(Font_FTFont) myFont; //!< FreeType font instance
Standard_ShortReal myAscender; //!< ascender provided my FT font
Standard_ShortReal myDescender; //!< descender provided my FT font
- Standard_ShortReal myLineSpacing; //!< line spacing provided my FT font
- Standard_Integer myTileSizeX; //!< tile width
Standard_Integer myTileSizeY; //!< tile height
Standard_Integer myLastTileId; //!< id of last tile
RectI myLastTilePx;
}
aMgr->SetTraceAliases (toEnable);
}
+ else if (anArgCase == "-unicodefallback"
+ || anArgCase == "-fallback"
+ || anArgCase == "-touseunicodesubsetfallback")
+ {
+ bool toEnable = true;
+ if (anArgIter + 1 < theArgNb
+ && ViewerTest::ParseOnOff (theArgVec[anArgIter + 1], toEnable))
+ {
+ ++anArgIter;
+ }
+ Font_FontMgr::ToUseUnicodeSubsetFallback() = toEnable;
+ }
else
{
std::cerr << "Warning! Unknown argument '" << anArg << "'\n";
__FILE__, TextToBRep, group);
theCommands.Add ("vfont",
"vfont [-add pathToFont [fontName] [regular,bold,italic,boldItalic=undefined] [singleStroke]]"
- "\n\t\t: [-strict {any|aliases|strict}] [-find fontName [regular,bold,italic,boldItalic=undefined]] [-verbose {on|off}]",
+ "\n\t\t: [-strict {any|aliases|strict}] [-find fontName [regular,bold,italic,boldItalic=undefined]] [-verbose {on|off}]"
+ "\n\t\t: [-unicodeFallback {on|off}]",
__FILE__, VFont, group);
theCommands.Add ("vsetedgetype",
--- /dev/null
+puts "================"
+puts "0022149: Strings with Japanese characters can not be displayed in 3D viewer"
+puts "================"
+puts ""
+
+pload MODELING VISUALIZATION
+
+dtracelevel trace
+vfont -verbose 1
+vclear
+vinit View1
+vaxo
+vpoint p0 0 0 0
+
+pload MODELING VISUALIZATION
+dtracelevel trace
+vfont -verbose 1
+vclear
+vinit View1
+vtop
+vpoint p00 0 0 0
+vpoint p01 0 10 0
+vpoint p11 10 10 0
+vpoint p10 10 0 0
+vfit
+vzoom 0.8
+vdrawtext t0 "한국어 (Korean) Čeština" -pos 0 0 0 -halign left -font korean
+vdrawtext t1 "한국어 (Korean) Čeština" -pos 10 1 0 -halign right -font sans
+vdrawtext t2 "简体中文 (Chinese)" -pos 0 2 0 -halign left -font cjk
+vdrawtext t3 "简体中文 (Chinese)" -pos 10 3 0 -halign right -font sans
+vdrawtext t4 "あ (Japanese)" -pos 0 4 0 -halign left -font cjk
+vdrawtext t5 "あ (Japanese)" -pos 10 5 0 -halign right -font sans
+
+vdump $imagedir/${casename}.png
+
+# just print font list
+vfont