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.

1343 lines
51KB

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