diff --git a/src/AIS/AIS.cxx b/src/AIS/AIS.cxx index fa84613c2f..d9129b05e4 100644 --- a/src/AIS/AIS.cxx +++ b/src/AIS/AIS.cxx @@ -783,6 +783,7 @@ Standard_Boolean AIS::GetPlaneFromFace(const TopoDS_Face& aFace, BRepAdaptor_Surface surf1( aFace ); Handle( Adaptor3d_HSurface ) surf2; Standard_Boolean isOffset = Standard_False; + Offset = 0.0; if (surf1.GetType() == GeomAbs_OffsetSurface) { @@ -801,7 +802,6 @@ Standard_Boolean AIS::GetPlaneFromFace(const TopoDS_Face& aFace, { aPlane = surf2->Plane(); aSurfType = AIS_KOS_Plane; - Offset = 0.; Result = Standard_True; } @@ -817,7 +817,6 @@ Standard_Boolean AIS::GetPlaneFromFace(const TopoDS_Face& aFace, gp_Pln thePlane( LinePos, LineDir ^ ExtrusionDir); aPlane = thePlane; aSurfType = AIS_KOS_Plane; - Offset = 0.; Result = Standard_True; } } @@ -826,7 +825,6 @@ Standard_Boolean AIS::GetPlaneFromFace(const TopoDS_Face& aFace, { aSurf = (Handle( Geom_OffsetSurface )::DownCast( aSurf ))->Surface(); aPlane = (Handle( Geom_Plane )::DownCast( aSurf ))->Pln(); - Offset = 0.0e0; } if (Result == Standard_False) { @@ -839,7 +837,6 @@ Standard_Boolean AIS::GetPlaneFromFace(const TopoDS_Face& aFace, TheType == STANDARD_TYPE(Geom_ToroidalSurface)) { aSurf = (Handle( Geom_OffsetSurface )::DownCast( aSurf ))->Surface(); - Offset = 0.0e0; } else { @@ -899,20 +896,18 @@ gp_Pnt AIS::ProjectPointOnLine( const gp_Pnt & aPoint, const gp_Lin & aLine ) //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; + } } //======================================================================= diff --git a/src/DrawResources/TestCommands.tcl b/src/DrawResources/TestCommands.tcl index bd79782cb7..d3626b1202 100644 --- a/src/DrawResources/TestCommands.tcl +++ b/src/DrawResources/TestCommands.tcl @@ -1298,23 +1298,23 @@ proc _run_test {scriptsdir group gridname casefile echo} { # 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" diff --git a/src/Font/FILES b/src/Font/FILES index bf614b54a2..c8440e44a9 100644 --- a/src/Font/FILES +++ b/src/Font/FILES @@ -17,3 +17,4 @@ Font_SystemFont.cxx Font_SystemFont.hxx Font_TextFormatter.hxx Font_TextFormatter.cxx +Font_UnicodeSubset.hxx diff --git a/src/Font/Font_BRepFont.cxx b/src/Font/Font_BRepFont.cxx index 571ea13201..2e2779aa1e 100755 --- a/src/Font/Font_BRepFont.cxx +++ b/src/Font/Font_BRepFont.cxx @@ -407,7 +407,7 @@ Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar, { theShape.Nullify(); if (!loadGlyph (theChar) - || myFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE) + || myActiveFTFace->glyph->format != FT_GLYPH_FORMAT_OUTLINE) { return Standard_False; } @@ -416,8 +416,7 @@ Standard_Boolean Font_BRepFont::renderGlyph (const Standard_Utf32Char theChar, return !theShape.IsNull(); } - FT_Outline& anOutline = myFTFace->glyph->outline; - + const FT_Outline& anOutline = myActiveFTFace->glyph->outline; if (!anOutline.n_contours) return Standard_False; diff --git a/src/Font/Font_FTFont.cxx b/src/Font/Font_FTFont.cxx index 22a1297c4d..2fa393cc6d 100755 --- a/src/Font/Font_FTFont.cxx +++ b/src/Font/Font_FTFont.cxx @@ -21,6 +21,8 @@ #include #include +#include + #include #include FT_FREETYPE_H @@ -33,9 +35,12 @@ IMPLEMENT_STANDARD_RTTIEXT(Font_FTFont,Standard_Transient) 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()) { @@ -66,6 +71,7 @@ void Font_FTFont::Release() FT_Done_Face (myFTFace); myFTFace = NULL; } + myActiveFTFace = NULL; myBuffer.Nullify(); } @@ -135,6 +141,7 @@ bool Font_FTFont::Init (const Handle(NCollection_Buffer)& theData, FT_Set_Transform (myFTFace, &aMat, 0); } + myActiveFTFace = myFTFace; return true; } @@ -161,6 +168,7 @@ Handle(Font_FTFont) Font_FTFont::FindAndCreate (const TCollection_AsciiString& t Handle(Font_FTFont) aFont = new Font_FTFont(); if (aFont->Init (aPath, aParams)) { + aFont->myFontAspect = aFontAspect; return aFont; } } @@ -177,22 +185,61 @@ bool Font_FTFont::FindAndInit (const TCollection_AsciiString& theFontName, 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 : @@ -206,9 +253,26 @@ bool Font_FTFont::loadGlyph (const Standard_Utf32Char theUChar) 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; } @@ -225,26 +289,67 @@ bool Font_FTFont::RenderGlyph (const Standard_Utf32Char theUChar) { 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; + } + } + } + else { return false; } - myGlyphImg.SetTopDown (aBitmap.pitch > 0); + myUChar = theUChar; return true; } @@ -253,24 +358,58 @@ bool Font_FTFont::RenderGlyph (const Standard_Utf32Char theUChar) // 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 (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 (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 (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 (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; } // ======================================================================= @@ -322,18 +461,22 @@ float Font_FTFont::AdvanceY (Standard_Utf32Char theUChar, 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; @@ -357,7 +500,7 @@ float Font_FTFont::AdvanceX (Standard_Utf32Char theUCharNext) const FT_Vector aKern; getKerning (aKern, myUChar, theUCharNext); - return myWidthScaling * fromFTPoints (myFTFace->glyph->advance.x + aKern.x); + return myWidthScaling * fromFTPoints (myActiveFTFace->glyph->advance.x + aKern.x); } // ======================================================================= @@ -373,29 +516,41 @@ float Font_FTFont::AdvanceY (Standard_Utf32Char theUCharNext) const FT_Vector aKern; getKerning (aKern, myUChar, theUCharNext); - return fromFTPoints (myFTFace->glyph->advance.y + aKern.y); + return fromFTPoints (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); } // ======================================================================= diff --git a/src/Font/Font_FTFont.hxx b/src/Font/Font_FTFont.hxx index bcabf531e4..4ba5743f62 100755 --- a/src/Font/Font_FTFont.hxx +++ b/src/Font/Font_FTFont.hxx @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -33,10 +34,10 @@ class Font_FTLibrary; //! 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) {} @@ -68,6 +69,69 @@ public: 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. @@ -119,6 +183,13 @@ public: 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; } @@ -136,10 +207,10 @@ public: 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; @@ -163,6 +234,9 @@ public: 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 @@ -187,8 +261,9 @@ public: 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; @@ -262,18 +337,25 @@ protected: 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.) }; diff --git a/src/Font/Font_FontMgr.cxx b/src/Font/Font_FontMgr.cxx index 034b33cd16..3fef8bdb77 100644 --- a/src/Font/Font_FontMgr.cxx +++ b/src/Font/Font_FontMgr.cxx @@ -193,6 +193,16 @@ Handle(Font_FontMgr) Font_FontMgr::GetInstance() return _mgr; } +// ======================================================================= +// function : ToUseUnicodeSubsetFallback +// purpose : +// ======================================================================= +Standard_Boolean& Font_FontMgr::ToUseUnicodeSubsetFallback() +{ + static Standard_Boolean TheToUseUnicodeSubsetFallback = true; + return TheToUseUnicodeSubsetFallback; +} + // ======================================================================= // function : addFontAlias // purpose : @@ -239,6 +249,7 @@ Font_FontMgr::Font_FontMgr() 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) @@ -289,6 +300,15 @@ Font_FontMgr::Font_FontMgr() 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 @@ -308,6 +328,7 @@ Font_FontMgr::Font_FontMgr() 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); @@ -686,6 +707,24 @@ Handle(Font_SystemFont) Font_FontMgr::GetFont (const TCollection_AsciiString& th 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 : @@ -770,7 +809,8 @@ Handle(Font_SystemFont) Font_FontMgr::FindFont (const TCollection_AsciiString& t } } - 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()); diff --git a/src/Font/Font_FontMgr.hxx b/src/Font/Font_FontMgr.hxx index e38af97be3..8f6bee2cf5 100644 --- a/src/Font/Font_FontMgr.hxx +++ b/src/Font/Font_FontMgr.hxx @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,9 @@ public: 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. @@ -106,7 +110,14 @@ public: { 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; diff --git a/src/Font/Font_NameOfFont.hxx b/src/Font/Font_NameOfFont.hxx index c63d869949..cbbd2b3db7 100644 --- a/src/Font/Font_NameOfFont.hxx +++ b/src/Font/Font_NameOfFont.hxx @@ -16,8 +16,9 @@ #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" diff --git a/src/Font/Font_UnicodeSubset.hxx b/src/Font/Font_UnicodeSubset.hxx new file mode 100644 index 0000000000..72256a25b3 --- /dev/null +++ b/src/Font/Font_UnicodeSubset.hxx @@ -0,0 +1,28 @@ +// 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 diff --git a/src/OpenGl/OpenGl_Font.cxx b/src/OpenGl/OpenGl_Font.cxx index 0692e87b56..34ddf425ab 100755 --- a/src/OpenGl/OpenGl_Font.cxx +++ b/src/OpenGl/OpenGl_Font.cxx @@ -21,7 +21,6 @@ #include #include - IMPLEMENT_STANDARD_RTTIEXT(OpenGl_Font,OpenGl_Resource) // ======================================================================= @@ -34,8 +33,6 @@ OpenGl_Font::OpenGl_Font (const Handle(Font_FTFont)& theFont, myFont (theFont), myAscender (0.0f), myDescender (0.0f), - myLineSpacing (0.0f), - myTileSizeX (0), myTileSizeY (0), myLastTileId (-1), myTextureFormat (GL_ALPHA) @@ -105,11 +102,9 @@ bool OpenGl_Font::Init (const Handle(OpenGl_Context)& theCtx) 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)) @@ -126,12 +121,16 @@ bool OpenGl_Font::Init (const Handle(OpenGl_Context)& theCtx) // ======================================================================= bool OpenGl_Font::createTexture (const Handle(OpenGl_Context)& theCtx) { - const Standard_Integer aMaxSize = theCtx->MaxTextureSize(); + // 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; - Standard_Integer aGlyphsNb = myFont->GlyphsNumber() - myLastTileId + 1; - - const Standard_Integer aTextureSizeX = OpenGl_Context::GetPowerOfTwo (aGlyphsNb * myTileSizeX, aMaxSize); - const Standard_Integer aTilesPerRow = aTextureSizeX / myTileSizeX; + 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)); @@ -186,14 +185,18 @@ bool OpenGl_Font::renderGlyph (const Handle(OpenGl_Context)& theCtx, 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)) { diff --git a/src/OpenGl/OpenGl_Font.hxx b/src/OpenGl/OpenGl_Font.hxx index a588762a5b..3373ac8918 100755 --- a/src/OpenGl/OpenGl_Font.hxx +++ b/src/OpenGl/OpenGl_Font.hxx @@ -106,12 +106,6 @@ public: 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 @@ -135,8 +129,6 @@ protected: 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; diff --git a/src/ViewerTest/ViewerTest_ObjectCommands.cxx b/src/ViewerTest/ViewerTest_ObjectCommands.cxx index 5b9560c0dc..c3d2b9c002 100644 --- a/src/ViewerTest/ViewerTest_ObjectCommands.cxx +++ b/src/ViewerTest/ViewerTest_ObjectCommands.cxx @@ -5598,6 +5598,18 @@ static int VFont (Draw_Interpretor& theDI, } 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"; @@ -6541,7 +6553,8 @@ void ViewerTest::ObjectCommands(Draw_Interpretor& theCommands) __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 ("vvertexmode", diff --git a/tests/3rdparty/fonts/C2 b/tests/3rdparty/fonts/C2 new file mode 100644 index 0000000000..0ff3c7fb3f --- /dev/null +++ b/tests/3rdparty/fonts/C2 @@ -0,0 +1,37 @@ +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