The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

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