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.

1312 lines
50KB

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