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.

893 lines
35KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE 6 technical preview.
  4. Copyright (c) 2020 - Raw Material Software Limited
  5. You may use this code under the terms of the GPL v3
  6. (see www.gnu.org/licenses).
  7. For this technical preview, this file is not subject to commercial licensing.
  8. JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
  9. EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
  10. DISCLAIMED.
  11. ==============================================================================
  12. */
  13. namespace juce
  14. {
  15. static constexpr float referenceFontSize = 1024.0f;
  16. static CTFontRef getCTFontFromTypeface (const Font&);
  17. namespace CoreTextTypeLayout
  18. {
  19. static String findBestAvailableStyle (const Font& font, CGAffineTransform& requiredTransform)
  20. {
  21. auto availableStyles = Font::findAllTypefaceStyles (font.getTypefaceName());
  22. auto style = font.getTypefaceStyle();
  23. if (! availableStyles.contains (style))
  24. {
  25. if (font.isItalic()) // Fake-up an italic font if there isn't a real one.
  26. requiredTransform = CGAffineTransformMake (1.0f, 0, 0.1f, 1.0f, 0, 0);
  27. return availableStyles[0];
  28. }
  29. return style;
  30. }
  31. static float getFontTotalHeight (CTFontRef font)
  32. {
  33. return std::abs ((float) CTFontGetAscent (font))
  34. + std::abs ((float) CTFontGetDescent (font));
  35. }
  36. static float getHeightToPointsFactor (CTFontRef font)
  37. {
  38. return referenceFontSize / getFontTotalHeight (font);
  39. }
  40. static CTFontRef getFontWithPointSize (CTFontRef font, float size)
  41. {
  42. auto newFont = CTFontCreateCopyWithAttributes (font, size, nullptr, nullptr);
  43. CFRelease (font);
  44. return newFont;
  45. }
  46. static CTFontRef createCTFont (const Font& font, const float fontSizePoints, CGAffineTransform& transformRequired)
  47. {
  48. auto cfFontFamily = FontStyleHelpers::getConcreteFamilyName (font).toCFString();
  49. auto cfFontStyle = findBestAvailableStyle (font, transformRequired).toCFString();
  50. CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
  51. CFTypeRef values[] = { cfFontFamily, cfFontStyle };
  52. auto fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys,
  53. (const void**) &values,
  54. numElementsInArray (keys),
  55. &kCFTypeDictionaryKeyCallBacks,
  56. &kCFTypeDictionaryValueCallBacks);
  57. CFRelease (cfFontStyle);
  58. auto ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  59. CFRelease (fontDescAttributes);
  60. auto ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSizePoints, nullptr);
  61. CFRelease (ctFontDescRef);
  62. CFRelease (cfFontFamily);
  63. return ctFontRef;
  64. }
  65. //==============================================================================
  66. struct Advances
  67. {
  68. Advances (CTRunRef run, CFIndex numGlyphs) : advances (CTRunGetAdvancesPtr (run))
  69. {
  70. if (advances == nullptr)
  71. {
  72. local.malloc (numGlyphs);
  73. CTRunGetAdvances (run, CFRangeMake (0, 0), local);
  74. advances = local;
  75. }
  76. }
  77. const CGSize* advances;
  78. HeapBlock<CGSize> local;
  79. };
  80. struct Glyphs
  81. {
  82. Glyphs (CTRunRef run, size_t numGlyphs) : glyphs (CTRunGetGlyphsPtr (run))
  83. {
  84. if (glyphs == nullptr)
  85. {
  86. local.malloc (numGlyphs);
  87. CTRunGetGlyphs (run, CFRangeMake (0, 0), local);
  88. glyphs = local;
  89. }
  90. }
  91. const CGGlyph* glyphs;
  92. HeapBlock<CGGlyph> local;
  93. };
  94. struct Positions
  95. {
  96. Positions (CTRunRef run, size_t numGlyphs) : points (CTRunGetPositionsPtr (run))
  97. {
  98. if (points == nullptr)
  99. {
  100. local.malloc (numGlyphs);
  101. CTRunGetPositions (run, CFRangeMake (0, 0), local);
  102. points = local;
  103. }
  104. }
  105. const CGPoint* points;
  106. HeapBlock<CGPoint> local;
  107. };
  108. struct LineInfo
  109. {
  110. LineInfo (CTFrameRef frame, CTLineRef line, CFIndex lineIndex)
  111. {
  112. CTFrameGetLineOrigins (frame, CFRangeMake (lineIndex, 1), &origin);
  113. CTLineGetTypographicBounds (line, &ascent, &descent, &leading);
  114. }
  115. CGPoint origin;
  116. CGFloat ascent, descent, leading;
  117. };
  118. static CTFontRef getOrCreateFont (const Font& f)
  119. {
  120. if (auto ctf = getCTFontFromTypeface (f))
  121. {
  122. CFRetain (ctf);
  123. return ctf;
  124. }
  125. CGAffineTransform transform;
  126. return createCTFont (f, referenceFontSize, transform);
  127. }
  128. //==============================================================================
  129. static CTTextAlignment getTextAlignment (const AttributedString& text)
  130. {
  131. switch (text.getJustification().getOnlyHorizontalFlags())
  132. {
  133. #if defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_8
  134. case Justification::right: return kCTTextAlignmentRight;
  135. case Justification::horizontallyCentred: return kCTTextAlignmentCenter;
  136. case Justification::horizontallyJustified: return kCTTextAlignmentJustified;
  137. default: return kCTTextAlignmentLeft;
  138. #else
  139. case Justification::right: return kCTRightTextAlignment;
  140. case Justification::horizontallyCentred: return kCTCenterTextAlignment;
  141. case Justification::horizontallyJustified: return kCTJustifiedTextAlignment;
  142. default: return kCTLeftTextAlignment;
  143. #endif
  144. }
  145. }
  146. static CTLineBreakMode getLineBreakMode (const AttributedString& text)
  147. {
  148. switch (text.getWordWrap())
  149. {
  150. case AttributedString::none: return kCTLineBreakByClipping;
  151. case AttributedString::byChar: return kCTLineBreakByCharWrapping;
  152. case AttributedString::byWord:
  153. default: return kCTLineBreakByWordWrapping;
  154. }
  155. }
  156. static CTWritingDirection getWritingDirection (const AttributedString& text)
  157. {
  158. switch (text.getReadingDirection())
  159. {
  160. case AttributedString::rightToLeft: return kCTWritingDirectionRightToLeft;
  161. case AttributedString::leftToRight: return kCTWritingDirectionLeftToRight;
  162. case AttributedString::natural:
  163. default: return kCTWritingDirectionNatural;
  164. }
  165. }
  166. //==============================================================================
  167. static CFAttributedStringRef createCFAttributedString (const AttributedString& text)
  168. {
  169. #if JUCE_IOS
  170. auto rgbColourSpace = CGColorSpaceCreateDeviceRGB();
  171. #endif
  172. auto attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0);
  173. auto cfText = text.getText().toCFString();
  174. CFAttributedStringReplaceString (attribString, CFRangeMake (0, 0), cfText);
  175. CFRelease (cfText);
  176. auto numCharacterAttributes = text.getNumAttributes();
  177. auto attribStringLen = CFAttributedStringGetLength (attribString);
  178. for (int i = 0; i < numCharacterAttributes; ++i)
  179. {
  180. auto& attr = text.getAttribute (i);
  181. auto rangeStart = attr.range.getStart();
  182. if (rangeStart >= attribStringLen)
  183. continue;
  184. auto range = CFRangeMake (rangeStart, jmin (attr.range.getEnd(), (int) attribStringLen) - rangeStart);
  185. if (auto ctFontRef = getOrCreateFont (attr.font))
  186. {
  187. ctFontRef = getFontWithPointSize (ctFontRef, attr.font.getHeight() * getHeightToPointsFactor (ctFontRef));
  188. CFAttributedStringSetAttribute (attribString, range, kCTFontAttributeName, ctFontRef);
  189. auto extraKerning = attr.font.getExtraKerningFactor();
  190. if (extraKerning != 0)
  191. {
  192. extraKerning *= attr.font.getHeight();
  193. auto numberRef = CFNumberCreate (nullptr, kCFNumberFloatType, &extraKerning);
  194. CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef);
  195. CFRelease (numberRef);
  196. }
  197. CFRelease (ctFontRef);
  198. }
  199. {
  200. auto col = attr.colour;
  201. #if JUCE_IOS
  202. const CGFloat components[] = { col.getFloatRed(),
  203. col.getFloatGreen(),
  204. col.getFloatBlue(),
  205. col.getFloatAlpha() };
  206. auto colour = CGColorCreate (rgbColourSpace, components);
  207. #else
  208. auto colour = CGColorCreateGenericRGB (col.getFloatRed(),
  209. col.getFloatGreen(),
  210. col.getFloatBlue(),
  211. col.getFloatAlpha());
  212. #endif
  213. CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour);
  214. CGColorRelease (colour);
  215. }
  216. }
  217. // Paragraph Attributes
  218. auto ctTextAlignment = getTextAlignment (text);
  219. auto ctLineBreakMode = getLineBreakMode (text);
  220. auto ctWritingDirection = getWritingDirection (text);
  221. CGFloat ctLineSpacing = text.getLineSpacing();
  222. CTParagraphStyleSetting settings[] =
  223. {
  224. { kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
  225. { kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
  226. { kCTParagraphStyleSpecifierBaseWritingDirection, sizeof (CTWritingDirection), &ctWritingDirection},
  227. { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
  228. };
  229. auto ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings));
  230. CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
  231. kCTParagraphStyleAttributeName, ctParagraphStyleRef);
  232. CFRelease (ctParagraphStyleRef);
  233. #if JUCE_IOS
  234. CGColorSpaceRelease (rgbColourSpace);
  235. #endif
  236. return attribString;
  237. }
  238. static CTFramesetterRef createCTFramesetter (const AttributedString& text)
  239. {
  240. auto attribString = createCFAttributedString (text);
  241. auto framesetter = CTFramesetterCreateWithAttributedString (attribString);
  242. CFRelease (attribString);
  243. return framesetter;
  244. }
  245. static CTFrameRef createCTFrame (CTFramesetterRef framesetter, CGRect bounds)
  246. {
  247. auto path = CGPathCreateMutable();
  248. CGPathAddRect (path, nullptr, bounds);
  249. auto frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr);
  250. CGPathRelease (path);
  251. return frame;
  252. }
  253. static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds)
  254. {
  255. auto framesetter = createCTFramesetter (text);
  256. auto frame = createCTFrame (framesetter, bounds);
  257. CFRelease (framesetter);
  258. return frame;
  259. }
  260. static Range<float> getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex)
  261. {
  262. LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex);
  263. return { (float) (info.origin.y - info.descent),
  264. (float) (info.origin.y + info.ascent) };
  265. }
  266. static float findCTFrameHeight (CTFrameRef frame)
  267. {
  268. auto lines = CTFrameGetLines (frame);
  269. auto numLines = CFArrayGetCount (lines);
  270. if (numLines == 0)
  271. return 0;
  272. auto range = getLineVerticalRange (frame, lines, 0);
  273. if (numLines > 1)
  274. range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1));
  275. return range.getLength();
  276. }
  277. static void drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
  278. const CGContextRef& context, float flipHeight)
  279. {
  280. auto framesetter = createCTFramesetter (text);
  281. // Ugly hack to fix a bug in OS X Sierra where the CTFrame needs to be slightly
  282. // larger than the font height - otherwise the CTFrame will be invalid
  283. CFRange fitrange;
  284. auto suggestedSingleLineFrameSize =
  285. CTFramesetterSuggestFrameSizeWithConstraints (framesetter, CFRangeMake (0, 0), nullptr,
  286. CGSizeMake (CGFLOAT_MAX, CGFLOAT_MAX), &fitrange);
  287. auto minCTFrameHeight = (float) suggestedSingleLineFrameSize.height;
  288. auto verticalJustification = text.getJustification().getOnlyVerticalFlags();
  289. auto ctFrameArea = [area, minCTFrameHeight, verticalJustification]
  290. {
  291. if (minCTFrameHeight < area.getHeight())
  292. return area;
  293. if (verticalJustification == Justification::verticallyCentred)
  294. return area.withSizeKeepingCentre (area.getWidth(), minCTFrameHeight);
  295. auto frameArea = area.withHeight (minCTFrameHeight);
  296. if (verticalJustification == Justification::bottom)
  297. return frameArea.withBottomY (area.getBottom());
  298. return frameArea;
  299. }();
  300. auto frame = createCTFrame (framesetter, CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
  301. (CGFloat) ctFrameArea.getWidth(), (CGFloat) ctFrameArea.getHeight()));
  302. CFRelease (framesetter);
  303. auto textMatrix = CGContextGetTextMatrix (context);
  304. CGContextSaveGState (context);
  305. if (verticalJustification == Justification::verticallyCentred
  306. || verticalJustification == Justification::bottom)
  307. {
  308. auto adjust = ctFrameArea.getHeight() - findCTFrameHeight (frame);
  309. if (verticalJustification == Justification::verticallyCentred)
  310. adjust *= 0.5f;
  311. CGContextTranslateCTM (context, 0, -adjust);
  312. }
  313. CTFrameDraw (frame, context);
  314. CGContextRestoreGState (context);
  315. CGContextSetTextMatrix (context, textMatrix);
  316. CFRelease (frame);
  317. }
  318. static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
  319. {
  320. auto boundsHeight = glyphLayout.getHeight();
  321. auto frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
  322. auto lines = CTFrameGetLines (frame);
  323. auto numLines = CFArrayGetCount (lines);
  324. glyphLayout.ensureStorageAllocated ((int) numLines);
  325. for (CFIndex i = 0; i < numLines; ++i)
  326. {
  327. auto line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
  328. auto runs = CTLineGetGlyphRuns (line);
  329. auto numRuns = CFArrayGetCount (runs);
  330. auto cfrlineStringRange = CTLineGetStringRange (line);
  331. auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length;
  332. Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
  333. LineInfo lineInfo (frame, line, i);
  334. auto glyphLine = std::make_unique<TextLayout::Line> (lineStringRange,
  335. Point<float> ((float) lineInfo.origin.x,
  336. (float) (boundsHeight - lineInfo.origin.y)),
  337. (float) lineInfo.ascent,
  338. (float) lineInfo.descent,
  339. (float) lineInfo.leading,
  340. (int) numRuns);
  341. for (CFIndex j = 0; j < numRuns; ++j)
  342. {
  343. auto run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
  344. auto numGlyphs = CTRunGetGlyphCount (run);
  345. auto runStringRange = CTRunGetStringRange (run);
  346. auto glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
  347. (int) (runStringRange.location + runStringRange.length - 1)),
  348. (int) numGlyphs);
  349. glyphLine->runs.add (glyphRun);
  350. CFDictionaryRef runAttributes = CTRunGetAttributes (run);
  351. CTFontRef ctRunFont;
  352. if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
  353. {
  354. auto cfsFontName = CTFontCopyPostScriptName (ctRunFont);
  355. auto ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr);
  356. CFRelease (cfsFontName);
  357. auto fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef);
  358. CFRelease (ctFontRef);
  359. auto cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute);
  360. auto cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute);
  361. glyphRun->font = Font (String::fromCFString (cfsFontFamily),
  362. String::fromCFString (cfsFontStyle),
  363. (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
  364. CFRelease (cfsFontStyle);
  365. CFRelease (cfsFontFamily);
  366. }
  367. CGColorRef cgRunColor;
  368. if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
  369. && CGColorGetNumberOfComponents (cgRunColor) == 4)
  370. {
  371. auto* components = CGColorGetComponents (cgRunColor);
  372. glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
  373. (float) components[1],
  374. (float) components[2],
  375. (float) components[3]);
  376. }
  377. const Glyphs glyphs (run, (size_t) numGlyphs);
  378. const Advances advances (run, numGlyphs);
  379. const Positions positions (run, (size_t) numGlyphs);
  380. for (CFIndex k = 0; k < numGlyphs; ++k)
  381. glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> ((float) positions.points[k].x,
  382. (float) positions.points[k].y),
  383. (float) advances.advances[k].width));
  384. }
  385. glyphLayout.addLine (std::move (glyphLine));
  386. }
  387. CFRelease (frame);
  388. }
  389. }
  390. //==============================================================================
  391. class OSXTypeface : public Typeface
  392. {
  393. public:
  394. OSXTypeface (const Font& font)
  395. : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), canBeUsedForLayout (true)
  396. {
  397. ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
  398. if (ctFontRef != nullptr)
  399. {
  400. fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  401. initialiseMetrics();
  402. }
  403. }
  404. OSXTypeface (const void* data, size_t dataSize)
  405. : Typeface ({}, {}), canBeUsedForLayout (false), dataCopy (data, dataSize)
  406. {
  407. // We can't use CFDataCreate here as this triggers a false positive in ASAN
  408. // so copy the data manually and use CFDataCreateWithBytesNoCopy
  409. auto cfData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) dataCopy.getData(),
  410. (CFIndex) dataCopy.getSize(), kCFAllocatorNull);
  411. auto provider = CGDataProviderCreateWithCFData (cfData);
  412. CFRelease (cfData);
  413. #if JUCE_IOS
  414. // Workaround for a an obscure iOS bug which can cause the app to dead-lock
  415. // when loading custom type faces. See: http://www.openradar.me/18778790 and
  416. // http://stackoverflow.com/questions/40242370/app-hangs-in-simulator
  417. [UIFont systemFontOfSize: 12];
  418. #endif
  419. fontRef = CGFontCreateWithDataProvider (provider);
  420. CGDataProviderRelease (provider);
  421. if (fontRef != nullptr)
  422. {
  423. #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
  424. if (SystemStats::getOperatingSystemType() >= SystemStats::OperatingSystemType::MacOSX_10_11)
  425. canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
  426. #endif
  427. ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
  428. if (ctFontRef != nullptr)
  429. {
  430. if (auto fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
  431. {
  432. name = String::fromCFString (fontName);
  433. CFRelease (fontName);
  434. }
  435. if (auto fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
  436. {
  437. style = String::fromCFString (fontStyle);
  438. CFRelease (fontStyle);
  439. }
  440. initialiseMetrics();
  441. }
  442. }
  443. }
  444. void initialiseMetrics()
  445. {
  446. auto ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
  447. auto ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
  448. auto ctTotalHeight = ctAscent + ctDescent;
  449. ascent = ctAscent / ctTotalHeight;
  450. unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
  451. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  452. fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
  453. const short zero = 0;
  454. auto numberRef = CFNumberCreate (nullptr, kCFNumberShortType, &zero);
  455. CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
  456. CFTypeRef values[] = { ctFontRef, numberRef };
  457. attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys,
  458. (const void**) &values, numElementsInArray (keys),
  459. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  460. CFRelease (numberRef);
  461. }
  462. ~OSXTypeface() override
  463. {
  464. if (attributedStringAtts != nullptr)
  465. CFRelease (attributedStringAtts);
  466. if (fontRef != nullptr)
  467. {
  468. #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
  469. if (dataCopy.getSize() != 0)
  470. CTFontManagerUnregisterGraphicsFont (fontRef, nullptr);
  471. #endif
  472. CGFontRelease (fontRef);
  473. }
  474. if (ctFontRef != nullptr)
  475. CFRelease (ctFontRef);
  476. }
  477. float getAscent() const override { return ascent; }
  478. float getDescent() const override { return 1.0f - ascent; }
  479. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  480. float getStringWidth (const String& text) override
  481. {
  482. float x = 0;
  483. if (ctFontRef != nullptr && text.isNotEmpty())
  484. {
  485. auto cfText = text.toCFString();
  486. auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  487. CFRelease (cfText);
  488. auto line = CTLineCreateWithAttributedString (attribString);
  489. auto runArray = CTLineGetGlyphRuns (line);
  490. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  491. {
  492. auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  493. auto length = CTRunGetGlyphCount (run);
  494. const CoreTextTypeLayout::Advances advances (run, length);
  495. for (int j = 0; j < length; ++j)
  496. x += (float) advances.advances[j].width;
  497. }
  498. CFRelease (line);
  499. CFRelease (attribString);
  500. x *= unitsToHeightScaleFactor;
  501. }
  502. return x;
  503. }
  504. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  505. {
  506. xOffsets.add (0);
  507. if (ctFontRef != nullptr && text.isNotEmpty())
  508. {
  509. float x = 0;
  510. auto cfText = text.toCFString();
  511. auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  512. CFRelease (cfText);
  513. auto line = CTLineCreateWithAttributedString (attribString);
  514. auto runArray = CTLineGetGlyphRuns (line);
  515. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  516. {
  517. auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  518. auto length = CTRunGetGlyphCount (run);
  519. const CoreTextTypeLayout::Advances advances (run, length);
  520. const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length);
  521. for (int j = 0; j < length; ++j)
  522. {
  523. x += (float) advances.advances[j].width;
  524. xOffsets.add (x * unitsToHeightScaleFactor);
  525. resultGlyphs.add (glyphs.glyphs[j]);
  526. }
  527. }
  528. CFRelease (line);
  529. CFRelease (attribString);
  530. }
  531. }
  532. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  533. {
  534. jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
  535. if (auto pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform))
  536. {
  537. CGPathApply (pathRef, &path, pathApplier);
  538. CFRelease (pathRef);
  539. if (! pathTransform.isIdentity())
  540. path.applyTransform (pathTransform);
  541. return true;
  542. }
  543. return false;
  544. }
  545. //==============================================================================
  546. CGFontRef fontRef = {};
  547. CTFontRef ctFontRef = {};
  548. float fontHeightToPointsFactor = 1.0f;
  549. CGAffineTransform renderingTransform = CGAffineTransformIdentity;
  550. bool canBeUsedForLayout;
  551. private:
  552. MemoryBlock dataCopy;
  553. CFDictionaryRef attributedStringAtts = {};
  554. float ascent = 0, unitsToHeightScaleFactor = 0;
  555. AffineTransform pathTransform;
  556. static void pathApplier (void* info, const CGPathElement* element)
  557. {
  558. auto& path = *static_cast<Path*> (info);
  559. auto* p = element->points;
  560. switch (element->type)
  561. {
  562. case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  563. case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  564. case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
  565. (float) p[1].x, (float) -p[1].y); break;
  566. case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  567. (float) p[1].x, (float) -p[1].y,
  568. (float) p[2].x, (float) -p[2].y); break;
  569. case kCGPathElementCloseSubpath: path.closeSubPath(); break;
  570. default: jassertfalse; break;
  571. }
  572. }
  573. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  574. };
  575. CTFontRef getCTFontFromTypeface (const Font& f)
  576. {
  577. if (auto* tf = dynamic_cast<OSXTypeface*> (f.getTypeface()))
  578. return tf->ctFontRef;
  579. return {};
  580. }
  581. StringArray Font::findAllTypefaceNames()
  582. {
  583. StringArray names;
  584. #if JUCE_MAC
  585. // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
  586. auto fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
  587. for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
  588. {
  589. auto family = String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i));
  590. if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
  591. names.addIfNotAlreadyThere (family);
  592. }
  593. CFRelease (fontFamilyArray);
  594. #else
  595. auto fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
  596. auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  597. CFRelease (fontCollectionRef);
  598. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  599. {
  600. auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  601. auto cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
  602. names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
  603. CFRelease (cfsFontFamily);
  604. }
  605. CFRelease (fontDescriptorArray);
  606. #endif
  607. names.sort (true);
  608. return names;
  609. }
  610. StringArray Font::findAllTypefaceStyles (const String& family)
  611. {
  612. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  613. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  614. StringArray results;
  615. auto cfsFontFamily = family.toCFString();
  616. CFStringRef keys[] = { kCTFontFamilyNameAttribute };
  617. CFTypeRef values[] = { cfsFontFamily };
  618. auto fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
  619. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  620. CFRelease (cfsFontFamily);
  621. auto ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  622. CFRelease (fontDescAttributes);
  623. auto fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
  624. CFRelease (ctFontDescRef);
  625. auto fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
  626. CFRelease (fontFamilyArray);
  627. auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  628. CFRelease (fontCollectionRef);
  629. if (fontDescriptorArray != nullptr)
  630. {
  631. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  632. {
  633. auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  634. auto cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
  635. results.add (String::fromCFString (cfsFontStyle));
  636. CFRelease (cfsFontStyle);
  637. }
  638. CFRelease (fontDescriptorArray);
  639. }
  640. return results;
  641. }
  642. //==============================================================================
  643. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return *new OSXTypeface (font); }
  644. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return *new OSXTypeface (data, size); }
  645. void Typeface::scanFolderForFonts (const File& folder)
  646. {
  647. for (auto& file : folder.findChildFiles (File::findFiles, false, "*.otf;*.ttf"))
  648. {
  649. if (auto urlref = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, file.getFullPathName().toCFString(), kCFURLPOSIXPathStyle, true))
  650. {
  651. CTFontManagerRegisterFontsForURL (urlref, kCTFontManagerScopeProcess, nullptr);
  652. CFRelease (urlref);
  653. }
  654. }
  655. }
  656. struct DefaultFontNames
  657. {
  658. #if JUCE_IOS
  659. String defaultSans { "Helvetica" },
  660. defaultSerif { "Times New Roman" },
  661. defaultFixed { "Courier New" };
  662. #else
  663. String defaultSans { "Lucida Grande" },
  664. defaultSerif { "Times New Roman" },
  665. defaultFixed { "Menlo" };
  666. #endif
  667. };
  668. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  669. {
  670. static DefaultFontNames defaultNames;
  671. auto newFont = font;
  672. auto& faceName = font.getTypefaceName();
  673. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  674. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  675. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  676. if (font.getTypefaceStyle() == getDefaultStyle())
  677. newFont.setTypefaceStyle ("Regular");
  678. return Typeface::createSystemTypefaceFor (newFont);
  679. }
  680. static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
  681. {
  682. auto numCharacterAttributes = text.getNumAttributes();
  683. for (int i = 0; i < numCharacterAttributes; ++i)
  684. {
  685. if (auto tf = dynamic_cast<OSXTypeface*> (text.getAttribute(i).font.getTypeface()))
  686. if (tf->canBeUsedForLayout)
  687. continue;
  688. return false;
  689. }
  690. return true;
  691. }
  692. bool TextLayout::createNativeLayout (const AttributedString& text)
  693. {
  694. if (canAllTypefacesBeUsedInLayout (text))
  695. {
  696. CoreTextTypeLayout::createLayout (*this, text);
  697. return true;
  698. }
  699. return false;
  700. }
  701. } // namespace juce