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.

900 lines
35KB

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