1
0
mirror of https://git.dev.opencascade.org/repos/occt.git synced 2025-04-04 18:06:22 +03:00

0030782: Visualization, Font_FTFont - use predefined fallback fonts for extended Unicode subsets [backported to occt720]

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
This commit is contained in:
kgv 2019-06-16 13:09:49 +03:00
parent 51204152b9
commit 0842e31d95
14 changed files with 459 additions and 101 deletions

View File

@ -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;
}
}
//=======================================================================

View File

@ -1146,23 +1146,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"

View File

@ -18,3 +18,4 @@ Font_SystemFont.hxx
Font_NameOfFont.hxx
Font_TextFormatter.hxx
Font_TextFormatter.cxx
Font_UnicodeSubset.hxx

View File

@ -411,7 +411,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;
}
@ -420,8 +420,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;

View File

@ -21,6 +21,8 @@
#include <Message.hxx>
#include <Message_Messenger.hxx>
#include <algorithm>
#include <ft2build.h>
#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,68 @@ 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;
myGlyphImg.ChangeRow (aRow)[aCol] = aBitOn ? 255 : 0;
}
}
}
else
{
return false;
}
myGlyphImg.SetTopDown (aBitmap.pitch > 0);
myUChar = theUChar;
return true;
}
@ -253,24 +359,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<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;
}
// =======================================================================
@ -322,18 +462,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 +501,7 @@ float Font_FTFont::AdvanceX (Standard_Utf32Char theUCharNext) const
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);
}
// =======================================================================
@ -373,29 +517,41 @@ float Font_FTFont::AdvanceY (Standard_Utf32Char theUCharNext) const
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);
}
// =======================================================================

View File

@ -19,6 +19,7 @@
#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>
@ -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.)
};

View File

@ -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());

View File

@ -22,6 +22,7 @@
#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>
@ -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;

View File

@ -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"

View File

@ -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

View File

@ -21,7 +21,6 @@
#include <Standard_Assert.hxx>
#include <TCollection_ExtendedString.hxx>
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)
@ -91,11 +88,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))
@ -112,12 +107,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));
@ -172,14 +171,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))
{

View File

@ -103,12 +103,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
@ -132,8 +126,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;

View File

@ -5673,6 +5673,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";
@ -6751,7 +6763,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 ("vsetedgetype",

37
tests/3rdparty/fonts/C2 vendored Normal file
View File

@ -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