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:
parent
dcb7e61ed5
commit
d8c065c81f
6 changed files with 283 additions and 58 deletions
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue