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.

1239 lines
48KB

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