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.

1212 lines
46KB

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