1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Added functionality for loading in-memory fonts! See Typeface::createSystemTypefaceFor()

This commit is contained in:
jules 2013-12-19 15:39:30 +00:00
parent dcb7e61ed5
commit d8c065c81f
6 changed files with 283 additions and 58 deletions

View file

@ -64,6 +64,12 @@ public:
/** Creates a new system typeface. */
static Ptr createSystemTypefaceFor (const Font& font);
/** Attempts to create a font from some raw font file data (e.g. a TTF or OTF file image).
The system will take its own internal copy of the data, so you can free the block once
this method has returned.
*/
static Ptr createSystemTypefaceFor (const void* fontFileData, size_t fontFileDataSize);
//==============================================================================
/** Destructor. */
virtual ~Typeface();

View file

@ -315,6 +315,12 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
return new AndroidTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void*, size_t)
{
jassertfalse; // not yet implemented!
return nullptr;
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not available unless using FreeType

View file

@ -56,6 +56,14 @@ struct FTFaceWrapper : public ReferenceCountedObject
face = 0;
}
FTFaceWrapper (const FTLibWrapper::Ptr& ftLib, const void* data, size_t dataSize, int faceIndex)
: face (0), library (ftLib), savedFaceData (data, dataSize)
{
if (FT_New_Memory_Face (ftLib->library, (const FT_Byte*) savedFaceData.getData(),
(FT_Long) savedFaceData.getSize(), faceIndex, &face) != 0)
face = 0;
}
~FTFaceWrapper()
{
if (face != 0)
@ -64,8 +72,9 @@ struct FTFaceWrapper : public ReferenceCountedObject
FT_Face face;
FTLibWrapper::Ptr library;
MemoryBlock savedFaceData;
typedef ReferenceCountedObjectPtr <FTFaceWrapper> Ptr;
typedef ReferenceCountedObjectPtr<FTFaceWrapper> Ptr;
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (FTFaceWrapper)
};
@ -106,6 +115,25 @@ public:
};
//==============================================================================
static FTFaceWrapper::Ptr selectUnicodeCharmap (FTFaceWrapper* face)
{
if (face != nullptr)
if (FT_Select_Charmap (face->face, ft_encoding_unicode) != 0)
FT_Set_Charmap (face->face, face->face->charmaps[0]);
return face;
}
FTFaceWrapper::Ptr createFace (const void* data, size_t dataSize, int index)
{
return selectUnicodeCharmap (new FTFaceWrapper (library, data, dataSize, index));
}
FTFaceWrapper::Ptr createFace (const File& file, int index)
{
return selectUnicodeCharmap (new FTFaceWrapper (library, file, index));
}
FTFaceWrapper::Ptr createFace (const String& fontName, const String& fontStyle)
{
const KnownTypeface* ftFace = matchTypeface (fontName, fontStyle);
@ -114,16 +142,7 @@ public:
if (ftFace == nullptr) ftFace = matchTypeface (fontName, String());
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 createFace (ftFace->file, ftFace->faceIndex);
return nullptr;
}
@ -272,20 +291,27 @@ class FreeTypeTypeface : public CustomTypeface
{
public:
FreeTypeTypeface (const Font& font)
: faceWrapper (FTTypefaceList::getInstance()
->createFace (font.getTypefaceName(), font.getTypefaceStyle()))
: 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());
}
initialiseCharacteristics (font.getTypefaceName(),
font.getTypefaceStyle());
}
FreeTypeTypeface (const void* data, size_t dataSize)
: faceWrapper (FTTypefaceList::getInstance()->createFace (data, dataSize, 0))
{
if (faceWrapper != nullptr)
initialiseCharacteristics (faceWrapper->face->family_name,
faceWrapper->face->style_name);
}
void initialiseCharacteristics (const String& name, const String& style)
{
setCharacteristics (name, style,
faceWrapper->face->ascender / (float) (faceWrapper->face->ascender - faceWrapper->face->descender),
L' ');
}
bool loadGlyphIfPossible (const juce_wchar character)
@ -295,7 +321,7 @@ public:
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
if (FT_Load_Glyph (face, glyphIndex, FT_LOAD_NO_SCALE | FT_LOAD_NO_BITMAP | FT_LOAD_IGNORE_TRANSFORM | FT_LOAD_NO_HINTING) == 0
&& face->glyph->format == ft_glyph_format_outline)
{
const float scale = 1.0f / (float) (face->ascender - face->descender);

View file

@ -69,6 +69,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
return new FreeTypeTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
{
return new FreeTypeTypeface (data, dataSize);
}
void Typeface::scanFolderForFonts (const File& folder)
{
FTTypefaceList::getInstance()->scanFontPaths (StringArray (folder.getFullPathName()));

View file

@ -496,28 +496,73 @@ public:
if (ctFontRef != nullptr)
{
const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
const float ctTotalHeight = ctAscent + ctDescent;
ascent = ctAscent / ctTotalHeight;
unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor);
fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
const short zero = 0;
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
CFTypeRef values[] = { ctFontRef, numberRef };
attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease (numberRef);
initialiseMetrics();
}
}
OSXTypeface (const void* data, size_t dataSize)
: Typeface (String(), String()),
fontRef (nullptr),
ctFontRef (nullptr),
fontHeightToPointsFactor (1.0f),
renderingTransform (CGAffineTransformIdentity),
attributedStringAtts (nullptr),
ascent (0.0f),
unitsToHeightScaleFactor (0.0f)
{
CFDataRef cfData = CFDataCreate (kCFAllocatorDefault, (const UInt8*) data, (CFIndex) dataSize);
CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData);
CFRelease (cfData);
fontRef = CGFontCreateWithDataProvider (provider);
CGDataProviderRelease (provider);
if (fontRef != nullptr)
{
ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
if (ctFontRef != nullptr)
{
if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
{
name = String::fromCFString (fontName);
CFRelease (fontName);
}
if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
{
style = String::fromCFString (fontStyle);
CFRelease (fontStyle);
}
initialiseMetrics();
}
}
}
void initialiseMetrics()
{
const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
const float ctTotalHeight = ctAscent + ctDescent;
ascent = ctAscent / ctTotalHeight;
unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
pathTransform = AffineTransform::identity.scale (unitsToHeightScaleFactor);
fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
const short zero = 0;
CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
CFTypeRef values[] = { ctFontRef, numberRef };
attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease (numberRef);
}
~OSXTypeface()
{
if (attributedStringAtts != nullptr)
@ -730,7 +775,7 @@ StringArray Font::findAllTypefaceStyles (const String& family)
CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
CFRelease (fontDescAttributes);
CFArrayRef fontFamilyArray = CFArrayCreate(kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
CFRelease (ctFontDescRef);
CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
@ -1202,6 +1247,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
return new OSXTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
{
return new OSXTypeface (data, dataSize);
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not implemented on this platform

View file

@ -22,6 +22,115 @@
==============================================================================
*/
/* This is some quick-and-dirty code to extract the typeface name from a lump of TTF file data.
It's needed because although win32 will happily load a TTF file from in-memory data, it won't
tell you the name of the damned font that it just loaded.. and in order to actually use the font,
you need to know its name!! Anyway, this awful hack seems to work for most fonts.
*/
namespace TTFNameExtractor
{
struct OffsetTable
{
uint32 version;
uint16 numTables, searchRange, entrySelector, rangeShift;
};
struct TableDirectory
{
char tag[4];
uint32 checkSum, offset, length;
};
struct NamingTable
{
uint16 formatSelector;
uint16 numberOfNameRecords;
uint16 offsetStartOfStringStorage;
};
struct NameRecord
{
uint16 platformID, encodingID, languageID;
uint16 nameID, stringLength, offsetFromStorageArea;
};
static String parseNameRecord (MemoryInputStream& input, const NameRecord& nameRecord,
const int64 directoryOffset, const int64 offsetOfStringStorage)
{
String result;
const int64 oldPos = input.getPosition();
input.setPosition (directoryOffset + offsetOfStringStorage + ByteOrder::swapIfLittleEndian (nameRecord.offsetFromStorageArea));
const int stringLength = (int) ByteOrder::swapIfLittleEndian (nameRecord.stringLength);
const int platformID = ByteOrder::swapIfLittleEndian (nameRecord.platformID);
if (platformID == 0 || platformID == 3)
{
const int numChars = stringLength / 2 + 1;
HeapBlock<uint16> buffer;
buffer.calloc (numChars + 1);
input.read (buffer, stringLength);
for (int i = 0; i < numChars; ++i)
buffer[i] = ByteOrder::swapIfLittleEndian (buffer[i]);
static_jassert (sizeof (CharPointer_UTF16::CharType) == sizeof (uint16));
result = CharPointer_UTF16 ((CharPointer_UTF16::CharType*) buffer.getData());
}
else
{
HeapBlock<char> buffer;
buffer.calloc (stringLength + 1);
input.read (buffer, stringLength);
result = CharPointer_UTF8 (buffer.getData());
}
input.setPosition (oldPos);
return result;
}
static String parseNameTable (MemoryInputStream& input, int64 directoryOffset)
{
input.setPosition (directoryOffset);
NamingTable namingTable = { 0 };
input.read (&namingTable, sizeof (namingTable));
for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (namingTable.numberOfNameRecords); ++i)
{
NameRecord nameRecord = { 0 };
input.read (&nameRecord, sizeof (nameRecord));
if (ByteOrder::swapIfLittleEndian (nameRecord.nameID) == 4)
{
const String result (parseNameRecord (input, nameRecord, directoryOffset,
ByteOrder::swapIfLittleEndian (namingTable.offsetStartOfStringStorage)));
if (result.isNotEmpty())
return result;
}
}
return String();
}
static String getTypefaceNameFromFile (MemoryInputStream& input)
{
OffsetTable offsetTable = { 0 };
input.read (&offsetTable, sizeof (offsetTable));
for (int i = 0; i < (int) ByteOrder::swapIfLittleEndian (offsetTable.numTables); ++i)
{
TableDirectory tableDirectory = { 0 };
input.read (&tableDirectory, sizeof (tableDirectory));
if (String (tableDirectory.tag, sizeof (tableDirectory.tag)).equalsIgnoreCase ("name"))
return parseNameTable (input, ByteOrder::swapIfLittleEndian (tableDirectory.offset));
}
return String();
}
}
namespace FontEnumerators
{
static int CALLBACK fontEnum2 (ENUMLOGFONTEXW* lpelfe, NEWTEXTMETRICEXW*, int type, LPARAM lParam)
@ -204,23 +313,29 @@ class WindowsTypeface : public Typeface
{
public:
WindowsTypeface (const Font& font)
: Typeface (font.getTypefaceName(),
font.getTypefaceStyle()),
fontH (0),
previousFontH (0),
dc (CreateCompatibleDC (0)),
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()),
fontH (0), previousFontH (0),
dc (CreateCompatibleDC (0)), memoryFont (0),
ascent (1.0f), heightToPointsFactor (1.0f),
defaultGlyph (-1)
{
loadFont();
loadFont (name);
}
if (GetTextMetrics (dc, &tm))
{
heightToPointsFactor = (72.0f / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
ascent = tm.tmAscent / (float) tm.tmHeight;
defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
createKerningPairs (dc, (float) tm.tmHeight);
}
WindowsTypeface (const void* data, size_t dataSize)
: Typeface (String(), String()),
fontH (0), previousFontH (0),
dc (CreateCompatibleDC (0)), memoryFont (0),
ascent (1.0f), heightToPointsFactor (1.0f),
defaultGlyph (-1)
{
DWORD numInstalled = 0;
memoryFont = AddFontMemResourceEx (const_cast<void*> (data), (DWORD) dataSize,
nullptr, &numInstalled);
MemoryInputStream m (data, dataSize, false);
loadFont (TTFNameExtractor::getTypefaceNameFromFile (m));
}
~WindowsTypeface()
@ -230,6 +345,9 @@ public:
if (fontH != 0)
DeleteObject (fontH);
if (memoryFont != 0)
RemoveFontMemResourceEx (memoryFont);
}
float getAscent() const { return ascent; }
@ -353,6 +471,7 @@ private:
HGDIOBJ previousFontH;
HDC dc;
TEXTMETRIC tm;
HANDLE memoryFont;
float ascent, heightToPointsFactor;
int defaultGlyph, heightInPoints;
@ -375,7 +494,7 @@ private:
SortedSet<KerningPair> kerningPairs;
void loadFont()
void loadFont (const String& faceName)
{
SetMapperFlags (dc, 0);
SetMapMode (dc, MM_TEXT);
@ -389,7 +508,7 @@ private:
lf.lfItalic = (BYTE) (style == "Italic" ? TRUE : FALSE);
lf.lfWeight = style == "Bold" ? FW_BOLD : FW_NORMAL;
lf.lfHeight = -256;
name.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
faceName.copyToUTF16 (lf.lfFaceName, sizeof (lf.lfFaceName));
HFONT standardSizedFont = CreateFontIndirect (&lf);
@ -411,6 +530,14 @@ private:
}
}
}
if (GetTextMetrics (dc, &tm))
{
heightToPointsFactor = (72.0f / GetDeviceCaps (dc, LOGPIXELSY)) * heightInPoints / (float) tm.tmHeight;
ascent = tm.tmAscent / (float) tm.tmHeight;
defaultGlyph = getGlyphForChar (dc, tm.tmDefaultChar);
createKerningPairs (dc, (float) tm.tmHeight);
}
}
void createKerningPairs (HDC dc, const float height)
@ -499,6 +626,11 @@ Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
return new WindowsTypeface (font);
}
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
{
return new WindowsTypeface (data, dataSize);
}
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not implemented on this platform