mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-11 23:54:18 +00:00
Refactored some linux freetype font classes.
This commit is contained in:
parent
82976c2ac7
commit
c8b3d28acc
4 changed files with 573 additions and 479 deletions
|
|
@ -71,11 +71,22 @@
|
|||
#endif
|
||||
|
||||
#elif JUCE_LINUX
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#undef SIZEOF
|
||||
#ifndef JUCE_USE_FREETYPE
|
||||
#define JUCE_USE_FREETYPE 1
|
||||
#endif
|
||||
|
||||
#if ! JUCE_USE_FREETYPE_AMALGAMATED
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED
|
||||
#include "native/freetype/FreeTypeAmalgam.h"
|
||||
#endif
|
||||
|
||||
#undef SIZEOF
|
||||
|
||||
#if (JUCE_MAC || JUCE_IOS) && USE_COREGRAPHICS_RENDERING && JUCE_USE_COREIMAGE_LOADER
|
||||
#define JUCE_USING_COREIMAGE_LOADER 1
|
||||
#else
|
||||
|
|
@ -117,6 +128,10 @@ namespace juce
|
|||
#include "effects/juce_DropShadowEffect.cpp"
|
||||
#include "effects/juce_GlowEffect.cpp"
|
||||
|
||||
#if JUCE_USE_FREETYPE
|
||||
#include "native/juce_freetype_Fonts.cpp"
|
||||
#endif
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_MAC || JUCE_IOS
|
||||
#include "../juce_core/native/juce_osx_ObjCHelpers.h"
|
||||
|
|
@ -142,5 +157,18 @@ namespace juce
|
|||
#include "native/juce_android_Fonts.cpp"
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_USE_FREETYPE && JUCE_USE_FREETYPE_AMALGAMATED
|
||||
#undef PIXEL_MASK
|
||||
#undef ZLIB_VERSION
|
||||
#undef Z_ASCII
|
||||
#undef ZEXTERN
|
||||
#undef ZEXPORT
|
||||
|
||||
extern "C"
|
||||
{
|
||||
#include "native/freetype/FreeTypeAmalgam.c"
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,6 +23,67 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
struct DefaultFontNames
|
||||
{
|
||||
DefaultFontNames()
|
||||
: defaultSans ("sans"),
|
||||
defaultSerif ("serif"),
|
||||
defaultFixed ("monospace"),
|
||||
defaultFallback ("sans")
|
||||
{
|
||||
}
|
||||
|
||||
String getRealFontName (const String& faceName) const
|
||||
{
|
||||
if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans;
|
||||
if (faceName == Font::getDefaultSerifFontName()) return defaultSerif;
|
||||
if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed;
|
||||
|
||||
return faceName;
|
||||
}
|
||||
|
||||
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
|
||||
};
|
||||
|
||||
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
|
||||
{
|
||||
static DefaultFontNames defaultNames;
|
||||
|
||||
Font f (font);
|
||||
f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName()));
|
||||
return Typeface::createSystemTypefaceFor (f);
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
#if JUCE_USE_FREETYPE
|
||||
|
||||
void FontFileIterator::findFontDirectories()
|
||||
{
|
||||
fontDirs.add ("/system/fonts");
|
||||
}
|
||||
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
|
||||
{
|
||||
return new FreeTypeTypeface (font);
|
||||
}
|
||||
|
||||
StringArray Font::findAllTypefaceNames()
|
||||
{
|
||||
return FTTypefaceList::getInstance()->findAllFamilyNames();
|
||||
}
|
||||
|
||||
StringArray Font::findAllTypefaceStyles (const String& family)
|
||||
{
|
||||
return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
//==============================================================================
|
||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||
STATICMETHOD (create, "create", "(Ljava/lang/String;I)Landroid/graphics/Typeface;") \
|
||||
|
|
@ -31,7 +92,6 @@
|
|||
DECLARE_JNI_CLASS (TypefaceClass, "android/graphics/Typeface");
|
||||
#undef JNI_CLASS_MEMBERS
|
||||
|
||||
|
||||
//==============================================================================
|
||||
StringArray Font::findAllTypefaceNames()
|
||||
{
|
||||
|
|
@ -61,34 +121,6 @@ StringArray Font::findAllTypefaceStyles (const String& family)
|
|||
return results;
|
||||
}
|
||||
|
||||
struct DefaultFontNames
|
||||
{
|
||||
DefaultFontNames()
|
||||
: defaultSans ("sans"),
|
||||
defaultSerif ("serif"),
|
||||
defaultFixed ("monospace"),
|
||||
defaultFallback ("sans")
|
||||
{
|
||||
}
|
||||
|
||||
String defaultSans, defaultSerif, defaultFixed, defaultFallback;
|
||||
};
|
||||
|
||||
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
|
||||
{
|
||||
static DefaultFontNames defaultNames;
|
||||
|
||||
String faceName (font.getTypefaceName());
|
||||
|
||||
if (faceName == Font::getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans;
|
||||
else if (faceName == Font::getDefaultSerifFontName()) faceName = defaultNames.defaultSerif;
|
||||
else if (faceName == Font::getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed;
|
||||
|
||||
Font f (font);
|
||||
f.setTypefaceName (faceName);
|
||||
return Typeface::createSystemTypefaceFor (f);
|
||||
}
|
||||
|
||||
const float referenceFontSize = 256.0f;
|
||||
const float referenceFontToUnits = 1.0f / referenceFontSize;
|
||||
|
||||
|
|
@ -283,3 +315,5 @@ bool TextLayout::createNativeLayout (const AttributedString&)
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
437
modules/juce_graphics/native/juce_freetype_Fonts.cpp
Normal file
437
modules/juce_graphics/native/juce_freetype_Fonts.cpp
Normal file
|
|
@ -0,0 +1,437 @@
|
|||
/*
|
||||
==============================================================================
|
||||
|
||||
This file is part of the JUCE library - "Jules' Utility Class Extensions"
|
||||
Copyright 2004-11 by Raw Material Software Ltd.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
JUCE can be redistributed and/or modified under the terms of the GNU General
|
||||
Public License (Version 2), as published by the Free Software Foundation.
|
||||
A copy of the license is included in the JUCE distribution, or can be found
|
||||
online at www.gnu.org/licenses.
|
||||
|
||||
JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
------------------------------------------------------------------------------
|
||||
|
||||
To release a closed-source product which uses JUCE, commercial licenses are
|
||||
available: visit www.rawmaterialsoftware.com/juce for more information.
|
||||
|
||||
==============================================================================
|
||||
*/
|
||||
|
||||
struct FTLibWrapper : public ReferenceCountedObject
|
||||
{
|
||||
FTLibWrapper() : library (0)
|
||||
{
|
||||
if (FT_Init_FreeType (&library) != 0)
|
||||
{
|
||||
library = 0;
|
||||
DBG ("Failed to initialize FreeType");
|
||||
}
|
||||
}
|
||||
|
||||
~FTLibWrapper()
|
||||
{
|
||||
if (library != 0)
|
||||
FT_Done_FreeType (library);
|
||||
}
|
||||
|
||||
FT_Library library;
|
||||
|
||||
typedef ReferenceCountedObjectPtr <FTLibWrapper> Ptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FTFaceWrapper : public ReferenceCountedObject
|
||||
{
|
||||
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex)
|
||||
: face (0), library (ftLib)
|
||||
{
|
||||
if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0)
|
||||
face = 0;
|
||||
}
|
||||
|
||||
~FTFaceWrapper()
|
||||
{
|
||||
if (face != 0)
|
||||
FT_Done_Face (face);
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
FTLibWrapper::Ptr library;
|
||||
|
||||
typedef ReferenceCountedObjectPtr <FTFaceWrapper> Ptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FontFileIterator
|
||||
{
|
||||
public:
|
||||
FontFileIterator() : index (0)
|
||||
{
|
||||
findFontDirectories();
|
||||
}
|
||||
|
||||
bool next()
|
||||
{
|
||||
if (iter != nullptr)
|
||||
{
|
||||
while (iter->next())
|
||||
if (getFile().hasFileExtension ("ttf;pfb;pcf;otf"))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (index >= fontDirs.size())
|
||||
return false;
|
||||
|
||||
iter = new DirectoryIterator (File::getCurrentWorkingDirectory()
|
||||
.getChildFile (fontDirs [index++]), true);
|
||||
return next();
|
||||
}
|
||||
|
||||
File getFile() const { jassert (iter != nullptr); return iter->getFile(); }
|
||||
|
||||
private:
|
||||
StringArray fontDirs;
|
||||
int index;
|
||||
ScopedPointer<DirectoryIterator> iter;
|
||||
|
||||
void findFontDirectories();
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FontFileIterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FTTypefaceList : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
FTTypefaceList()
|
||||
: library (new FTLibWrapper())
|
||||
{
|
||||
FontFileIterator fontFileIterator;
|
||||
|
||||
while (fontFileIterator.next())
|
||||
{
|
||||
int faceIndex = 0;
|
||||
int numFaces = 0;
|
||||
|
||||
do
|
||||
{
|
||||
FTFaceWrapper face (library, fontFileIterator.getFile(), faceIndex);
|
||||
|
||||
if (face.face != 0)
|
||||
{
|
||||
if (faceIndex == 0)
|
||||
numFaces = face.face->num_faces;
|
||||
|
||||
if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
|
||||
faces.add (new KnownTypeface (fontFileIterator.getFile(), faceIndex, face));
|
||||
}
|
||||
|
||||
++faceIndex;
|
||||
}
|
||||
while (faceIndex < numFaces);
|
||||
}
|
||||
}
|
||||
|
||||
~FTTypefaceList()
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct KnownTypeface
|
||||
{
|
||||
KnownTypeface (const File& f, const int index, const FTFaceWrapper& face)
|
||||
: file (f),
|
||||
family (face.face->family_name),
|
||||
style (face.face->style_name),
|
||||
faceIndex (index),
|
||||
isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0),
|
||||
isSansSerif (isFaceSansSerif (family))
|
||||
{
|
||||
}
|
||||
|
||||
const File file;
|
||||
const String family, style;
|
||||
const int faceIndex;
|
||||
const bool isMonospaced, isSansSerif;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle)
|
||||
{
|
||||
const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle);
|
||||
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular");
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, String::empty);
|
||||
|
||||
if (ftFace != nullptr)
|
||||
{
|
||||
if (FTFaceWrapper::Ptr face = new FTFaceWrapper (library, ftFace->file, ftFace->faceIndex))
|
||||
{
|
||||
// If there isn't a unicode charmap then select the first one.
|
||||
if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0)
|
||||
FT_Set_Charmap (face->face, face->face->charmaps[0]);
|
||||
|
||||
return face;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray findAllFamilyNames() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
s.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray findAllTypefaceStyles (const String& family) const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
if (face->family == family)
|
||||
s.addIfNotAlreadyThere (face->style);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void getMonospacedNames (StringArray& monoSpaced) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (faces.getUnchecked(i)->isMonospaced)
|
||||
monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
void getSerifNames (StringArray& serif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (! faces.getUnchecked(i)->isSansSerif)
|
||||
serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
void getSansSerifNames (StringArray& sansSerif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (faces.getUnchecked(i)->isSansSerif)
|
||||
sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList);
|
||||
|
||||
private:
|
||||
FTLibWrapper::Ptr library;
|
||||
OwnedArray<KnownTypeface> faces;
|
||||
|
||||
const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
if (face->family == familyName
|
||||
&& (face->style.equalsIgnoreCase (style) || style.isEmpty()))
|
||||
return face;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool isFaceSansSerif (const String& family)
|
||||
{
|
||||
const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (sansNames); ++i)
|
||||
if (family.containsIgnoreCase (sansNames[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTTypefaceList)
|
||||
};
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (FTTypefaceList)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FreeTypeTypeface : public CustomTypeface
|
||||
{
|
||||
public:
|
||||
FreeTypeTypeface (const Font& font)
|
||||
: faceWrapper (FTTypefaceList::getInstance()
|
||||
->createFace (font.getTypefaceName(), font.getTypefaceStyle()))
|
||||
{
|
||||
if (faceWrapper != nullptr)
|
||||
{
|
||||
setCharacteristics (font.getTypefaceName(),
|
||||
font.getTypefaceStyle(),
|
||||
faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender),
|
||||
L' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG ("Failed to create typeface: " << font.toString());
|
||||
}
|
||||
}
|
||||
|
||||
bool loadGlyphIfPossible (const juce_wchar character)
|
||||
{
|
||||
if (faceWrapper != nullptr)
|
||||
{
|
||||
FT_Face face = faceWrapper->face;
|
||||
const unsigned int glyphIndex = FT_Get_Char_Index (face, character);
|
||||
|
||||
if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) == 0
|
||||
&& face->glyph->format == ft_glyph_format_outline)
|
||||
{
|
||||
const float scale = 1.0f / (float) (face->ascender - face->descender);
|
||||
Path destShape;
|
||||
|
||||
if (getGlyphShape (destShape, face->glyph->outline, scale))
|
||||
{
|
||||
addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale);
|
||||
|
||||
if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0)
|
||||
addKerning (face, character, glyphIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
FTFaceWrapper::Ptr faceWrapper;
|
||||
|
||||
bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX)
|
||||
{
|
||||
const float scaleY = -scaleX;
|
||||
const short* const contours = outline.contours;
|
||||
const char* const tags = outline.tags;
|
||||
const FT_Vector* const points = outline.points;
|
||||
|
||||
for (int c = 0; c < outline.n_contours; ++c)
|
||||
{
|
||||
const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1;
|
||||
const int endPoint = contours[c];
|
||||
|
||||
for (int p = startPoint; p <= endPoint; ++p)
|
||||
{
|
||||
const float x = scaleX * points[p].x;
|
||||
const float y = scaleY * points[p].y;
|
||||
|
||||
if (p == startPoint)
|
||||
{
|
||||
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
float x2 = scaleX * points [endPoint].x;
|
||||
float y2 = scaleY * points [endPoint].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On)
|
||||
{
|
||||
x2 = (x + x2) * 0.5f;
|
||||
y2 = (y + y2) * 0.5f;
|
||||
}
|
||||
|
||||
destShape.startNewSubPath (x2, y2);
|
||||
}
|
||||
else
|
||||
{
|
||||
destShape.startNewSubPath (x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On)
|
||||
{
|
||||
if (p != startPoint)
|
||||
destShape.lineTo (x, y);
|
||||
}
|
||||
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
const int nextIndex = (p == endPoint) ? startPoint : p + 1;
|
||||
float x2 = scaleX * points [nextIndex].x;
|
||||
float y2 = scaleY * points [nextIndex].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
x2 = (x + x2) * 0.5f;
|
||||
y2 = (y + y2) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
++p;
|
||||
}
|
||||
|
||||
destShape.quadraticTo (x, y, x2, y2);
|
||||
}
|
||||
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic)
|
||||
{
|
||||
const int next1 = p + 1;
|
||||
const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2);
|
||||
|
||||
if (p >= endPoint
|
||||
|| FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic
|
||||
|| FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On)
|
||||
return false;
|
||||
|
||||
const float x2 = scaleX * points [next1].x;
|
||||
const float y2 = scaleY * points [next1].y;
|
||||
const float x3 = scaleX * points [next2].x;
|
||||
const float y3 = scaleY * points [next2].y;
|
||||
|
||||
destShape.cubicTo (x, y, x2, y2, x3, y3);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
destShape.closeSubPath();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex)
|
||||
{
|
||||
const float height = (float) (face->ascender - face->descender);
|
||||
|
||||
uint32 rightGlyphIndex;
|
||||
uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
|
||||
|
||||
while (rightGlyphIndex != 0)
|
||||
{
|
||||
FT_Vector kerning;
|
||||
|
||||
if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0
|
||||
&& kerning.x != 0)
|
||||
addKerningPair (character, rightCharCode, kerning.x / height);
|
||||
|
||||
rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface)
|
||||
};
|
||||
|
|
@ -23,453 +23,45 @@
|
|||
==============================================================================
|
||||
*/
|
||||
|
||||
struct FTLibWrapper : public ReferenceCountedObject
|
||||
void FontFileIterator::findFontDirectories()
|
||||
{
|
||||
FTLibWrapper() : library (0)
|
||||
fontDirs.addTokens (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty);
|
||||
fontDirs.removeEmptyStrings (true);
|
||||
|
||||
if (fontDirs.size() == 0)
|
||||
{
|
||||
if (FT_Init_FreeType (&library) != 0)
|
||||
const ScopedPointer<XmlElement> fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf")));
|
||||
|
||||
if (fontsInfo != nullptr)
|
||||
{
|
||||
library = 0;
|
||||
DBG ("Failed to initialize FreeType");
|
||||
}
|
||||
}
|
||||
|
||||
~FTLibWrapper()
|
||||
{
|
||||
if (library != 0)
|
||||
FT_Done_FreeType (library);
|
||||
}
|
||||
|
||||
FT_Library library;
|
||||
|
||||
typedef ReferenceCountedObjectPtr <FTLibWrapper> Ptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTLibWrapper)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
struct FTFaceWrapper : public ReferenceCountedObject
|
||||
{
|
||||
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const File& file, int faceIndex)
|
||||
: face (0), library (ftLib)
|
||||
{
|
||||
if (FT_New_Face (ftLib->library, file.getFullPathName().toUTF8(), faceIndex, &face) != 0)
|
||||
face = 0;
|
||||
}
|
||||
|
||||
~FTFaceWrapper()
|
||||
{
|
||||
if (face != 0)
|
||||
FT_Done_Face (face);
|
||||
}
|
||||
|
||||
FT_Face face;
|
||||
FTLibWrapper::Ptr library;
|
||||
|
||||
typedef ReferenceCountedObjectPtr <FTFaceWrapper> Ptr;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class LinuxFontFileIterator
|
||||
{
|
||||
public:
|
||||
LinuxFontFileIterator()
|
||||
: index (0)
|
||||
{
|
||||
fontDirs.addTokens (CharPointer_UTF8 (getenv ("JUCE_FONT_PATH")), ";,", String::empty);
|
||||
fontDirs.removeEmptyStrings (true);
|
||||
|
||||
if (fontDirs.size() == 0)
|
||||
{
|
||||
const ScopedPointer<XmlElement> fontsInfo (XmlDocument::parse (File ("/etc/fonts/fonts.conf")));
|
||||
|
||||
if (fontsInfo != nullptr)
|
||||
forEachXmlChildElementWithTagName (*fontsInfo, e, "dir")
|
||||
{
|
||||
forEachXmlChildElementWithTagName (*fontsInfo, e, "dir")
|
||||
String fontPath (e->getAllSubText().trim());
|
||||
|
||||
if (fontPath.isNotEmpty())
|
||||
{
|
||||
String fontPath (e->getAllSubText().trim());
|
||||
|
||||
if (fontPath.isNotEmpty())
|
||||
if (e->getStringAttribute ("prefix") == "xdg")
|
||||
{
|
||||
if (e->getStringAttribute ("prefix") == "xdg")
|
||||
{
|
||||
String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String::empty));
|
||||
String xdgDataHome (SystemStats::getEnvironmentVariable ("XDG_DATA_HOME", String::empty));
|
||||
|
||||
if (xdgDataHome.trimStart().isEmpty())
|
||||
xdgDataHome = "~/.local/share";
|
||||
if (xdgDataHome.trimStart().isEmpty())
|
||||
xdgDataHome = "~/.local/share";
|
||||
|
||||
fontPath = File (xdgDataHome).getChildFile (fontPath).getFullPathName();
|
||||
}
|
||||
|
||||
fontDirs.add (fontPath);
|
||||
fontPath = File (xdgDataHome).getChildFile (fontPath).getFullPathName();
|
||||
}
|
||||
|
||||
fontDirs.add (fontPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fontDirs.size() == 0)
|
||||
fontDirs.add ("/usr/X11R6/lib/X11/fonts");
|
||||
|
||||
fontDirs.removeDuplicates (false);
|
||||
}
|
||||
|
||||
bool next()
|
||||
{
|
||||
if (iter != nullptr)
|
||||
{
|
||||
while (iter->next())
|
||||
if (getFile().hasFileExtension ("ttf;pfb;pcf;otf"))
|
||||
return true;
|
||||
}
|
||||
if (fontDirs.size() == 0)
|
||||
fontDirs.add ("/usr/X11R6/lib/X11/fonts");
|
||||
|
||||
if (index >= fontDirs.size())
|
||||
return false;
|
||||
fontDirs.removeDuplicates (false);
|
||||
}
|
||||
|
||||
iter = new DirectoryIterator (File::getCurrentWorkingDirectory()
|
||||
.getChildFile (fontDirs [index++]), true);
|
||||
return next();
|
||||
}
|
||||
|
||||
File getFile() const { jassert (iter != nullptr); return iter->getFile(); }
|
||||
|
||||
private:
|
||||
StringArray fontDirs;
|
||||
int index;
|
||||
ScopedPointer<DirectoryIterator> iter;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (LinuxFontFileIterator)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
class FTTypefaceList : private DeletedAtShutdown
|
||||
{
|
||||
public:
|
||||
FTTypefaceList()
|
||||
: library (new FTLibWrapper())
|
||||
{
|
||||
LinuxFontFileIterator fontFileIterator;
|
||||
|
||||
while (fontFileIterator.next())
|
||||
{
|
||||
int faceIndex = 0;
|
||||
int numFaces = 0;
|
||||
|
||||
do
|
||||
{
|
||||
FTFaceWrapper face (library, fontFileIterator.getFile(), faceIndex);
|
||||
|
||||
if (face.face != 0)
|
||||
{
|
||||
if (faceIndex == 0)
|
||||
numFaces = face.face->num_faces;
|
||||
|
||||
if ((face.face->face_flags & FT_FACE_FLAG_SCALABLE) != 0)
|
||||
faces.add (new KnownTypeface (fontFileIterator.getFile(), faceIndex, face));
|
||||
}
|
||||
|
||||
++faceIndex;
|
||||
}
|
||||
while (faceIndex < numFaces);
|
||||
}
|
||||
}
|
||||
|
||||
~FTTypefaceList()
|
||||
{
|
||||
clearSingletonInstance();
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct KnownTypeface
|
||||
{
|
||||
KnownTypeface (const File& f, const int index, const FTFaceWrapper& face)
|
||||
: file (f),
|
||||
family (face.face->family_name),
|
||||
style (face.face->style_name),
|
||||
faceIndex (index),
|
||||
isMonospaced ((face.face->face_flags & FT_FACE_FLAG_FIXED_WIDTH) != 0),
|
||||
isSansSerif (isFaceSansSerif (family))
|
||||
{
|
||||
}
|
||||
|
||||
const File file;
|
||||
const String family, style;
|
||||
const int faceIndex;
|
||||
const bool isMonospaced, isSansSerif;
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (KnownTypeface)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle)
|
||||
{
|
||||
const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle);
|
||||
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, "Regular");
|
||||
if (ftFace == nullptr) ftFace = matchTypeface (fontName, String::empty);
|
||||
|
||||
if (ftFace != nullptr)
|
||||
{
|
||||
if (FTFaceWrapper::Ptr face = new FTFaceWrapper (library, ftFace->file, ftFace->faceIndex))
|
||||
{
|
||||
// If there isn't a unicode charmap then select the first one.
|
||||
if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0)
|
||||
FT_Set_Charmap (face->face, face->face->charmaps[0]);
|
||||
|
||||
return face;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
StringArray findAllFamilyNames() const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
s.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
StringArray findAllTypefaceStyles (const String& family) const
|
||||
{
|
||||
StringArray s;
|
||||
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
if (face->family == family)
|
||||
s.addIfNotAlreadyThere (face->style);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void getMonospacedNames (StringArray& monoSpaced) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (faces.getUnchecked(i)->isMonospaced)
|
||||
monoSpaced.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
void getSerifNames (StringArray& serif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (! faces.getUnchecked(i)->isSansSerif)
|
||||
serif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
void getSansSerifNames (StringArray& sansSerif) const
|
||||
{
|
||||
for (int i = 0; i < faces.size(); i++)
|
||||
if (faces.getUnchecked(i)->isSansSerif)
|
||||
sansSerif.addIfNotAlreadyThere (faces.getUnchecked(i)->family);
|
||||
}
|
||||
|
||||
juce_DeclareSingleton_SingleThreaded_Minimal (FTTypefaceList);
|
||||
|
||||
private:
|
||||
FTLibWrapper::Ptr library;
|
||||
OwnedArray<KnownTypeface> faces;
|
||||
|
||||
const KnownTypeface* matchTypeface (const String& familyName, const String& style) const noexcept
|
||||
{
|
||||
for (int i = 0; i < faces.size(); ++i)
|
||||
{
|
||||
const KnownTypeface* const face = faces.getUnchecked(i);
|
||||
|
||||
if (face->family == familyName
|
||||
&& (face->style.equalsIgnoreCase (style) || style.isEmpty()))
|
||||
return face;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool isFaceSansSerif (const String& family)
|
||||
{
|
||||
const char* sansNames[] = { "Sans", "Verdana", "Arial", "Ubuntu" };
|
||||
|
||||
for (int i = 0; i < numElementsInArray (sansNames); ++i)
|
||||
if (family.containsIgnoreCase (sansNames[i]))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTTypefaceList)
|
||||
};
|
||||
|
||||
juce_ImplementSingleton_SingleThreaded (FTTypefaceList)
|
||||
|
||||
|
||||
//==============================================================================
|
||||
class FreeTypeTypeface : public CustomTypeface
|
||||
{
|
||||
public:
|
||||
FreeTypeTypeface (const Font& font)
|
||||
: faceWrapper (FTTypefaceList::getInstance()
|
||||
->createFace (font.getTypefaceName(), font.getTypefaceStyle()))
|
||||
{
|
||||
if (faceWrapper != nullptr)
|
||||
{
|
||||
setCharacteristics (font.getTypefaceName(),
|
||||
font.getTypefaceStyle(),
|
||||
faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender),
|
||||
L' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
DBG ("Failed to create typeface: " << font.toString());
|
||||
}
|
||||
}
|
||||
|
||||
bool loadGlyphIfPossible (const juce_wchar character)
|
||||
{
|
||||
if (faceWrapper != nullptr)
|
||||
{
|
||||
FT_Face face = faceWrapper->face;
|
||||
const unsigned int glyphIndex = FT_Get_Char_Index (face, character);
|
||||
|
||||
if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM) == 0
|
||||
&& face->glyph->format == ft_glyph_format_outline)
|
||||
{
|
||||
const float scale = 1.0f / (float) (face->ascender - face->descender);
|
||||
Path destShape;
|
||||
|
||||
if (getGlyphShape (destShape, face->glyph->outline, scale))
|
||||
{
|
||||
addGlyph (character, destShape, face->glyph->metrics.horiAdvance * scale);
|
||||
|
||||
if ((face->face_flags & FT_FACE_FLAG_KERNING) != 0)
|
||||
addKerning (face, character, glyphIndex);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
FTFaceWrapper::Ptr faceWrapper;
|
||||
|
||||
bool getGlyphShape (Path& destShape, const FT_Outline& outline, const float scaleX)
|
||||
{
|
||||
const float scaleY = -scaleX;
|
||||
const short* const contours = outline.contours;
|
||||
const char* const tags = outline.tags;
|
||||
const FT_Vector* const points = outline.points;
|
||||
|
||||
for (int c = 0; c < outline.n_contours; ++c)
|
||||
{
|
||||
const int startPoint = (c == 0) ? 0 : contours [c - 1] + 1;
|
||||
const int endPoint = contours[c];
|
||||
|
||||
for (int p = startPoint; p <= endPoint; ++p)
|
||||
{
|
||||
const float x = scaleX * points[p].x;
|
||||
const float y = scaleY * points[p].y;
|
||||
|
||||
if (p == startPoint)
|
||||
{
|
||||
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
float x2 = scaleX * points [endPoint].x;
|
||||
float y2 = scaleY * points [endPoint].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags[endPoint]) != FT_Curve_Tag_On)
|
||||
{
|
||||
x2 = (x + x2) * 0.5f;
|
||||
y2 = (y + y2) * 0.5f;
|
||||
}
|
||||
|
||||
destShape.startNewSubPath (x2, y2);
|
||||
}
|
||||
else
|
||||
{
|
||||
destShape.startNewSubPath (x, y);
|
||||
}
|
||||
}
|
||||
|
||||
if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_On)
|
||||
{
|
||||
if (p != startPoint)
|
||||
destShape.lineTo (x, y);
|
||||
}
|
||||
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
const int nextIndex = (p == endPoint) ? startPoint : p + 1;
|
||||
float x2 = scaleX * points [nextIndex].x;
|
||||
float y2 = scaleY * points [nextIndex].y;
|
||||
|
||||
if (FT_CURVE_TAG (tags [nextIndex]) == FT_Curve_Tag_Conic)
|
||||
{
|
||||
x2 = (x + x2) * 0.5f;
|
||||
y2 = (y + y2) * 0.5f;
|
||||
}
|
||||
else
|
||||
{
|
||||
++p;
|
||||
}
|
||||
|
||||
destShape.quadraticTo (x, y, x2, y2);
|
||||
}
|
||||
else if (FT_CURVE_TAG (tags[p]) == FT_Curve_Tag_Cubic)
|
||||
{
|
||||
const int next1 = p + 1;
|
||||
const int next2 = (p == (endPoint - 1)) ? startPoint : (p + 2);
|
||||
|
||||
if (p >= endPoint
|
||||
|| FT_CURVE_TAG (tags[next1]) != FT_Curve_Tag_Cubic
|
||||
|| FT_CURVE_TAG (tags[next2]) != FT_Curve_Tag_On)
|
||||
return false;
|
||||
|
||||
const float x2 = scaleX * points [next1].x;
|
||||
const float y2 = scaleY * points [next1].y;
|
||||
const float x3 = scaleX * points [next2].x;
|
||||
const float y3 = scaleY * points [next2].y;
|
||||
|
||||
destShape.cubicTo (x, y, x2, y2, x3, y3);
|
||||
p += 2;
|
||||
}
|
||||
}
|
||||
|
||||
destShape.closeSubPath();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void addKerning (FT_Face face, const uint32 character, const uint32 glyphIndex)
|
||||
{
|
||||
const float height = (float) (face->ascender - face->descender);
|
||||
|
||||
uint32 rightGlyphIndex;
|
||||
uint32 rightCharCode = FT_Get_First_Char (face, &rightGlyphIndex);
|
||||
|
||||
while (rightGlyphIndex != 0)
|
||||
{
|
||||
FT_Vector kerning;
|
||||
|
||||
if (FT_Get_Kerning (face, glyphIndex, rightGlyphIndex, ft_kerning_unscaled, &kerning) == 0
|
||||
&& kerning.x != 0)
|
||||
addKerningPair (character, rightCharCode, kerning.x / height);
|
||||
|
||||
rightCharCode = FT_Get_Next_Char (face, rightCharCode, &rightGlyphIndex);
|
||||
}
|
||||
}
|
||||
|
||||
JUCE_DECLARE_NON_COPYABLE (FreeTypeTypeface)
|
||||
};
|
||||
|
||||
//==============================================================================
|
||||
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
|
||||
{
|
||||
return new FreeTypeTypeface (font);
|
||||
|
|
@ -485,6 +77,11 @@ StringArray Font::findAllTypefaceStyles (const String& family)
|
|||
return FTTypefaceList::getInstance()->findAllTypefaceStyles (family);
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//==============================================================================
|
||||
struct DefaultFontNames
|
||||
{
|
||||
|
|
@ -495,6 +92,15 @@ struct DefaultFontNames
|
|||
{
|
||||
}
|
||||
|
||||
String getRealFontName (const String& faceName) const
|
||||
{
|
||||
if (faceName == Font::getDefaultSansSerifFontName()) return defaultSans;
|
||||
if (faceName == Font::getDefaultSerifFontName()) return defaultSerif;
|
||||
if (faceName == Font::getDefaultMonospacedFontName()) return defaultFixed;
|
||||
|
||||
return faceName;
|
||||
}
|
||||
|
||||
String defaultSans, defaultSerif, defaultFixed;
|
||||
|
||||
private:
|
||||
|
|
@ -525,7 +131,7 @@ private:
|
|||
FTTypefaceList::getInstance()->getSansSerifNames (allFonts);
|
||||
|
||||
const char* targets[] = { "Verdana", "Bitstream Vera Sans", "Luxi Sans",
|
||||
"Liberation Sans", "DejaVu Sans", "Sans", 0 };
|
||||
"Liberation Sans", "DejaVu Sans", "Sans", nullptr };
|
||||
return pickBestFont (allFonts, targets);
|
||||
}
|
||||
|
||||
|
|
@ -535,7 +141,7 @@ private:
|
|||
FTTypefaceList::getInstance()->getSerifNames (allFonts);
|
||||
|
||||
const char* targets[] = { "Bitstream Vera Serif", "Times", "Nimbus Roman",
|
||||
"Liberation Serif", "DejaVu Serif", "Serif", 0 };
|
||||
"Liberation Serif", "DejaVu Serif", "Serif", nullptr };
|
||||
return pickBestFont (allFonts, targets);
|
||||
}
|
||||
|
||||
|
|
@ -545,7 +151,7 @@ private:
|
|||
FTTypefaceList::getInstance()->getMonospacedNames (allFonts);
|
||||
|
||||
const char* targets[] = { "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Sans Mono",
|
||||
"Liberation Mono", "Courier", "DejaVu Mono", "Mono", 0 };
|
||||
"Liberation Mono", "Courier", "DejaVu Mono", "Mono", nullptr };
|
||||
return pickBestFont (allFonts, targets);
|
||||
}
|
||||
|
||||
|
|
@ -556,18 +162,7 @@ Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
|
|||
{
|
||||
static DefaultFontNames defaultNames;
|
||||
|
||||
String faceName (font.getTypefaceName());
|
||||
|
||||
if (faceName == getDefaultSansSerifFontName()) faceName = defaultNames.defaultSans;
|
||||
else if (faceName == getDefaultSerifFontName()) faceName = defaultNames.defaultSerif;
|
||||
else if (faceName == getDefaultMonospacedFontName()) faceName = defaultNames.defaultFixed;
|
||||
|
||||
Font f (font);
|
||||
f.setTypefaceName (faceName);
|
||||
f.setTypefaceName (defaultNames.getRealFontName (font.getTypefaceName()));
|
||||
return Typeface::createSystemTypefaceFor (f);
|
||||
}
|
||||
|
||||
bool TextLayout::createNativeLayout (const AttributedString&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue