fix macOS build (following Projucer changes made in Windows, which removed /Applications/JUCE/modules from its headers). move JUCE headers under source control, so that Windows and macOS can both build against same version of JUCE. remove AUv3 target (I think it's an iOS thing, so it will never work with this macOS fluidsynth dylib).

This commit is contained in:
Alex Birch
2018-06-17 13:34:53 +01:00
parent a2be47c887
commit dff4d13a1d
1563 changed files with 601601 additions and 3466 deletions

View File

@ -0,0 +1,856 @@
/*
==============================================================================
This file is part of the JUCE library.
Copyright (c) 2017 - ROLI Ltd.
JUCE is an open source library subject to commercial or open-source
licensing.
By using JUCE, you agree to the terms of both the JUCE 5 End-User License
Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
27th April 2017).
End User License Agreement: www.juce.com/juce-5-licence
Privacy Policy: www.juce.com/juce-5-privacy-policy
Or: You may also use this code under the terms of the GPL v3 (see
www.gnu.org/licenses).
JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
DISCLAIMED.
==============================================================================
*/
namespace juce
{
static constexpr float referenceFontSize = 1024.0f;
static CTFontRef getCTFontFromTypeface (const Font&);
namespace CoreTextTypeLayout
{
static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform)
{
auto availableStyles = Font::findAllTypefaceStyles (font.getTypefaceName());
auto style = font.getTypefaceStyle();
if (! availableStyles.contains (style))
{
if (font.isItalic()) // Fake-up an italic font if there isn't a real one.
requiredTransform = CGAffineTransformMake (1.0f, 0, 0.25f, 1.0f, 0, 0);
return availableStyles[0];
}
return style;
}
static float getFontTotalHeight (CTFontRef font)
{
return std::abs ((float) CTFontGetAscent (font))
+ std::abs ((float) CTFontGetDescent (font));
}
static float getHeightToPointsFactor (CTFontRef font)
{
return referenceFontSize / getFontTotalHeight (font);
}
static CTFontRef getFontWithPointSize (CTFontRef font, float size)
{
auto newFont = CTFontCreateCopyWithAttributes (font, size, nullptr, nullptr);
CFRelease (font);
return newFont;
}
static CTFontRef createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired)
{
auto cfFontFamily = FontStyleHelpers::getConcreteFamilyName (font).toCFString();
auto cfFontStyle = findBestAvailableStyle (font, transformRequired).toCFString();
CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
CFTypeRef values[] = { cfFontFamily, cfFontStyle };
auto fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys,
(const void**) &values,
numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);
CFRelease (cfFontStyle);
auto ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
CFRelease (fontDescAttributes);
auto ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSizePoints, nullptr);
CFRelease (ctFontDescRef);
CFRelease (cfFontFamily);
return ctFontRef;
}
//==============================================================================
struct Advances
{
Advances (CTRunRef run, CFIndex numGlyphs) : advances (CTRunGetAdvancesPtr (run))
{
if (advances == nullptr)
{
local.malloc (numGlyphs);
CTRunGetAdvances (run, CFRangeMake (0, 0), local);
advances = local;
}
}
const CGSize* advances;
HeapBlock<CGSize> local;
};
struct Glyphs
{
Glyphs (CTRunRef run, size_t numGlyphs) : glyphs (CTRunGetGlyphsPtr (run))
{
if (glyphs == nullptr)
{
local.malloc (numGlyphs);
CTRunGetGlyphs (run, CFRangeMake (0, 0), local);
glyphs = local;
}
}
const CGGlyph* glyphs;
HeapBlock<CGGlyph> local;
};
struct Positions
{
Positions (CTRunRef run, size_t numGlyphs) : points (CTRunGetPositionsPtr (run))
{
if (points == nullptr)
{
local.malloc (numGlyphs);
CTRunGetPositions (run, CFRangeMake (0, 0), local);
points = local;
}
}
const CGPoint* points;
HeapBlock<CGPoint> local;
};
struct LineInfo
{
LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex)
{
CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin);
CTLineGetTypographicBounds (line, &ascent, &descent, &leading);
}
CGPoint origin;
CGFloat ascent, descent, leading;
};
static CTFontRef getOrCreateFont (const Font& f)
{
if (auto ctf = getCTFontFromTypeface (f))
{
CFRetain (ctf);
return ctf;
}
CGAffineTransform transform;
return createCTFont (f, referenceFontSize, transform);
}
//==============================================================================
static CTTextAlignment getTextAlignment (const AttributedString& text)
{
switch (text.getJustification().getOnlyHorizontalFlags())
{
#if defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
case Justification::right: return kCTTextAlignmentRight;
case Justification::horizontallyCentred: return kCTTextAlignmentCenter;
case Justification::horizontallyJustified: return kCTTextAlignmentJustified;
default: return kCTTextAlignmentLeft;
#else
case Justification::right: return kCTRightTextAlignment;
case Justification::horizontallyCentred: return kCTCenterTextAlignment;
case Justification::horizontallyJustified: return kCTJustifiedTextAlignment;
default: return kCTLeftTextAlignment;
#endif
}
}
static CTLineBreakMode getLineBreakMode (const AttributedString& text)
{
switch (text.getWordWrap())
{
case AttributedString::none: return kCTLineBreakByClipping;
case AttributedString::byChar: return kCTLineBreakByCharWrapping;
default: return kCTLineBreakByWordWrapping;
}
}
static CTWritingDirection getWritingDirection (const AttributedString& text)
{
switch (text.getReadingDirection())
{
case AttributedString::rightToLeft: return kCTWritingDirectionRightToLeft;
case AttributedString::leftToRight: return kCTWritingDirectionLeftToRight;
default: return kCTWritingDirectionNatural;
}
}
//==============================================================================
static CFAttributedStringRef createCFAttributedString (const AttributedString& text)
{
#if JUCE_IOS
auto rgbColourSpace = CGColorSpaceCreateDeviceRGB();
#endif
auto attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0);
auto cfText = text.getText().toCFString();
CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText);
CFRelease (cfText);
auto numCharacterAttributes = text.getNumAttributes();
auto attribStringLen = CFAttributedStringGetLength (attribString);
for (int i = 0; i < numCharacterAttributes; ++i)
{
auto& attr = text.getAttribute (i);
auto rangeStart = attr.range.getStart();
if (rangeStart >= attribStringLen)
continue;
auto range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart);
if (auto ctFontRef = getOrCreateFont (attr.font))
{
ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef));
CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef);
auto extraKerning = attr.font.getExtraKerningFactor();
if (extraKerning != 0)
{
extraKerning *= attr.font.getHeight();
auto numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning);
CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef);
CFRelease (numberRef);
}
CFRelease (ctFontRef);
}
{
auto col = attr.colour;
#if JUCE_IOS
const CGFloat components[] = { col.getFloatRed(),
col.getFloatGreen(),
col.getFloatBlue(),
col.getFloatAlpha() };
auto colour = CGColorCreate (rgbColourSpace, components);
#else
auto colour = CGColorCreateGenericRGB (col.getFloatRed(),
col.getFloatGreen(),
col.getFloatBlue(),
col.getFloatAlpha());
#endif
CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour);
CGColorRelease (colour);
}
}
// Paragraph Attributes
auto ctTextAlignment = getTextAlignment (text);
auto ctLineBreakMode = getLineBreakMode (text);
auto ctWritingDirection = getWritingDirection (text);
CGFloat ctLineSpacing = text.getLineSpacing();
CTParagraphStyleSetting settings[] =
{
{ kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
{ kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
{ kCTParagraphStyleSpecifierBaseWritingDirection, sizeof (CTWritingDirection), &ctWritingDirection},
{ kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
};
auto ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings));
CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
kCTParagraphStyleAttributeName, ctParagraphStyleRef);
CFRelease (ctParagraphStyleRef);
#if JUCE_IOS
CGColorSpaceRelease (rgbColourSpace);
#endif
return attribString;
}
static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds)
{
auto attribString = createCFAttributedString (text);
auto framesetter = CTFramesetterCreateWithAttributedString (attribString);
CFRelease (attribString);
auto path = CGPathCreateMutable();
CGPathAddRect (path, nullptr, bounds);
auto frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr);
CFRelease (framesetter);
CGPathRelease (path);
return frame;
}
static Range<float> getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex)
{
LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex);
return { (float) (info.origin.y - info.descent),
(float) (info.origin.y + info.ascent) };
}
static float findCTFrameHeight (CTFrameRef frame)
{
auto lines = CTFrameGetLines (frame);
auto numLines = CFArrayGetCount (lines);
if (numLines == 0)
return 0;
auto range = getLineVerticalRange (frame, lines, 0);
if (numLines > 1)
range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1));
return range.getLength();
}
static void drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
const CGContextRef& context, float flipHeight)
{
Rectangle<float> ctFrameArea;
auto verticalJustification = text.getJustification().getOnlyVerticalFlags();
// Ugly hack to fix a bug in OS X Sierra where the CTFrame needs to be slightly
// larger than the font height - otherwise the CTFrame will be invalid
if (verticalJustification == Justification::verticallyCentred)
ctFrameArea = area.withSizeKeepingCentre (area.getWidth(), area.getHeight() * 1.1f);
else if (verticalJustification == Justification::bottom)
ctFrameArea = area.withTop (area.getY() - (area.getHeight() * 0.1f));
else
ctFrameArea = area.withHeight (area.getHeight() * 1.1f);
auto frame = createCTFrame (text, CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
(CGFloat) ctFrameArea.getWidth(), (CGFloat) ctFrameArea.getHeight()));
if (verticalJustification == Justification::verticallyCentred
|| verticalJustification == Justification::bottom)
{
auto adjust = ctFrameArea.getHeight() - findCTFrameHeight (frame);
if (verticalJustification == Justification::verticallyCentred)
adjust *= 0.5f;
CGContextSaveGState (context);
CGContextTranslateCTM (context, 0, -adjust);
CTFrameDraw (frame, context);
CGContextRestoreGState (context);
}
else
{
CTFrameDraw (frame, context);
}
CFRelease (frame);
}
static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
{
auto boundsHeight = glyphLayout.getHeight();
auto frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
auto lines = CTFrameGetLines (frame);
auto numLines = CFArrayGetCount (lines);
glyphLayout.ensureStorageAllocated ((int) numLines);
for (CFIndex i = 0; i < numLines; ++i)
{
auto line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
auto runs = CTLineGetGlyphRuns (line);
auto numRuns = CFArrayGetCount (runs);
auto cfrlineStringRange = CTLineGetStringRange (line);
auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1;
Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
LineInfo lineInfo (frame, line, i);
auto glyphLine = new TextLayout::Line (lineStringRange,
Point<float> ((float) lineInfo.origin.x,
(float) (boundsHeight - lineInfo.origin.y)),
(float) lineInfo.ascent,
(float) lineInfo.descent,
(float) lineInfo.leading,
(int) numRuns);
glyphLayout.addLine (glyphLine);
for (CFIndex j = 0; j < numRuns; ++j)
{
auto run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
auto numGlyphs = CTRunGetGlyphCount (run);
auto runStringRange = CTRunGetStringRange (run);
auto glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
(int) (runStringRange.location + runStringRange.length - 1)),
(int) numGlyphs);
glyphLine->runs.add (glyphRun);
CFDictionaryRef runAttributes = CTRunGetAttributes (run);
CTFontRef ctRunFont;
if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
{
auto cfsFontName = CTFontCopyPostScriptName (ctRunFont);
auto ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr);
CFRelease (cfsFontName);
auto fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef);
CFRelease (ctFontRef);
auto cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute);
auto cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute);
glyphRun->font = Font (String::fromCFString (cfsFontFamily),
String::fromCFString (cfsFontStyle),
(float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
CFRelease (cfsFontStyle);
CFRelease (cfsFontFamily);
}
CGColorRef cgRunColor;
if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
&& CGColorGetNumberOfComponents (cgRunColor) == 4)
{
auto* components = CGColorGetComponents (cgRunColor);
glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
(float) components[1],
(float) components[2],
(float) components[3]);
}
const Glyphs glyphs (run, (size_t) numGlyphs);
const Advances advances (run, numGlyphs);
const Positions positions (run, (size_t) numGlyphs);
for (CFIndex k = 0; k < numGlyphs; ++k)
glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> ((float) positions.points[k].x,
(float) positions.points[k].y),
(float) advances.advances[k].width));
}
}
CFRelease (frame);
}
}
//==============================================================================
class OSXTypeface : public Typeface
{
public:
OSXTypeface (const Font& font)
: Typeface (font.getTypefaceName(), font.getTypefaceStyle()), canBeUsedForLayout (true)
{
ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
if (ctFontRef != nullptr)
{
fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
initialiseMetrics();
}
}
OSXTypeface (const void* data, size_t dataSize)
: Typeface ({}, {}), canBeUsedForLayout (false), dataCopy (data, dataSize)
{
// We can't use CFDataCreate here as this triggers a false positive in ASAN
// so copy the data manually and use CFDataCreateWithBytesNoCopy
auto cfData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) dataCopy.getData(),
(CFIndex) dataCopy.getSize(), kCFAllocatorNull);
auto provider = CGDataProviderCreateWithCFData (cfData);
CFRelease (cfData);
#if JUCE_IOS
// Workaround for a an obscure iOS bug which can cause the app to dead-lock
// when loading custom type faces. See: http://www.openradar.me/18778790 and
// http://stackoverflow.com/questions/40242370/app-hangs-in-simulator
[UIFont systemFontOfSize: 12];
#endif
fontRef = CGFontCreateWithDataProvider (provider);
CGDataProviderRelease (provider);
if (fontRef != nullptr)
{
#if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
#endif
ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
if (ctFontRef != nullptr)
{
if (auto fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
{
name = String::fromCFString (fontName);
CFRelease (fontName);
}
if (auto fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
{
style = String::fromCFString (fontStyle);
CFRelease (fontStyle);
}
initialiseMetrics();
}
}
}
void initialiseMetrics()
{
auto ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
auto ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
auto ctTotalHeight = ctAscent + ctDescent;
ascent = ctAscent / ctTotalHeight;
unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
const short zero = 0;
auto 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)
CFRelease (attributedStringAtts);
if (fontRef != nullptr)
{
#if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
CTFontManagerUnregisterGraphicsFont (fontRef, nullptr);
#endif
CGFontRelease (fontRef);
}
if (ctFontRef != nullptr)
CFRelease (ctFontRef);
}
float getAscent() const override { return ascent; }
float getDescent() const override { return 1.0f - ascent; }
float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
float getStringWidth (const String& text) override
{
float x = 0;
if (ctFontRef != nullptr && text.isNotEmpty())
{
auto cfText = text.toCFString();
auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
CFRelease (cfText);
auto line = CTLineCreateWithAttributedString (attribString);
auto runArray = CTLineGetGlyphRuns (line);
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
auto length = CTRunGetGlyphCount (run);
const CoreTextTypeLayout::Advances advances (run, length);
for (int j = 0; j < length; ++j)
x += (float) advances.advances[j].width;
}
CFRelease (line);
CFRelease (attribString);
x *= unitsToHeightScaleFactor;
}
return x;
}
void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
{
xOffsets.add (0);
if (ctFontRef != nullptr && text.isNotEmpty())
{
float x = 0;
auto cfText = text.toCFString();
auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
CFRelease (cfText);
auto line = CTLineCreateWithAttributedString (attribString);
auto runArray = CTLineGetGlyphRuns (line);
for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
{
auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
auto length = CTRunGetGlyphCount (run);
const CoreTextTypeLayout::Advances advances (run, length);
const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length);
for (int j = 0; j < length; ++j)
{
x += (float) advances.advances[j].width;
xOffsets.add (x * unitsToHeightScaleFactor);
resultGlyphs.add (glyphs.glyphs[j]);
}
}
CFRelease (line);
CFRelease (attribString);
}
}
bool getOutlineForGlyph (int glyphNumber, Path& path) override
{
jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
if (auto pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform))
{
CGPathApply (pathRef, &path, pathApplier);
CFRelease (pathRef);
if (! pathTransform.isIdentity())
path.applyTransform (pathTransform);
return true;
}
return false;
}
//==============================================================================
CGFontRef fontRef = {};
CTFontRef ctFontRef = {};
float fontHeightToPointsFactor = 1.0f;
CGAffineTransform renderingTransform = CGAffineTransformIdentity;
bool canBeUsedForLayout;
private:
MemoryBlock dataCopy;
CFDictionaryRef attributedStringAtts = {};
float ascent = 0, unitsToHeightScaleFactor = 0;
AffineTransform pathTransform;
static void pathApplier (void* info, const CGPathElement* element)
{
auto& path = *static_cast<Path*> (info);
auto* p = element->points;
switch (element->type)
{
case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y); break;
case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
(float) p[1].x, (float) -p[1].y,
(float) p[2].x, (float) -p[2].y); break;
case kCGPathElementCloseSubpath: path.closeSubPath(); break;
default: jassertfalse; break;
}
}
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
};
CTFontRef getCTFontFromTypeface (const Font& f)
{
if (auto* tf = dynamic_cast<OSXTypeface*> (f.getTypeface()))
return tf->ctFontRef;
return {};
}
StringArray Font::findAllTypefaceNames()
{
StringArray names;
#if JUCE_MAC
// CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
auto fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
{
auto family = String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i));
if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
names.addIfNotAlreadyThere (family);
}
CFRelease (fontFamilyArray);
#else
auto fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
CFRelease (fontCollectionRef);
for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
{
auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
auto cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
CFRelease (cfsFontFamily);
}
CFRelease (fontDescriptorArray);
#endif
names.sort (true);
return names;
}
StringArray Font::findAllTypefaceStyles (const String& family)
{
if (FontStyleHelpers::isPlaceholderFamilyName (family))
return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
StringArray results;
auto cfsFontFamily = family.toCFString();
CFStringRef keys[] = { kCTFontFamilyNameAttribute };
CFTypeRef values[] = { cfsFontFamily };
auto fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
&kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFRelease (cfsFontFamily);
auto ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
CFRelease (fontDescAttributes);
auto fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
CFRelease (ctFontDescRef);
auto fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
CFRelease (fontFamilyArray);
auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
CFRelease (fontCollectionRef);
if (fontDescriptorArray != nullptr)
{
for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
{
auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
auto cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
results.add (String::fromCFString (cfsFontStyle));
CFRelease (cfsFontStyle);
}
CFRelease (fontDescriptorArray);
}
return results;
}
//==============================================================================
Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return new OSXTypeface (font); }
Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return new OSXTypeface (data, size); }
void Typeface::scanFolderForFonts (const File&)
{
jassertfalse; // not implemented on this platform
}
struct DefaultFontNames
{
#if JUCE_IOS
String defaultSans { "Helvetica" },
defaultSerif { "Times New Roman" },
defaultFixed { "Courier New" };
#else
String defaultSans { "Lucida Grande" },
defaultSerif { "Times New Roman" },
defaultFixed { "Menlo" };
#endif
};
Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
{
static DefaultFontNames defaultNames;
auto newFont = font;
auto& faceName = font.getTypefaceName();
if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
if (font.getTypefaceStyle() == getDefaultStyle())
newFont.setTypefaceStyle ("Regular");
return Typeface::createSystemTypefaceFor (newFont);
}
static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
{
auto numCharacterAttributes = text.getNumAttributes();
for (int i = 0; i < numCharacterAttributes; ++i)
{
if (auto tf = dynamic_cast<OSXTypeface*> (text.getAttribute(i).font.getTypeface()))
if (tf->canBeUsedForLayout)
continue;
return false;
}
return true;
}
bool TextLayout::createNativeLayout (const AttributedString& text)
{
if (canAllTypefacesBeUsedInLayout (text))
{
CoreTextTypeLayout::createLayout (*this, text);
return true;
}
return false;
}
} // namespace juce