vcl: Drop direct FreeType use

Nothing really uses FreeType internally anymore (externals like Cairo or
Skia create their FT_Face’s themselves and don’t use ours).

We now load the hb_blob_t from file directly as HarfBuzz already uses
mmap, so FreetypeFontFile is gone now as well.

Change-Id: I90c532e47b658cdb55d7b13d7d788ecef8680942
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208542
Tested-by: Jenkins
Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
This commit is contained in:
Khaled Hosny
2026-07-24 17:08:55 +02:00
committed by Khaled Hosny
parent 199adf1343
commit f9fac37352
7 changed files with 30 additions and 354 deletions
+1 -10
View File
@@ -37,15 +37,12 @@ class VCL_DLLPUBLIC FreetypeFont final : public LogicalFontInstance
FreetypeFontFace::CreateFontInstance(const vcl::font::FontSelectPattern&) const;
public:
virtual ~FreetypeFont() override;
const FreetypeFontFace* GetFontFace() const
{
return static_cast<const FreetypeFontFace*>(LogicalFontInstance::GetFontFace());
}
bool TestFont() const { return mbFaceOk; }
FT_Face GetFtFace() const;
bool TestFont() const;
const FontConfigFontOptions* GetFontOptions() const;
bool GetAntialiasAdvice() const;
@@ -53,15 +50,9 @@ public:
private:
explicit FreetypeFont(const FreetypeFontFace&, const vcl::font::FontSelectPattern&);
int mnWidth;
int mnPrioAntiAlias;
double mfStretch;
FT_FaceRec_* maFaceFT;
FT_SizeRec_* maSizeFT;
mutable std::unique_ptr<FontConfigFontOptions> mxFontOptions;
bool mbFaceOk;
};
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
+3 -41
View File
@@ -21,54 +21,18 @@
#include <sal/config.h>
#include <freetype/config/ftheader.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
#include <font/PhysicalFontFace.hxx>
#include <fontattributes.hxx>
#include <glyphid.hxx>
class FreetypeFontList;
// FreetypeFontFile has the responsibility that a font file is only mapped once.
// (#86621#) the old directly ft-managed solution caused it to be mapped
// in up to nTTC*nSizes*nOrientation*nSynthetic times
class FreetypeFontFile final
{
public:
bool Map();
void Unmap();
const unsigned char* GetBuffer() const { return mpFileMap; }
int GetFileSize() const { return mnFileSize; }
const OString& GetFileName() const { return maNativeFileName; }
int GetLangBoost() const { return mnLangBoost; }
private:
friend class FreetypeFontList;
explicit FreetypeFontFile( OString aNativeFileName );
const OString maNativeFileName;
unsigned char* mpFileMap;
int mnFileSize;
int mnRefCount;
int mnLangBoost;
intptr_t mnHandle;
};
// FreetypeFontFace corresponds to an unscaled font face
class FreetypeFontFace final : public vcl::font::PhysicalFontFace
{
public:
FreetypeFontFace( const FontAttributes&, FreetypeFontFile* const pFontFile,
FreetypeFontFace( const FontAttributes&, OString aNativeFileName,
int nFaceNum, int nFaceVariation, sal_IntPtr nFontId );
FT_FaceRec_* GetFaceFT() const;
void ReleaseFaceFT() const;
FreetypeFontFile* GetFontFile() const { return mpFontFile; }
const OString& GetFontFileName() const { return mpFontFile->GetFileName(); }
const OString& GetFontFileName() const { return maNativeFileName; }
int GetFontFaceIndex() const { return mnFaceNum; }
int GetFontFaceVariation() const { return mnFaceVariation; }
@@ -81,11 +45,9 @@ public:
const std::vector<vcl::font::Variation>& GetVariations(const LogicalFontInstance&) const override;
private:
mutable FT_FaceRec_* maFaceFT;
FreetypeFontFile* const mpFontFile;
const OString maNativeFileName;
const int mnFaceNum;
const int mnFaceVariation;
mutable int mnRefCount;
const sal_IntPtr mnFontId;
};
+3 -15
View File
@@ -33,27 +33,19 @@
#include <string_view>
#include <unordered_map>
class FreetypeFontFile;
namespace vcl::font
{
class PhysicalFontCollection;
}
FT_Library GetFreetypeLibrary();
int GetDefaultAntiAliasPrio();
/**
* The FreetypeFontList is the list of the fonts we know about.
*
* It enumerates them from fontconfig, maps a font id to the FreetypeFontFace
* for it, and keeps the mmapped font files those faces read from. The faces it
* hands to a PhysicalFontCollection are the very same objects, shared by every
* collection.
*
* The resources are:
* FreetypeFontFile = holds the mmapped font file, as long as it's used by any face.
* FreetypeFontFace = holds the FT_FaceRec_ object, as long as it's used by any FreetypeFont.
* FreetypeFont = holds the FT_SizeRec_; it is the Freetype LogicalFontInstance.
* It enumerates them from fontconfig and maps a font id to the FreetypeFontFace
* for it. The faces it hands to a PhysicalFontCollection are the very same
* objects, shared by every collection.
**/
class VCL_DLLPUBLIC FreetypeFontList final
{
@@ -80,17 +72,13 @@ private:
SAL_DLLPRIVATE void Init();
SAL_DLLPRIVATE static void InitFreetype();
SAL_DLLPRIVATE FreetypeFontFile* FindFontFile(const OString& rNativeFileName);
SAL_DLLPRIVATE void AddFontFace(const FontAttributes& rDFA, const OString& rFileName,
int nFaceNum, int nVariationNum);
typedef std::unordered_map<sal_IntPtr, rtl::Reference<FreetypeFontFace>> FontFaceList;
typedef std::unordered_map<const char*, std::unique_ptr<FreetypeFontFile>, rtl::CStringHash, rtl::CStringEqual> FontFileList;
sal_IntPtr m_nNextFontId = 1;
FontFaceList m_aFontFaceList;
FontFileList m_aFontFileList;
std::unordered_map<OString, o3tl::sorted_vector<sal_IntPtr>> m_aFontFileToFontId;
};
+7 -56
View File
@@ -27,60 +27,19 @@
#include <font/LogicalFontInstance.hxx>
#include <fontattributes.hxx>
#include <sal/log.hxx>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_SIZES_H
FT_Face FreetypeFont::GetFtFace() const
{
FT_Activate_Size( maSizeFT );
return maFaceFT;
}
#include <hb.h>
// FreetypeFont
FreetypeFont::FreetypeFont(const FreetypeFontFace& rFontFace, const vcl::font::FontSelectPattern& rFSD)
: LogicalFontInstance(rFontFace, rFSD),
mnPrioAntiAlias(GetDefaultAntiAliasPrio()),
maFaceFT( nullptr ),
maSizeFT( nullptr ),
mbFaceOk( false )
: LogicalFontInstance(rFontFace, rFSD)
, mnPrioAntiAlias(GetDefaultAntiAliasPrio())
{
maFaceFT = rFontFace.GetFaceFT();
}
// set the pixel size of the font instance
mnWidth = rFSD.mnWidth;
if( !mnWidth )
mnWidth = rFSD.mnHeight;
if (rFSD.mnHeight == 0)
{
SAL_WARN("vcl", "FreetypeFont divide by zero");
mfStretch = 1.0;
return;
}
mfStretch = static_cast<double>(mnWidth) / rFSD.mnHeight;
// sanity checks (e.g. #i66394#, #i66244#, #i66537#)
if (mnWidth < 0)
{
SAL_WARN("vcl", "FreetypeFont negative font width of: " << mnWidth);
return;
}
SAL_WARN_IF(mfStretch > +64.0 || mfStretch < -64.0, "vcl", "FreetypeFont excessive stretch of: " << mfStretch);
if( !maFaceFT )
return;
FT_New_Size( maFaceFT, &maSizeFT );
FT_Activate_Size( maSizeFT );
/* This might fail for color bitmap fonts, but that is fine since we will
* not need any glyph data from FreeType in this case */
/*FT_Error rc = */ FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
mbFaceOk = true;
bool FreetypeFont::TestFont() const
{
return hb_face_get_glyph_count(GetFontFace()->GetHbFace()) > 0;
}
namespace
@@ -104,14 +63,6 @@ const FontConfigFontOptions* FreetypeFont::GetFontOptions() const
return mxFontOptions.get();
}
FreetypeFont::~FreetypeFont()
{
if( maSizeFT )
FT_Done_Size( maSizeFT );
GetFontFace()->ReleaseFaceFT();
}
bool FreetypeFont::GetAntialiasAdvice() const
{
// TODO: also use GASP info
+6 -183
View File
@@ -23,177 +23,23 @@
#include <unx/font/FreetypeFontFace.hxx>
#include <unx/font/FreetypeFont.hxx>
#include <unx/font/FreetypeFontList.hxx>
#include <font/LogicalFontInstance.hxx>
#include <fontattributes.hxx>
#include <sal/log.hxx>
#include <osl/module.h>
#include <i18nlangtag/mslangid.hxx>
#include <vcl/settings.hxx>
#include <vcl/svapp.hxx>
#include <ft2build.h>
#include FT_FREETYPE_H
#include <hb-ot.h>
#include <tools/UnixWrappers.h>
#include <fcntl.h>
#include <sys/stat.h>
#ifdef _WIN32
#define strncasecmp(x, y, z) _strnicmp(x, y, z)
#endif
namespace
{
const char* getLangBoost()
{
const LanguageType eLang = Application::GetSettings().GetUILanguageTag().getLanguageType();
if (eLang == LANGUAGE_JAPANESE)
return "jan";
if (MsLangId::isKorean(eLang))
return "kor";
if (MsLangId::isSimplifiedChinese(eLang))
return "zhs";
if (MsLangId::isTraditionalChinese(eLang))
return "zht";
return nullptr;
}
}
FreetypeFontFile::FreetypeFontFile(OString aNativeFileName)
: maNativeFileName(std::move(aNativeFileName))
, mpFileMap(nullptr)
, mnFileSize(0)
, mnRefCount(0)
, mnLangBoost(0)
, mnHandle(0)
{
// boost font preference if UI language is mentioned in filename
int nPos = maNativeFileName.lastIndexOf('_');
if (nPos == -1 || maNativeFileName[nPos + 1] == '.')
mnLangBoost += 0x1000; // no langinfo => good
else
{
static const char* pLangBoost = nullptr;
static bool bOnce = true;
if (bOnce)
{
bOnce = false;
pLangBoost = getLangBoost();
}
if (pLangBoost && !strncasecmp(pLangBoost, &maNativeFileName.getStr()[nPos + 1], 3))
mnLangBoost += 0x2000; // matching langinfo => better
}
}
bool FreetypeFontFile::Map()
{
if (mnRefCount++ == 0)
{
const char* pFileName = maNativeFileName.getStr();
int nFile;
int nFD;
int n;
if (sscanf(pFileName, "/:FD:/%d%n", &nFD, &n) == 1 && pFileName[n] == '\0')
{
lseek(nFD, 0, SEEK_SET);
nFile = wrap_dup(nFD);
}
else
nFile = wrap_open(pFileName, O_RDONLY, 0);
if (nFile < 0)
{
SAL_WARN("vcl.unx.freetype",
"open('" << maNativeFileName << "') failed: " << strerror(errno));
return false;
}
struct stat aStat;
int nRet = wrap_fstat(nFile, &aStat);
if (nRet < 0)
{
SAL_WARN("vcl.unx.freetype",
"fstat on '" << maNativeFileName << "' failed: " << strerror(errno));
wrap_close(nFile);
return false;
}
mnFileSize = aStat.st_size;
mpFileMap = static_cast<unsigned char*>(wrap_mmap(mnFileSize, nFile, &mnHandle));
if (mpFileMap == MAP_FAILED)
{
SAL_WARN("vcl.unx.freetype",
"mmap of '" << maNativeFileName << "' failed: " << strerror(errno));
mpFileMap = nullptr;
}
else
SAL_INFO("vcl.unx.freetype", "mmap'ed '" << maNativeFileName << "' successfully");
wrap_close(nFile);
}
return (mpFileMap != nullptr);
}
void FreetypeFontFile::Unmap()
{
if (--mnRefCount != 0)
return;
assert(mnRefCount >= 0 && "how did this go negative\n");
if (mpFileMap)
{
wrap_munmap(mpFileMap, mnFileSize, mnHandle);
mpFileMap = nullptr;
}
}
FreetypeFontFace::FreetypeFontFace(const FontAttributes& rDevFontAttributes,
FreetypeFontFile* const pFontFile, int nFaceNum,
int nFaceVariation, sal_IntPtr nFontId)
OString aNativeFileName, int nFaceNum, int nFaceVariation,
sal_IntPtr nFontId)
: vcl::font::PhysicalFontFace(rDevFontAttributes)
, maFaceFT(nullptr)
, mpFontFile(pFontFile)
, maNativeFileName(std::move(aNativeFileName))
, mnFaceNum(nFaceNum)
, mnFaceVariation(nFaceVariation)
, mnRefCount(0)
, mnFontId(nFontId)
{
// prefer font with low ID
IncreaseQualityBy(10000 - nFontId);
// prefer font with matching file names
IncreaseQualityBy(mpFontFile->GetLangBoost());
}
FT_FaceRec_* FreetypeFontFace::GetFaceFT() const
{
if (!maFaceFT && mpFontFile->Map())
{
FT_Error rc = FT_New_Memory_Face(GetFreetypeLibrary(), mpFontFile->GetBuffer(),
mpFontFile->GetFileSize(), mnFaceNum, &maFaceFT);
if ((rc != FT_Err_Ok) || (maFaceFT->num_glyphs <= 0))
maFaceFT = nullptr;
}
++mnRefCount;
return maFaceFT;
}
void FreetypeFontFace::ReleaseFaceFT() const
{
if (--mnRefCount == 0)
{
if (maFaceFT)
{
FT_Done_Face(maFaceFT);
maFaceFT = nullptr;
}
mpFontFile->Unmap();
}
assert(mnRefCount >= 0 && "how did this go negative\n");
}
rtl::Reference<LogicalFontInstance>
@@ -202,36 +48,13 @@ FreetypeFontFace::CreateFontInstance(const vcl::font::FontSelectPattern& rFSD) c
return new FreetypeFont(*this, rFSD);
}
namespace
{
hb_blob_t* CreateHbBlob(FreetypeFontFile* pFontFile)
{
auto pFileName = pFontFile->GetFileName().getStr();
int nFD;
int n;
if (sscanf(pFileName, "/:FD:/%d%n", &nFD, &n) == 1 && pFileName[n] == '\0')
{
if (pFontFile->Map())
return hb_blob_create(
reinterpret_cast<const char*>(pFontFile->GetBuffer()), pFontFile->GetFileSize(),
HB_MEMORY_MODE_READONLY, pFontFile,
[](void* data) { static_cast<FreetypeFontFile*>(data)->Unmap(); });
pFontFile->Unmap();
return hb_blob_get_empty();
}
return hb_blob_create_from_file(pFileName);
}
}
hb_face_t* FreetypeFontFace::GetHbFace() const
{
if (!mpHbFace)
{
auto pFontFile = GetFontFile();
auto nIndex = GetFontFaceIndex();
auto pHbBlob = CreateHbBlob(pFontFile);
mpHbFace = hb_face_create(pHbBlob, nIndex);
hb_blob_destroy(pHbBlob);
hb_blob_t* pBlob = hb_blob_create_from_file(maNativeFileName.getStr());
mpHbFace = hb_face_create(pBlob, GetFontFaceIndex());
hb_blob_destroy(pBlob);
}
return mpHbFace;
}
+7 -43
View File
@@ -32,11 +32,6 @@
#include <sal/log.hxx>
#include <tools/urlobj.hxx>
#include <ft2build.h>
#include FT_FREETYPE_H
static FT_Library aLibFT = nullptr;
// TODO: remove when the priorities are selected by UI
// if (AH==0) => disable autohinting
// if (AA==0) => disable antialiasing
@@ -44,27 +39,15 @@ static FT_Library aLibFT = nullptr;
// if (AA prio <= AH prio) => antialias + autohint
// if (AH<AA) => do not autohint when antialiasing
// if (EB<AH) => do not autohint for monochrome
static int nDefaultPrioAntiAlias = 1;
FT_Library GetFreetypeLibrary() { return aLibFT; }
static int nDefaultPrioAntiAlias = 1;
int GetDefaultAntiAliasPrio() { return nDefaultPrioAntiAlias; }
void FreetypeFontList::InitFreetype()
{
/*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
// TODO: remove when the priorities are selected by UI
char* pEnv;
pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
if( pEnv )
nDefaultPrioAntiAlias = pEnv[0] - '0';
}
FreetypeFontList::FreetypeFontList()
{
InitFreetype();
// TODO: remove when the priorities are selected by UI
if (const char* pEnv = ::getenv("SAL_ANTIALIASED_TEXT_PRIORITY"))
nDefaultPrioAntiAlias = pEnv[0] - '0';
}
void FreetypeFontList::Init()
@@ -82,11 +65,7 @@ void FreetypeFontList::Init()
SAL_INFO("vcl.fonts", "have " << m_aFontFaceList.size() << " fonts");
}
FreetypeFontList::~FreetypeFontList()
{
// the faces point at the font files, so they have to go first
m_aFontFaceList.clear();
}
FreetypeFontList::~FreetypeFontList() {}
FreetypeFontList& FreetypeFontList::get()
{
@@ -95,21 +74,6 @@ FreetypeFontList& FreetypeFontList::get()
return *pSalData->GetFreetypeFontList();
}
FreetypeFontFile* FreetypeFontList::FindFontFile(const OString& rNativeFileName)
{
// font file already known? (e.g. for ttc, synthetic, aliased fonts)
const char* pFileName = rNativeFileName.getStr();
FontFileList::const_iterator it = m_aFontFileList.find(pFileName);
if (it != m_aFontFileList.end())
return it->second.get();
// no => create new one
FreetypeFontFile* pFontFile = new FreetypeFontFile(rNativeFileName);
pFileName = pFontFile->maNativeFileName.getStr();
m_aFontFileList[pFileName].reset(pFontFile);
return pFontFile;
}
void FreetypeFontList::AddFontFace(const FontAttributes& rDevFontAttr, const OString& rFileName,
int nFaceNum, int nVariationNum)
{
@@ -117,8 +81,8 @@ void FreetypeFontList::AddFontFace(const FontAttributes& rDevFontAttr, const OSt
return;
const sal_IntPtr nFontId = m_nNextFontId++;
m_aFontFaceList[ nFontId ] = new FreetypeFontFace( rDevFontAttr,
FindFontFile(rFileName), nFaceNum, nVariationNum, nFontId);
m_aFontFaceList[nFontId]
= new FreetypeFontFace(rDevFontAttr, rFileName, nFaceNum, nVariationNum, nFontId);
m_aFontFileToFontId[ rFileName ].insert( nFontId );
}
+3 -6
View File
@@ -41,20 +41,18 @@
namespace {
typedef struct FT_FaceRec_* FT_Face;
class CairoFontsCache : public CacheOwner
{
public:
struct CacheId
{
FT_Face maFace;
const void* mpFace;
const FontConfigFontOptions *mpOptions;
bool mbEmbolden;
bool mbVerticalMetrics;
bool operator ==(const CacheId& rOther) const
{
return maFace == rOther.maFace &&
return mpFace == rOther.mpFace &&
mpOptions == rOther.mpOptions &&
mbEmbolden == rOther.mbEmbolden &&
mbVerticalMetrics == rOther.mbVerticalMetrics;
@@ -261,9 +259,8 @@ static CairoFontsCache::CacheId makeCacheId(const GenericSalLayout& rLayout)
{
const FreetypeFont& rFont = static_cast<const FreetypeFont&>(rLayout.GetFont());
FT_Face aFace = rFont.GetFtFace();
CairoFontsCache::CacheId aId;
aId.maFace = aFace;
aId.mpFace = rFont.GetFontFace();
aId.mpOptions = rFont.GetFontOptions();
aId.mbEmbolden = rFont.NeedsArtificialBold();
aId.mbVerticalMetrics = false;