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.

1223 lines
47KB

  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::ReadingDirection::rightToLeft: return kCTWritingDirectionRightToLeft;
  204. case AttributedString::ReadingDirection::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. CTFrameRef frame = createCTFrame (text, CGRectMake ((CGFloat) area.getX(), flipHeight - (CGFloat) area.getBottom(),
  317. (CGFloat) area.getWidth(), (CGFloat) area.getHeight()));
  318. const int verticalJustification = text.getJustification().getOnlyVerticalFlags();
  319. if (verticalJustification == Justification::verticallyCentred
  320. || verticalJustification == Justification::bottom)
  321. {
  322. float adjust = area.getHeight() - findCTFrameHeight (frame);
  323. if (verticalJustification == Justification::verticallyCentred)
  324. adjust *= 0.5f;
  325. CGContextSaveGState (context);
  326. CGContextTranslateCTM (context, 0, -adjust);
  327. CTFrameDraw (frame, context);
  328. CGContextRestoreGState (context);
  329. }
  330. else
  331. {
  332. CTFrameDraw (frame, context);
  333. }
  334. CFRelease (frame);
  335. }
  336. static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
  337. {
  338. const CGFloat boundsHeight = glyphLayout.getHeight();
  339. CTFrameRef frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
  340. CFArrayRef lines = CTFrameGetLines (frame);
  341. const CFIndex numLines = CFArrayGetCount (lines);
  342. glyphLayout.ensureStorageAllocated ((int) numLines);
  343. for (CFIndex i = 0; i < numLines; ++i)
  344. {
  345. CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
  346. CFArrayRef runs = CTLineGetGlyphRuns (line);
  347. const CFIndex numRuns = CFArrayGetCount (runs);
  348. const CFRange cfrlineStringRange = CTLineGetStringRange (line);
  349. const CFIndex lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1;
  350. const Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
  351. LineInfo lineInfo (frame, line, i);
  352. TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange,
  353. Point<float> ((float) lineInfo.origin.x,
  354. (float) (boundsHeight - lineInfo.origin.y)),
  355. (float) lineInfo.ascent,
  356. (float) lineInfo.descent,
  357. (float) lineInfo.leading,
  358. (int) numRuns);
  359. glyphLayout.addLine (glyphLine);
  360. for (CFIndex j = 0; j < numRuns; ++j)
  361. {
  362. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
  363. const CFIndex numGlyphs = CTRunGetGlyphCount (run);
  364. const CFRange runStringRange = CTRunGetStringRange (run);
  365. TextLayout::Run* const glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
  366. (int) (runStringRange.location + runStringRange.length - 1)),
  367. (int) numGlyphs);
  368. glyphLine->runs.add (glyphRun);
  369. CFDictionaryRef runAttributes = CTRunGetAttributes (run);
  370. CTFontRef ctRunFont;
  371. if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
  372. {
  373. CFStringRef cfsFontName = CTFontCopyPostScriptName (ctRunFont);
  374. CTFontRef ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr);
  375. CFRelease (cfsFontName);
  376. const float fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef);
  377. CFRelease (ctFontRef);
  378. CFStringRef cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute);
  379. CFStringRef cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute);
  380. glyphRun->font = Font (String::fromCFString (cfsFontFamily),
  381. String::fromCFString (cfsFontStyle),
  382. (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
  383. CFRelease (cfsFontStyle);
  384. CFRelease (cfsFontFamily);
  385. }
  386. CGColorRef cgRunColor;
  387. if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
  388. && CGColorGetNumberOfComponents (cgRunColor) == 4)
  389. {
  390. const CGFloat* const components = CGColorGetComponents (cgRunColor);
  391. glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
  392. (float) components[1],
  393. (float) components[2],
  394. (float) components[3]);
  395. }
  396. const Glyphs glyphs (run, (size_t) numGlyphs);
  397. const Advances advances (run, numGlyphs);
  398. const Positions positions (run, (size_t) numGlyphs);
  399. for (CFIndex k = 0; k < numGlyphs; ++k)
  400. glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> ((float) positions.points[k].x,
  401. (float) positions.points[k].y),
  402. (float) advances.advances[k].width));
  403. }
  404. }
  405. CFRelease (frame);
  406. }
  407. }
  408. //==============================================================================
  409. class OSXTypeface : public Typeface
  410. {
  411. public:
  412. OSXTypeface (const Font& font)
  413. : Typeface (font.getTypefaceName(),
  414. font.getTypefaceStyle()),
  415. fontRef (nullptr),
  416. ctFontRef (nullptr),
  417. fontHeightToPointsFactor (1.0f),
  418. renderingTransform (CGAffineTransformIdentity),
  419. isMemoryFont (false),
  420. attributedStringAtts (nullptr),
  421. ascent (0.0f),
  422. unitsToHeightScaleFactor (0.0f)
  423. {
  424. ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
  425. if (ctFontRef != nullptr)
  426. {
  427. fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  428. initialiseMetrics();
  429. }
  430. }
  431. OSXTypeface (const void* data, size_t dataSize)
  432. : Typeface (String(), String()),
  433. fontRef (nullptr),
  434. ctFontRef (nullptr),
  435. fontHeightToPointsFactor (1.0f),
  436. renderingTransform (CGAffineTransformIdentity),
  437. isMemoryFont (true),
  438. attributedStringAtts (nullptr),
  439. ascent (0.0f),
  440. unitsToHeightScaleFactor (0.0f)
  441. {
  442. CFDataRef cfData = CFDataCreate (kCFAllocatorDefault, (const UInt8*) data, (CFIndex) dataSize);
  443. CGDataProviderRef provider = CGDataProviderCreateWithCFData (cfData);
  444. CFRelease (cfData);
  445. fontRef = CGFontCreateWithDataProvider (provider);
  446. CGDataProviderRelease (provider);
  447. if (fontRef != nullptr)
  448. {
  449. ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
  450. if (ctFontRef != nullptr)
  451. {
  452. if (CFStringRef fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
  453. {
  454. name = String::fromCFString (fontName);
  455. CFRelease (fontName);
  456. }
  457. if (CFStringRef fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
  458. {
  459. style = String::fromCFString (fontStyle);
  460. CFRelease (fontStyle);
  461. }
  462. initialiseMetrics();
  463. }
  464. }
  465. }
  466. void initialiseMetrics()
  467. {
  468. const float ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
  469. const float ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
  470. const float ctTotalHeight = ctAscent + ctDescent;
  471. ascent = ctAscent / ctTotalHeight;
  472. unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
  473. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  474. fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
  475. const short zero = 0;
  476. CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
  477. CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
  478. CFTypeRef values[] = { ctFontRef, numberRef };
  479. attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
  480. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  481. CFRelease (numberRef);
  482. }
  483. ~OSXTypeface()
  484. {
  485. if (attributedStringAtts != nullptr)
  486. CFRelease (attributedStringAtts);
  487. if (fontRef != nullptr)
  488. CGFontRelease (fontRef);
  489. if (ctFontRef != nullptr)
  490. CFRelease (ctFontRef);
  491. }
  492. float getAscent() const override { return ascent; }
  493. float getDescent() const override { return 1.0f - ascent; }
  494. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  495. float getStringWidth (const String& text) override
  496. {
  497. float x = 0;
  498. if (ctFontRef != nullptr && text.isNotEmpty())
  499. {
  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. for (int j = 0; j < length; ++j)
  511. x += (float) advances.advances[j].width;
  512. }
  513. CFRelease (line);
  514. CFRelease (attribString);
  515. x *= unitsToHeightScaleFactor;
  516. }
  517. return x;
  518. }
  519. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  520. {
  521. xOffsets.add (0);
  522. if (ctFontRef != nullptr && text.isNotEmpty())
  523. {
  524. float x = 0;
  525. CFStringRef cfText = text.toCFString();
  526. CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  527. CFRelease (cfText);
  528. CTLineRef line = CTLineCreateWithAttributedString (attribString);
  529. CFArrayRef runArray = CTLineGetGlyphRuns (line);
  530. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  531. {
  532. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  533. CFIndex length = CTRunGetGlyphCount (run);
  534. const CoreTextTypeLayout::Advances advances (run, length);
  535. const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length);
  536. for (int j = 0; j < length; ++j)
  537. {
  538. x += (float) advances.advances[j].width;
  539. xOffsets.add (x * unitsToHeightScaleFactor);
  540. resultGlyphs.add (glyphs.glyphs[j]);
  541. }
  542. }
  543. CFRelease (line);
  544. CFRelease (attribString);
  545. }
  546. }
  547. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  548. {
  549. jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
  550. CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform);
  551. if (pathRef == 0)
  552. return false;
  553. CGPathApply (pathRef, &path, pathApplier);
  554. CFRelease (pathRef);
  555. if (! pathTransform.isIdentity())
  556. path.applyTransform (pathTransform);
  557. return true;
  558. }
  559. //==============================================================================
  560. CGFontRef fontRef;
  561. CTFontRef ctFontRef;
  562. float fontHeightToPointsFactor;
  563. CGAffineTransform renderingTransform;
  564. bool isMemoryFont;
  565. private:
  566. CFDictionaryRef attributedStringAtts;
  567. float ascent, unitsToHeightScaleFactor;
  568. AffineTransform pathTransform;
  569. static void pathApplier (void* info, const CGPathElement* const element)
  570. {
  571. Path& path = *static_cast<Path*> (info);
  572. const CGPoint* const p = element->points;
  573. switch (element->type)
  574. {
  575. case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  576. case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  577. case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
  578. (float) p[1].x, (float) -p[1].y); break;
  579. case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  580. (float) p[1].x, (float) -p[1].y,
  581. (float) p[2].x, (float) -p[2].y); break;
  582. case kCGPathElementCloseSubpath: path.closeSubPath(); break;
  583. default: jassertfalse; break;
  584. }
  585. }
  586. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  587. };
  588. CTFontRef getCTFontFromTypeface (const Font& f)
  589. {
  590. if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (f.getTypeface()))
  591. return tf->ctFontRef;
  592. return 0;
  593. }
  594. StringArray Font::findAllTypefaceNames()
  595. {
  596. StringArray names;
  597. #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS
  598. // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
  599. CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
  600. for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
  601. {
  602. const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i)));
  603. if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
  604. names.addIfNotAlreadyThere (family);
  605. }
  606. CFRelease (fontFamilyArray);
  607. #else
  608. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
  609. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  610. CFRelease (fontCollectionRef);
  611. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  612. {
  613. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  614. CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
  615. names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
  616. CFRelease (cfsFontFamily);
  617. }
  618. CFRelease (fontDescriptorArray);
  619. #endif
  620. names.sort (true);
  621. return names;
  622. }
  623. StringArray Font::findAllTypefaceStyles (const String& family)
  624. {
  625. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  626. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  627. StringArray results;
  628. CFStringRef cfsFontFamily = family.toCFString();
  629. CFStringRef keys[] = { kCTFontFamilyNameAttribute };
  630. CFTypeRef values[] = { cfsFontFamily };
  631. CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  632. CFRelease (cfsFontFamily);
  633. CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  634. CFRelease (fontDescAttributes);
  635. CFArrayRef fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
  636. CFRelease (ctFontDescRef);
  637. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
  638. CFRelease (fontFamilyArray);
  639. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  640. CFRelease (fontCollectionRef);
  641. if (fontDescriptorArray != nullptr)
  642. {
  643. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  644. {
  645. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  646. CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
  647. results.add (String::fromCFString (cfsFontStyle));
  648. CFRelease (cfsFontStyle);
  649. }
  650. CFRelease (fontDescriptorArray);
  651. }
  652. return results;
  653. }
  654. #else
  655. //==============================================================================
  656. class OSXTypeface : public Typeface
  657. {
  658. public:
  659. OSXTypeface (const Font& font)
  660. : Typeface (font.getTypefaceName(), font.getTypefaceStyle())
  661. {
  662. JUCE_AUTORELEASEPOOL
  663. {
  664. renderingTransform = CGAffineTransformIdentity;
  665. NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys:
  666. juceStringToNS (name), NSFontFamilyAttribute,
  667. juceStringToNS (style), NSFontFaceAttribute, nil];
  668. NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict];
  669. nsFont = [NSFont fontWithDescriptor: nsFontDesc size: referenceFontSize];
  670. [nsFont retain];
  671. fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]);
  672. const float absAscent = std::abs ((float) CGFontGetAscent (fontRef));
  673. const float totalHeight = absAscent + std::abs ((float) CGFontGetDescent (fontRef));
  674. ascent = absAscent / totalHeight;
  675. unitsToHeightScaleFactor = 1.0f / totalHeight;
  676. const float nsFontAscent = std::abs ([nsFont ascender]);
  677. const float nsFontDescent = std::abs ([nsFont descender]);
  678. fontHeightToPointsFactor = referenceFontSize / (nsFontAscent + nsFontDescent);
  679. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  680. }
  681. }
  682. ~OSXTypeface()
  683. {
  684. #if ! JUCE_IOS
  685. [nsFont release];
  686. #endif
  687. if (fontRef != 0)
  688. CGFontRelease (fontRef);
  689. }
  690. float getAscent() const override { return ascent; }
  691. float getDescent() const override { return 1.0f - ascent; }
  692. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  693. float getStringWidth (const String& text) override
  694. {
  695. if (fontRef == 0 || text.isEmpty())
  696. return 0;
  697. const int length = text.length();
  698. HeapBlock<CGGlyph> glyphs;
  699. createGlyphsForString (text.getCharPointer(), length, glyphs);
  700. float x = 0;
  701. HeapBlock<int> advances (length);
  702. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  703. for (int i = 0; i < length; ++i)
  704. x += advances[i];
  705. return x * unitsToHeightScaleFactor;
  706. }
  707. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  708. {
  709. xOffsets.add (0);
  710. if (fontRef == 0 || text.isEmpty())
  711. return;
  712. const int length = text.length();
  713. HeapBlock<CGGlyph> glyphs;
  714. createGlyphsForString (text.getCharPointer(), length, glyphs);
  715. HeapBlock<int> advances (length);
  716. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  717. {
  718. int x = 0;
  719. for (int i = 0; i < length; ++i)
  720. {
  721. x += advances [i];
  722. xOffsets.add (x * unitsToHeightScaleFactor);
  723. resultGlyphs.add (glyphs[i]);
  724. }
  725. }
  726. }
  727. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  728. {
  729. #if JUCE_IOS
  730. return false;
  731. #else
  732. if (nsFont == nil)
  733. return false;
  734. // we might need to apply a transform to the path, so it mustn't have anything else in it
  735. jassert (path.isEmpty());
  736. JUCE_AUTORELEASEPOOL
  737. {
  738. NSBezierPath* bez = [NSBezierPath bezierPath];
  739. [bez moveToPoint: NSMakePoint (0, 0)];
  740. [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber
  741. inFont: nsFont];
  742. for (int i = 0; i < [bez elementCount]; ++i)
  743. {
  744. NSPoint p[3];
  745. switch ([bez elementAtIndex: i associatedPoints: p])
  746. {
  747. case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  748. case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  749. case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  750. (float) p[1].x, (float) -p[1].y,
  751. (float) p[2].x, (float) -p[2].y); break;
  752. case NSClosePathBezierPathElement: path.closeSubPath(); break;
  753. default: jassertfalse; break;
  754. }
  755. }
  756. path.applyTransform (pathTransform);
  757. }
  758. return true;
  759. #endif
  760. }
  761. //==============================================================================
  762. CGFontRef fontRef;
  763. float fontHeightToPointsFactor;
  764. CGAffineTransform renderingTransform;
  765. private:
  766. float ascent, unitsToHeightScaleFactor;
  767. #if ! JUCE_IOS
  768. NSFont* nsFont;
  769. AffineTransform pathTransform;
  770. #endif
  771. void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock<CGGlyph>& glyphs)
  772. {
  773. if (charToGlyphMapper == nullptr)
  774. charToGlyphMapper = new CharToGlyphMapper (fontRef);
  775. glyphs.malloc (length);
  776. for (int i = 0; i < length; ++i)
  777. glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance());
  778. }
  779. // Reads a CGFontRef's character map table to convert unicode into glyph numbers
  780. class CharToGlyphMapper
  781. {
  782. public:
  783. CharToGlyphMapper (CGFontRef cgFontRef)
  784. : segCount (0), endCode (0), startCode (0), idDelta (0),
  785. idRangeOffset (0), glyphIndexes (0)
  786. {
  787. CFDataRef cmapTable = CGFontCopyTableForTag (cgFontRef, 'cmap');
  788. if (cmapTable != 0)
  789. {
  790. const int numSubtables = getValue16 (cmapTable, 2);
  791. for (int i = 0; i < numSubtables; ++i)
  792. {
  793. if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0
  794. {
  795. const int offset = getValue32 (cmapTable, i * 8 + 8);
  796. if (getValue16 (cmapTable, offset) == 4) // check that it's format 4..
  797. {
  798. const int length = getValue16 (cmapTable, offset + 2);
  799. const int segCountX2 = getValue16 (cmapTable, offset + 6);
  800. segCount = segCountX2 / 2;
  801. const int endCodeOffset = offset + 14;
  802. const int startCodeOffset = endCodeOffset + 2 + segCountX2;
  803. const int idDeltaOffset = startCodeOffset + segCountX2;
  804. const int idRangeOffsetOffset = idDeltaOffset + segCountX2;
  805. const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2;
  806. endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2);
  807. startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2);
  808. idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2);
  809. idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2);
  810. glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset);
  811. }
  812. break;
  813. }
  814. }
  815. CFRelease (cmapTable);
  816. }
  817. }
  818. ~CharToGlyphMapper()
  819. {
  820. if (endCode != 0)
  821. {
  822. CFRelease (endCode);
  823. CFRelease (startCode);
  824. CFRelease (idDelta);
  825. CFRelease (idRangeOffset);
  826. CFRelease (glyphIndexes);
  827. }
  828. }
  829. int getGlyphForCharacter (const juce_wchar c) const
  830. {
  831. for (int i = 0; i < segCount; ++i)
  832. {
  833. if (getValue16 (endCode, i * 2) >= c)
  834. {
  835. const int start = getValue16 (startCode, i * 2);
  836. if (start > c)
  837. break;
  838. const int delta = getValue16 (idDelta, i * 2);
  839. const int rangeOffset = getValue16 (idRangeOffset, i * 2);
  840. if (rangeOffset == 0)
  841. return delta + c;
  842. return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i)));
  843. }
  844. }
  845. // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts!
  846. return jmax (-1, (int) c - 29);
  847. }
  848. private:
  849. int segCount;
  850. CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes;
  851. static uint16 getValue16 (CFDataRef data, const int index)
  852. {
  853. return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index));
  854. }
  855. static uint32 getValue32 (CFDataRef data, const int index)
  856. {
  857. return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index));
  858. }
  859. };
  860. ScopedPointer<CharToGlyphMapper> charToGlyphMapper;
  861. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  862. };
  863. StringArray Font::findAllTypefaceNames()
  864. {
  865. StringArray names;
  866. JUCE_AUTORELEASEPOOL
  867. {
  868. #if JUCE_IOS
  869. for (NSString* name in [UIFont familyNames])
  870. #else
  871. for (NSString* name in [[NSFontManager sharedFontManager] availableFontFamilies])
  872. #endif
  873. names.add (nsStringToJuce (name));
  874. names.sort (true);
  875. }
  876. return names;
  877. }
  878. StringArray Font::findAllTypefaceStyles (const String& family)
  879. {
  880. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  881. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  882. StringArray results;
  883. JUCE_AUTORELEASEPOOL
  884. {
  885. for (NSArray* style in [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)])
  886. results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1]));
  887. }
  888. return results;
  889. }
  890. #endif
  891. //==============================================================================
  892. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  893. {
  894. return new OSXTypeface (font);
  895. }
  896. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t dataSize)
  897. {
  898. #if JUCE_CORETEXT_AVAILABLE
  899. return new OSXTypeface (data, dataSize);
  900. #else
  901. jassertfalse; // You need CoreText enabled to use this feature!
  902. return nullptr;
  903. #endif
  904. }
  905. void Typeface::scanFolderForFonts (const File&)
  906. {
  907. jassertfalse; // not implemented on this platform
  908. }
  909. struct DefaultFontNames
  910. {
  911. DefaultFontNames()
  912. #if JUCE_IOS
  913. : defaultSans ("Helvetica"),
  914. defaultSerif ("Times New Roman"),
  915. defaultFixed ("Courier New"),
  916. #else
  917. : defaultSans ("Lucida Grande"),
  918. defaultSerif ("Times New Roman"),
  919. defaultFixed ("Menlo"),
  920. #endif
  921. defaultFallback ("Arial Unicode MS")
  922. {
  923. }
  924. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  925. };
  926. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  927. {
  928. static DefaultFontNames defaultNames;
  929. Font newFont (font);
  930. const String& faceName = font.getTypefaceName();
  931. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  932. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  933. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  934. if (font.getTypefaceStyle() == getDefaultStyle())
  935. newFont.setTypefaceStyle ("Regular");
  936. return Typeface::createSystemTypefaceFor (newFont);
  937. }
  938. #if JUCE_CORETEXT_AVAILABLE
  939. static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
  940. {
  941. const int numCharacterAttributes = text.getNumAttributes();
  942. for (int i = 0; i < numCharacterAttributes; ++i)
  943. {
  944. Typeface* t = text.getAttribute(i).font.getTypeface();
  945. if (OSXTypeface* tf = dynamic_cast<OSXTypeface*> (t))
  946. {
  947. if (tf->isMemoryFont)
  948. return false;
  949. }
  950. else if (dynamic_cast<CustomTypeface*> (t) != nullptr)
  951. {
  952. return false;
  953. }
  954. }
  955. return true;
  956. }
  957. #endif
  958. bool TextLayout::createNativeLayout (const AttributedString& text)
  959. {
  960. #if JUCE_CORETEXT_AVAILABLE
  961. // Seems to be an unfathomable bug in CoreText which prevents the layout working with
  962. // typefaces that were loaded from memory, so have to fallback if we hit any of those..
  963. if (canAllTypefacesBeUsedInLayout (text))
  964. {
  965. CoreTextTypeLayout::createLayout (*this, text);
  966. return true;
  967. }
  968. #endif
  969. ignoreUnused (text);
  970. return false;
  971. }