Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1253 lines
48KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library.
  4. Copyright (c) 2017 - ROLI Ltd.
  5. JUCE is an open source library subject to commercial or open-source
  6. licensing.
  7. By using JUCE, you agree to the terms of both the JUCE 5 End-User License
  8. Agreement and JUCE 5 Privacy Policy (both updated and effective as of the
  9. 27th April 2017).
  10. End User License Agreement: www.juce.com/juce-5-licence
  11. Privacy Policy: www.juce.com/juce-5-privacy-policy
  12. Or: You may also use this code under the terms of the GPL v3 (see
  13. www.gnu.org/licenses).
  14. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  15. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  16. DISCLAIMED.
  17. ==============================================================================
  18. */
  19. namespace juce
  20. {
  21. #ifndef JUCE_CORETEXT_AVAILABLE
  22. #define JUCE_CORETEXT_AVAILABLE 1
  23. #endif
  24. const float referenceFontSize = 1024.0f;
  25. #if JUCE_CORETEXT_AVAILABLE
  26. #if JUCE_MAC && MAC_OS_X_VERSION_MAX_ALLOWED == MAC_OS_X_VERSION_10_5
  27. extern "C"
  28. {
  29. void CTRunGetAdvances (CTRunRef, CFRange, CGSize buffer[]);
  30. const CGSize* CTRunGetAdvancesPtr (CTRunRef);
  31. }
  32. #endif
  33. static CTFontRef getCTFontFromTypeface (const Font& f);
  34. namespace CoreTextTypeLayout
  35. {
  36. static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform)
  37. {
  38. const StringArray availableStyles (Font::findAllTypefaceStyles (font.getTypefaceName()));
  39. const String style (font.getTypefaceStyle());
  40. if (! availableStyles.contains (style))
  41. {
  42. if (font.isItalic()) // Fake-up an italic font if there isn't a real one.
  43. requiredTransform = CGAffineTransformMake (1.0f, 0, 0.25f, 1.0f, 0, 0);
  44. return availableStyles[0];
  45. }
  46. return style;
  47. }
  48. // Workaround for Apple bug in CTFontCreateWithFontDescriptor in Garageband/Logic on 10.6
  49. #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
  50. static CTFontRef getFontWithTrait (CTFontRef ctFontRef, CTFontSymbolicTraits trait)
  51. {
  52. if (CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr, trait, trait))
  53. {
  54. CFRelease (ctFontRef);
  55. return newFont;
  56. }
  57. return ctFontRef;
  58. }
  59. static CTFontRef useStyleFallbackIfNecessary (CTFontRef ctFontRef, CFStringRef cfFontFamily,
  60. const float fontSizePoints, const Font& font)
  61. {
  62. CFStringRef cfActualFontFamily = (CFStringRef) CTFontCopyAttribute (ctFontRef, kCTFontFamilyNameAttribute);
  63. if (CFStringCompare (cfFontFamily, cfActualFontFamily, 0) != kCFCompareEqualTo)
  64. {
  65. CFRelease (ctFontRef);
  66. ctFontRef = CTFontCreateWithName (cfFontFamily, fontSizePoints, nullptr);
  67. if (font.isItalic()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontItalicTrait);
  68. if (font.isBold()) ctFontRef = getFontWithTrait (ctFontRef, kCTFontBoldTrait);
  69. }
  70. CFRelease (cfActualFontFamily);
  71. return ctFontRef;
  72. }
  73. #endif
  74. static float getFontTotalHeight (CTFontRef font)
  75. {
  76. return std::abs ((float) CTFontGetAscent (font)) + std::abs ((float) CTFontGetDescent (font));
  77. }
  78. static float getHeightToPointsFactor (CTFontRef font)
  79. {
  80. return referenceFontSize / getFontTotalHeight (font);
  81. }
  82. static CTFontRef getFontWithPointSize (CTFontRef font, float size)
  83. {
  84. CTFontRef newFont = CTFontCreateCopyWithAttributes (font, size, nullptr, nullptr);
  85. CFRelease (font);
  86. return newFont;
  87. }
  88. static CTFontRef createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired)
  89. {
  90. CFStringRef cfFontFamily = FontStyleHelpers::getConcreteFamilyName (font).toCFString();
  91. CFStringRef cfFontStyle = findBestAvailableStyle (font, transformRequired).toCFString();
  92. CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
  93. CFTypeRef values[] = { cfFontFamily, cfFontStyle };
  94. CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys,
  95. (const void**) &values,
  96. numElementsInArray (keys),
  97. &kCFTypeDictionaryKeyCallBacks,
  98. &kCFTypeDictionaryValueCallBacks);
  99. CFRelease (cfFontStyle);
  100. CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  101. CFRelease (fontDescAttributes);
  102. CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSizePoints, nullptr);
  103. CFRelease (ctFontDescRef);
  104. #if JUCE_MAC && ((! defined (MAC_OS_X_VERSION_10_7)) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7)
  105. ctFontRef = useStyleFallbackIfNecessary (ctFontRef, cfFontFamily, fontSizePoints, font);
  106. #endif
  107. CFRelease (cfFontFamily);
  108. return ctFontRef;
  109. }
  110. //==============================================================================
  111. struct Advances
  112. {
  113. Advances (CTRunRef run, const CFIndex numGlyphs)
  114. : advances (CTRunGetAdvancesPtr (run))
  115. {
  116. if (advances == nullptr)
  117. {
  118. local.malloc ((size_t) numGlyphs);
  119. CTRunGetAdvances (run, CFRangeMake (0, 0), local);
  120. advances = local;
  121. }
  122. }
  123. const CGSize* advances;
  124. HeapBlock<CGSize> local;
  125. };
  126. struct Glyphs
  127. {
  128. Glyphs (CTRunRef run, const size_t numGlyphs)
  129. : glyphs (CTRunGetGlyphsPtr (run))
  130. {
  131. if (glyphs == nullptr)
  132. {
  133. local.malloc (numGlyphs);
  134. CTRunGetGlyphs (run, CFRangeMake (0, 0), local);
  135. glyphs = local;
  136. }
  137. }
  138. const CGGlyph* glyphs;
  139. HeapBlock<CGGlyph> local;
  140. };
  141. struct Positions
  142. {
  143. Positions (CTRunRef run, const size_t numGlyphs)
  144. : points (CTRunGetPositionsPtr (run))
  145. {
  146. if (points == nullptr)
  147. {
  148. local.malloc (numGlyphs);
  149. CTRunGetPositions (run, CFRangeMake (0, 0), local);
  150. points = local;
  151. }
  152. }
  153. const CGPoint* points;
  154. HeapBlock<CGPoint> local;
  155. };
  156. struct LineInfo
  157. {
  158. LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex)
  159. {
  160. CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin);
  161. CTLineGetTypographicBounds (line, &ascent, &descent, &leading);
  162. }
  163. CGPoint origin;
  164. CGFloat ascent, descent, leading;
  165. };
  166. static CTFontRef getOrCreateFont (const Font& f)
  167. {
  168. if (CTFontRef ctf = getCTFontFromTypeface (f))
  169. {
  170. CFRetain (ctf);
  171. return ctf;
  172. }
  173. CGAffineTransform transform;
  174. return createCTFont (f, referenceFontSize, transform);
  175. }
  176. //==============================================================================
  177. static CTTextAlignment getTextAlignment (const AttributedString& text)
  178. {
  179. switch (text.getJustification().getOnlyHorizontalFlags())
  180. {
  181. #if defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
  182. case Justification::right: return kCTTextAlignmentRight;
  183. case Justification::horizontallyCentred: return kCTTextAlignmentCenter;
  184. case Justification::horizontallyJustified: return kCTTextAlignmentJustified;
  185. default: return kCTTextAlignmentLeft;
  186. #else
  187. case Justification::right: return kCTRightTextAlignment;
  188. case Justification::horizontallyCentred: return kCTCenterTextAlignment;
  189. case Justification::horizontallyJustified: return kCTJustifiedTextAlignment;
  190. default: return kCTLeftTextAlignment;
  191. #endif
  192. }
  193. }
  194. static CTLineBreakMode getLineBreakMode (const AttributedString& text)
  195. {
  196. switch (text.getWordWrap())
  197. {
  198. case AttributedString::none: return kCTLineBreakByClipping;
  199. case AttributedString::byChar: return kCTLineBreakByCharWrapping;
  200. default: return kCTLineBreakByWordWrapping;
  201. }
  202. }
  203. static CTWritingDirection getWritingDirection (const AttributedString& text)
  204. {
  205. switch (text.getReadingDirection())
  206. {
  207. case AttributedString::rightToLeft: return kCTWritingDirectionRightToLeft;
  208. case AttributedString::leftToRight: return kCTWritingDirectionLeftToRight;
  209. default: return kCTWritingDirectionNatural;
  210. }
  211. }
  212. //==============================================================================
  213. static CFAttributedStringRef createCFAttributedString (const AttributedString& text)
  214. {
  215. #if JUCE_IOS
  216. CGColorSpaceRef rgbColourSpace = CGColorSpaceCreateDeviceRGB();
  217. #endif
  218. CFMutableAttributedStringRef attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0);
  219. CFStringRef cfText = text.getText().toCFString();
  220. CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText);
  221. CFRelease (cfText);
  222. const int numCharacterAttributes = text.getNumAttributes();
  223. const CFIndex attribStringLen = CFAttributedStringGetLength (attribString);
  224. for (int i = 0; i < numCharacterAttributes; ++i)
  225. {
  226. const AttributedString::Attribute& attr = text.getAttribute (i);
  227. const int rangeStart = attr.range.getStart();
  228. if (rangeStart >= attribStringLen)
  229. continue;
  230. CFRange range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart);
  231. if (CTFontRef ctFontRef = getOrCreateFont (attr.font))
  232. {
  233. ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef));
  234. CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef);
  235. float extraKerning = attr.font.getExtraKerningFactor();
  236. if (extraKerning != 0.0f)
  237. {
  238. extraKerning *= attr.font.getHeight();
  239. CFNumberRef numberRef = CFNumberCreate (0, kCFNumberFloatType, &extraKerning);
  240. CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef);
  241. CFRelease (numberRef);
  242. }
  243. CFRelease (ctFontRef);
  244. }
  245. {
  246. const Colour col (attr.colour);
  247. #if JUCE_IOS
  248. const CGFloat components[] = { col.getFloatRed(),
  249. col.getFloatGreen(),
  250. col.getFloatBlue(),
  251. col.getFloatAlpha() };
  252. CGColorRef colour = CGColorCreate (rgbColourSpace, components);
  253. #else
  254. CGColorRef colour = CGColorCreateGenericRGB (col.getFloatRed(),
  255. col.getFloatGreen(),
  256. col.getFloatBlue(),
  257. col.getFloatAlpha());
  258. #endif
  259. CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour);
  260. CGColorRelease (colour);
  261. }
  262. }
  263. // Paragraph Attributes
  264. CTTextAlignment ctTextAlignment = getTextAlignment (text);
  265. CTLineBreakMode ctLineBreakMode = getLineBreakMode (text);
  266. CTWritingDirection ctWritingDirection = getWritingDirection (text);
  267. const CGFloat ctLineSpacing = text.getLineSpacing();
  268. CTParagraphStyleSetting settings[] =
  269. {
  270. { kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
  271. { kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
  272. { kCTParagraphStyleSpecifierBaseWritingDirection, sizeof (CTWritingDirection), &ctWritingDirection},
  273. #if defined (MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
  274. { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
  275. #else
  276. { kCTParagraphStyleSpecifierLineSpacing, sizeof (CGFloat), &ctLineSpacing }
  277. #endif
  278. };
  279. CTParagraphStyleRef ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings));
  280. CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
  281. kCTParagraphStyleAttributeName, ctParagraphStyleRef);
  282. CFRelease (ctParagraphStyleRef);
  283. #if JUCE_IOS
  284. CGColorSpaceRelease (rgbColourSpace);
  285. #endif
  286. return attribString;
  287. }
  288. static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds)
  289. {
  290. CFAttributedStringRef attribString = createCFAttributedString (text);
  291. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString);
  292. CFRelease (attribString);
  293. CGMutablePathRef path = CGPathCreateMutable();
  294. CGPathAddRect (path, nullptr, bounds);
  295. CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr);
  296. CFRelease (framesetter);
  297. CGPathRelease (path);
  298. return frame;
  299. }
  300. static Range<float> getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex)
  301. {
  302. LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex);
  303. return Range<float> ((float) (info.origin.y - info.descent),
  304. (float) (info.origin.y + info.ascent));
  305. }
  306. static float findCTFrameHeight (CTFrameRef frame)
  307. {
  308. CFArrayRef lines = CTFrameGetLines (frame);
  309. const CFIndex numLines = CFArrayGetCount (lines);
  310. if (numLines == 0)
  311. return 0;
  312. Range<float> range (getLineVerticalRange (frame, lines, 0));
  313. if (numLines > 1)
  314. range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1));
  315. return range.getLength();
  316. }
  317. static void drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
  318. const CGContextRef& context, const float flipHeight)
  319. {
  320. Rectangle<float> ctFrameArea;
  321. const int verticalJustification = text.getJustification().getOnlyVerticalFlags();
  322. // Ugly hack to fix a bug in OS X Sierra where the CTFrame needs to be slightly
  323. // larger than the font height - otherwise the CTFrame will be invalid
  324. if (verticalJustification == Justification::verticallyCentred)
  325. ctFrameArea = area.withSizeKeepingCentre (area.getWidth(), area.getHeight() * 1.1f);
  326. else if (verticalJustification == Justification::bottom)
  327. ctFrameArea = area.withTop (area.getY() - (area.getHeight() * 0.1f));
  328. else
  329. ctFrameArea = area.withHeight (area.getHeight() * 1.1f);
  330. CTFrameRef frame = createCTFrame (text, CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
  331. (CGFloat) ctFrameArea.getWidth(), (CGFloat) ctFrameArea.getHeight()));
  332. if (verticalJustification == Justification::verticallyCentred
  333. || verticalJustification == Justification::bottom)
  334. {
  335. float adjust = ctFrameArea.getHeight() - findCTFrameHeight (frame);
  336. if (verticalJustification == Justification::verticallyCentred)
  337. adjust *= 0.5f;
  338. CGContextSaveGState (context);
  339. CGContextTranslateCTM (context, 0, -adjust);
  340. CTFrameDraw (frame, context);
  341. CGContextRestoreGState (context);
  342. }
  343. else
  344. {
  345. CTFrameDraw (frame, context);
  346. }
  347. CFRelease (frame);
  348. }
  349. static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
  350. {
  351. const CGFloat boundsHeight = glyphLayout.getHeight();
  352. CTFrameRef frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
  353. CFArrayRef lines = CTFrameGetLines (frame);
  354. const CFIndex numLines = CFArrayGetCount (lines);
  355. glyphLayout.ensureStorageAllocated ((int) numLines);
  356. for (CFIndex i = 0; i < numLines; ++i)
  357. {
  358. CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
  359. CFArrayRef runs = CTLineGetGlyphRuns (line);
  360. const CFIndex numRuns = CFArrayGetCount (runs);
  361. const CFRange cfrlineStringRange = CTLineGetStringRange (line);
  362. const CFIndex lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1;
  363. const Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
  364. LineInfo lineInfo (frame, line, i);
  365. TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange,
  366. Point<float> ((float) lineInfo.origin.x,
  367. (float) (boundsHeight - lineInfo.origin.y)),
  368. (float) lineInfo.ascent,
  369. (float) lineInfo.descent,
  370. (float) lineInfo.leading,
  371. (int) numRuns);
  372. glyphLayout.addLine (glyphLine);
  373. for (CFIndex j = 0; j < numRuns; ++j)
  374. {
  375. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
  376. const CFIndex numGlyphs = CTRunGetGlyphCount (run);
  377. const CFRange runStringRange = CTRunGetStringRange (run);
  378. TextLayout::Run* const glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
  379. (int) (runStringRange.location + runStringRange.length - 1)),
  380. (int) numGlyphs);
  381. glyphLine->runs.add (glyphRun);
  382. CFDictionaryRef runAttributes = CTRunGetAttributes (run);
  383. CTFontRef ctRunFont;
  384. if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
  385. {
  386. CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont);
  387. CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr);
  388. CFRelease (cfsFontName);
  389. const float fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef);
  390. CFRelease (ctFontRef);
  391. CFStringRef cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute);
  392. CFStringRef cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute);
  393. glyphRun->font = Font (String::fromCFString (cfsFontFamily),
  394. String::fromCFString (cfsFontStyle),
  395. (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
  396. CFRelease (cfsFontStyle);
  397. CFRelease (cfsFontFamily);
  398. }
  399. CGColorRef cgRunColor;
  400. if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
  401. && CGColorGetNumberOfComponents (cgRunColor) == 4)
  402. {
  403. const CGFloat* const components = CGColorGetComponents (cgRunColor);
  404. glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
  405. (float) components[1],
  406. (float) components[2],
  407. (float) components[3]);
  408. }
  409. const Glyphs glyphs (run, (size_t) numGlyphs);
  410. const Advances advances (run, numGlyphs);
  411. const Positions positions (run, (size_t) numGlyphs);
  412. for (CFIndex k = 0; k < numGlyphs; ++k)
  413. glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> ((float) positions.points[k].x,
  414. (float) positions.points[k].y),
  415. (float) advances.advances[k].width));
  416. }
  417. }
  418. CFRelease (frame);
  419. }
  420. }
  421. //==============================================================================
  422. class OSXTypeface : public Typeface
  423. {
  424. public:
  425. OSXTypeface (const Font& font)
  426. : Typeface (font.getTypefaceName(),
  427. font.getTypefaceStyle()),
  428. fontRef (nullptr),
  429. ctFontRef (nullptr),
  430. fontHeightToPointsFactor (1.0f),
  431. renderingTransform (CGAffineTransformIdentity),
  432. isMemoryFont (false),
  433. attributedStringAtts (nullptr),
  434. ascent (0.0f),
  435. unitsToHeightScaleFactor (0.0f)
  436. {
  437. ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
  438. if (ctFontRef != nullptr)
  439. {
  440. fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  441. initialiseMetrics();
  442. }
  443. }
  444. OSXTypeface (const void* data, size_t dataSize)
  445. : Typeface (String(), String()),
  446. fontRef (nullptr),
  447. ctFontRef (nullptr),
  448. fontHeightToPointsFactor (1.0f),
  449. renderingTransform (CGAffineTransformIdentity),
  450. isMemoryFont (true),
  451. dataCopy (data, dataSize),
  452. attributedStringAtts (nullptr),
  453. ascent (0.0f),
  454. unitsToHeightScaleFactor (0.0f)
  455. {
  456. // We can't use CFDataCreate here as this triggers a false positive in ASAN
  457. // so copy the data manually and use CFDataCreateWithBytesNoCopy
  458. CFDataRef cfData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) dataCopy.getData(),
  459. (CFIndex) dataCopy.getSize(), kCFAllocatorNull);
  460. CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData);
  461. CFRelease (cfData);
  462. #if JUCE_IOS
  463. // Workaround for a an obscure iOS bug which can cause the app to dead-lock
  464. // when loading custom type faces. See: http://www.openradar.me/18778790 and
  465. // http://stackoverflow.com/questions/40242370/app-hangs-in-simulator
  466. [UIFont systemFontOfSize: 12];
  467. #endif
  468. fontRef = CGFontCreateWithDataProvider (provider);
  469. CGDataProviderRelease (provider);
  470. if (fontRef != nullptr)
  471. {
  472. ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
  473. if (ctFontRef != nullptr)
  474. {
  475. if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
  476. {
  477. name = String::fromCFString (fontName);
  478. CFRelease (fontName);
  479. }
  480. if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
  481. {
  482. style = String::fromCFString (fontStyle);
  483. CFRelease (fontStyle);
  484. }
  485. initialiseMetrics();
  486. }
  487. }
  488. }
  489. void initialiseMetrics()
  490. {
  491. const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
  492. const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
  493. const float ctTotalHeight = ctAscent + ctDescent;
  494. ascent = ctAscent / ctTotalHeight;
  495. unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
  496. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  497. fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
  498. const short zero = 0;
  499. CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
  500. CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
  501. CFTypeRef values[] = { ctFontRef, numberRef };
  502. attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
  503. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  504. CFRelease (numberRef);
  505. }
  506. ~OSXTypeface()
  507. {
  508. if (attributedStringAtts != nullptr)
  509. CFRelease (attributedStringAtts);
  510. if (fontRef != nullptr)
  511. CGFontRelease (fontRef);
  512. if (ctFontRef != nullptr)
  513. CFRelease (ctFontRef);
  514. }
  515. float getAscent() const override { return ascent; }
  516. float getDescent() const override { return 1.0f - ascent; }
  517. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  518. float getStringWidth (const String& text) override
  519. {
  520. float x = 0;
  521. if (ctFontRef != nullptr && text.isNotEmpty())
  522. {
  523. CFStringRef cfText = text.toCFString();
  524. CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  525. CFRelease (cfText);
  526. CTLineRef line = CTLineCreateWithAttributedString (attribString);
  527. CFArrayRef runArray = CTLineGetGlyphRuns (line);
  528. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  529. {
  530. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  531. CFIndex length = CTRunGetGlyphCount (run);
  532. const CoreTextTypeLayout::Advances advances (run, length);
  533. for (int j = 0; j < length; ++j)
  534. x += (float) advances.advances[j].width;
  535. }
  536. CFRelease (line);
  537. CFRelease (attribString);
  538. x *= unitsToHeightScaleFactor;
  539. }
  540. return x;
  541. }
  542. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  543. {
  544. xOffsets.add (0);
  545. if (ctFontRef != nullptr && text.isNotEmpty())
  546. {
  547. float x = 0;
  548. CFStringRef cfText = text.toCFString();
  549. CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  550. CFRelease (cfText);
  551. CTLineRef line = CTLineCreateWithAttributedString (attribString);
  552. CFArrayRef runArray = CTLineGetGlyphRuns (line);
  553. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  554. {
  555. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  556. CFIndex length = CTRunGetGlyphCount (run);
  557. const CoreTextTypeLayout::Advances advances (run, length);
  558. const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length);
  559. for (int j = 0; j < length; ++j)
  560. {
  561. x += (float) advances.advances[j].width;
  562. xOffsets.add (x * unitsToHeightScaleFactor);
  563. resultGlyphs.add (glyphs.glyphs[j]);
  564. }
  565. }
  566. CFRelease (line);
  567. CFRelease (attribString);
  568. }
  569. }
  570. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  571. {
  572. jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
  573. CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform);
  574. if (pathRef == 0)
  575. return false;
  576. CGPathApply (pathRef, &path, pathApplier);
  577. CFRelease (pathRef);
  578. if (! pathTransform.isIdentity())
  579. path.applyTransform (pathTransform);
  580. return true;
  581. }
  582. //==============================================================================
  583. CGFontRef fontRef;
  584. CTFontRef ctFontRef;
  585. float fontHeightToPointsFactor;
  586. CGAffineTransform renderingTransform;
  587. bool isMemoryFont;
  588. private:
  589. MemoryBlock dataCopy;
  590. CFDictionaryRef attributedStringAtts;
  591. float ascent, unitsToHeightScaleFactor;
  592. AffineTransform pathTransform;
  593. static void pathApplier (void* info, const CGPathElement* const element)
  594. {
  595. Path& path = *static_cast<Path*> (info);
  596. const CGPoint* const p = element->points;
  597. switch (element->type)
  598. {
  599. case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  600. case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  601. case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
  602. (float) p[1].x, (float) -p[1].y); break;
  603. case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  604. (float) p[1].x, (float) -p[1].y,
  605. (float) p[2].x, (float) -p[2].y); break;
  606. case kCGPathElementCloseSubpath: path.closeSubPath(); break;
  607. default: jassertfalse; break;
  608. }
  609. }
  610. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  611. };
  612. CTFontRef getCTFontFromTypeface (const Font& f)
  613. {
  614. if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (f.getTypeface()))
  615. return tf->ctFontRef;
  616. return 0;
  617. }
  618. StringArray Font::findAllTypefaceNames()
  619. {
  620. StringArray names;
  621. #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS
  622. // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
  623. CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
  624. for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
  625. {
  626. const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i)));
  627. if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
  628. names.addIfNotAlreadyThere (family);
  629. }
  630. CFRelease (fontFamilyArray);
  631. #else
  632. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
  633. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  634. CFRelease (fontCollectionRef);
  635. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  636. {
  637. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  638. CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
  639. names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
  640. CFRelease (cfsFontFamily);
  641. }
  642. CFRelease (fontDescriptorArray);
  643. #endif
  644. names.sort (true);
  645. return names;
  646. }
  647. StringArray Font::findAllTypefaceStyles (const String& family)
  648. {
  649. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  650. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  651. StringArray results;
  652. CFStringRef cfsFontFamily = family.toCFString();
  653. CFStringRef keys[] = { kCTFontFamilyNameAttribute };
  654. CFTypeRef values[] = { cfsFontFamily };
  655. CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  656. CFRelease (cfsFontFamily);
  657. CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  658. CFRelease (fontDescAttributes);
  659. CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
  660. CFRelease (ctFontDescRef);
  661. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
  662. CFRelease (fontFamilyArray);
  663. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  664. CFRelease (fontCollectionRef);
  665. if (fontDescriptorArray != nullptr)
  666. {
  667. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  668. {
  669. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  670. CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
  671. results.add (String::fromCFString (cfsFontStyle));
  672. CFRelease (cfsFontStyle);
  673. }
  674. CFRelease (fontDescriptorArray);
  675. }
  676. return results;
  677. }
  678. #else
  679. //==============================================================================
  680. class OSXTypeface : public Typeface
  681. {
  682. public:
  683. OSXTypeface (const Font& font)
  684. : Typeface (font.getTypefaceName(), font.getTypefaceStyle())
  685. {
  686. JUCE_AUTORELEASEPOOL
  687. {
  688. renderingTransform = CGAffineTransformIdentity;
  689. NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys:
  690. juceStringToNS (name), NSFontFamilyAttribute,
  691. juceStringToNS (style), NSFontFaceAttribute, nil];
  692. NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict];
  693. nsFont = [NSFont fontWithDescriptor: nsFontDesc size: referenceFontSize];
  694. [nsFont retain];
  695. fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]);
  696. const float absAscent = std::abs ((float) CGFontGetAscent (fontRef));
  697. const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef));
  698. ascent = absAscent / totalHeight;
  699. unitsToHeightScaleFactor = 1.0f / totalHeight;
  700. const float nsFontAscent = std::abs ([nsFont ascender]);
  701. const float nsFontDescent = std::abs ([nsFont descender]);
  702. fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent);
  703. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  704. }
  705. }
  706. ~OSXTypeface()
  707. {
  708. #if ! JUCE_IOS
  709. [nsFont release];
  710. #endif
  711. if (fontRef != 0)
  712. CGFontRelease (fontRef);
  713. }
  714. float getAscent() const override { return ascent; }
  715. float getDescent() const override { return 1.0f - ascent; }
  716. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  717. float getStringWidth (const String& text) override
  718. {
  719. if (fontRef == 0 || text.isEmpty())
  720. return 0;
  721. const int length = text.length();
  722. HeapBlock<CGGlyph> glyphs;
  723. createGlyphsForString (text.getCharPointer(), length, glyphs);
  724. float x = 0;
  725. HeapBlock<int> advances (length);
  726. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  727. for (int i = 0; i < length; ++i)
  728. x += advances[i];
  729. return x * unitsToHeightScaleFactor;
  730. }
  731. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  732. {
  733. xOffsets.add (0);
  734. if (fontRef == 0 || text.isEmpty())
  735. return;
  736. const int length = text.length();
  737. HeapBlock<CGGlyph> glyphs;
  738. createGlyphsForString (text.getCharPointer(), length, glyphs);
  739. HeapBlock<int> advances (length);
  740. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  741. {
  742. int x = 0;
  743. for (int i = 0; i < length; ++i)
  744. {
  745. x += advances [i];
  746. xOffsets.add (x * unitsToHeightScaleFactor);
  747. resultGlyphs.add (glyphs[i]);
  748. }
  749. }
  750. }
  751. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  752. {
  753. #if JUCE_IOS
  754. return false;
  755. #else
  756. if (nsFont == nil)
  757. return false;
  758. // we might need to apply a transform to the path, so it mustn't have anything else in it
  759. jassert (path.isEmpty());
  760. JUCE_AUTORELEASEPOOL
  761. {
  762. NSBezierPath* bez = [NSBezierPath bezierPath];
  763. [bez moveToPoint: NSMakePoint (0, 0)];
  764. [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber
  765. inFont: nsFont];
  766. for (int i = 0; i < [bez elementCount]; ++i)
  767. {
  768. NSPoint p[3];
  769. switch ([bez elementAtIndex: i associatedPoints: p])
  770. {
  771. case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  772. case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  773. case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  774. (float) p[1].x, (float) -p[1].y,
  775. (float) p[2].x, (float) -p[2].y); break;
  776. case NSClosePathBezierPathElement: path.closeSubPath(); break;
  777. default: jassertfalse; break;
  778. }
  779. }
  780. path.applyTransform (pathTransform);
  781. }
  782. return true;
  783. #endif
  784. }
  785. //==============================================================================
  786. CGFontRef fontRef;
  787. float fontHeightToPointsFactor;
  788. CGAffineTransform renderingTransform;
  789. private:
  790. float ascent, unitsToHeightScaleFactor;
  791. #if ! JUCE_IOS
  792. NSFont* nsFont;
  793. AffineTransform pathTransform;
  794. #endif
  795. void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock<CGGlyph>& glyphs)
  796. {
  797. if (charToGlyphMapper == nullptr)
  798. charToGlyphMapper = new CharToGlyphMapper (fontRef);
  799. glyphs.malloc (length);
  800. for (int i = 0; i < length; ++i)
  801. glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance());
  802. }
  803. // Reads a CGFontRef's character map table to convert unicode into glyph numbers
  804. class CharToGlyphMapper
  805. {
  806. public:
  807. CharToGlyphMapper (CGFontRef cgFontRef)
  808. : segCount (0), endCode (0), startCode (0), idDelta (0),
  809. idRangeOffset (0), glyphIndexes (0)
  810. {
  811. CFDataRef cmapTable = CGFontCopyTableForTag (cgFontRef, 'cmap');
  812. if (cmapTable != 0)
  813. {
  814. const int numSubtables = getValue16 (cmapTable, 2);
  815. for (int i = 0; i < numSubtables; ++i)
  816. {
  817. if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0
  818. {
  819. const int offset = getValue32 (cmapTable, i * 8 + 8);
  820. if (getValue16 (cmapTable, offset) == 4) // check that it's format 4..
  821. {
  822. const int length = getValue16 (cmapTable, offset + 2);
  823. const int segCountX2 = getValue16 (cmapTable, offset + 6);
  824. segCount = segCountX2 / 2;
  825. const int endCodeOffset = offset + 14;
  826. const int startCodeOffset = endCodeOffset + 2 + segCountX2;
  827. const int idDeltaOffset = startCodeOffset + segCountX2;
  828. const int idRangeOffsetOffset = idDeltaOffset + segCountX2;
  829. const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2;
  830. endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2);
  831. startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2);
  832. idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2);
  833. idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2);
  834. glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset);
  835. }
  836. break;
  837. }
  838. }
  839. CFRelease (cmapTable);
  840. }
  841. }
  842. ~CharToGlyphMapper()
  843. {
  844. if (endCode != 0)
  845. {
  846. CFRelease (endCode);
  847. CFRelease (startCode);
  848. CFRelease (idDelta);
  849. CFRelease (idRangeOffset);
  850. CFRelease (glyphIndexes);
  851. }
  852. }
  853. int getGlyphForCharacter (const juce_wchar c) const
  854. {
  855. for (int i = 0; i < segCount; ++i)
  856. {
  857. if (getValue16 (endCode, i * 2) >= c)
  858. {
  859. const int start = getValue16 (startCode, i * 2);
  860. if (start > c)
  861. break;
  862. const int delta = getValue16 (idDelta, i * 2);
  863. const int rangeOffset = getValue16 (idRangeOffset, i * 2);
  864. if (rangeOffset == 0)
  865. return delta + c;
  866. return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i)));
  867. }
  868. }
  869. // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts!
  870. return jmax (-1, (int) c - 29);
  871. }
  872. private:
  873. int segCount;
  874. CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes;
  875. static uint16 getValue16 (CFDataRef data, const int index)
  876. {
  877. return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index));
  878. }
  879. static uint32 getValue32 (CFDataRef data, const int index)
  880. {
  881. return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index));
  882. }
  883. };
  884. ScopedPointer<CharToGlyphMapper> charToGlyphMapper;
  885. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  886. };
  887. StringArray Font::findAllTypefaceNames()
  888. {
  889. StringArray names;
  890. JUCE_AUTORELEASEPOOL
  891. {
  892. #if JUCE_IOS
  893. for (NSString* name in [UIFont familyNames])
  894. #else
  895. for (NSString* name in [[NSFontManager sharedFontManager] availableFontFamilies])
  896. #endif
  897. names.add (nsStringToJuce (name));
  898. names.sort (true);
  899. }
  900. return names;
  901. }
  902. StringArray Font::findAllTypefaceStyles (const String& family)
  903. {
  904. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  905. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  906. StringArray results;
  907. JUCE_AUTORELEASEPOOL
  908. {
  909. for (NSArray* style in [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)])
  910. results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1]));
  911. }
  912. return results;
  913. }
  914. #endif
  915. //==============================================================================
  916. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  917. {
  918. return new OSXTypeface (font);
  919. }
  920. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
  921. {
  922. #if JUCE_CORETEXT_AVAILABLE
  923. return new OSXTypeface (data, dataSize);
  924. #else
  925. jassertfalse; // You need CoreText enabled to use this feature!
  926. return nullptr;
  927. #endif
  928. }
  929. void Typeface::scanFolderForFonts (const File&)
  930. {
  931. jassertfalse; // not implemented on this platform
  932. }
  933. struct DefaultFontNames
  934. {
  935. DefaultFontNames()
  936. #if JUCE_IOS
  937. : defaultSans ("Helvetica"),
  938. defaultSerif ("Times New Roman"),
  939. defaultFixed ("Courier New"),
  940. #else
  941. : defaultSans ("Lucida Grande"),
  942. defaultSerif ("Times New Roman"),
  943. defaultFixed ("Menlo"),
  944. #endif
  945. defaultFallback ("Arial Unicode MS")
  946. {
  947. }
  948. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  949. };
  950. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  951. {
  952. static DefaultFontNames defaultNames;
  953. Font newFont (font);
  954. const String& faceName = font.getTypefaceName();
  955. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  956. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  957. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  958. if (font.getTypefaceStyle() == getDefaultStyle())
  959. newFont.setTypefaceStyle ("Regular");
  960. return Typeface::createSystemTypefaceFor (newFont);
  961. }
  962. #if JUCE_CORETEXT_AVAILABLE
  963. static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
  964. {
  965. const int numCharacterAttributes = text.getNumAttributes();
  966. for (int i = 0; i < numCharacterAttributes; ++i)
  967. {
  968. Typeface* t = text.getAttribute(i).font.getTypeface();
  969. if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (t))
  970. {
  971. if (tf->isMemoryFont)
  972. return false;
  973. }
  974. else if (dynamic_cast<CustomTypeface*> (t) != nullptr)
  975. {
  976. return false;
  977. }
  978. }
  979. return true;
  980. }
  981. #endif
  982. bool TextLayout::createNativeLayout (const AttributedString& text)
  983. {
  984. #if JUCE_CORETEXT_AVAILABLE
  985. // Seems to be an unfathomable bug in CoreText which prevents the layout working with
  986. // typefaces that were loaded from memory, so have to fallback if we hit any of those..
  987. if (canAllTypefacesBeUsedInLayout (text))
  988. {
  989. CoreTextTypeLayout::createLayout (*this, text);
  990. return true;
  991. }
  992. #endif
  993. ignoreUnused (text);
  994. return false;
  995. }
  996. } // namespace juce