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.

juce_mac_Fonts.mm 36KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928
  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. if (attr.font.isUnderlined())
  195. {
  196. auto underline = kCTUnderlineStyleSingle;
  197. auto numberRef = CFNumberCreate (nullptr, kCFNumberIntType, &underline);
  198. CFAttributedStringSetAttribute (attribString, range, kCTUnderlineStyleAttributeName, numberRef);
  199. CFRelease (numberRef);
  200. }
  201. auto extraKerning = attr.font.getExtraKerningFactor();
  202. if (extraKerning != 0)
  203. {
  204. extraKerning *= attr.font.getHeight();
  205. auto numberRef = CFNumberCreate (nullptr, kCFNumberFloatType, &extraKerning);
  206. CFAttributedStringSetAttribute (attribString, range, kCTKernAttributeName, numberRef);
  207. CFRelease (numberRef);
  208. }
  209. CFRelease (ctFontRef);
  210. }
  211. {
  212. auto col = attr.colour;
  213. #if JUCE_IOS
  214. const CGFloat components[] = { col.getFloatRed(),
  215. col.getFloatGreen(),
  216. col.getFloatBlue(),
  217. col.getFloatAlpha() };
  218. auto colour = CGColorCreate (rgbColourSpace, components);
  219. #else
  220. auto colour = CGColorCreateGenericRGB (col.getFloatRed(),
  221. col.getFloatGreen(),
  222. col.getFloatBlue(),
  223. col.getFloatAlpha());
  224. #endif
  225. CFAttributedStringSetAttribute (attribString, range, kCTForegroundColorAttributeName, colour);
  226. CGColorRelease (colour);
  227. }
  228. }
  229. // Paragraph Attributes
  230. auto ctTextAlignment = getTextAlignment (text);
  231. auto ctLineBreakMode = getLineBreakMode (text);
  232. auto ctWritingDirection = getWritingDirection (text);
  233. CGFloat ctLineSpacing = text.getLineSpacing();
  234. CTParagraphStyleSetting settings[] =
  235. {
  236. { kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
  237. { kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
  238. { kCTParagraphStyleSpecifierBaseWritingDirection, sizeof (CTWritingDirection), &ctWritingDirection},
  239. { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
  240. };
  241. auto ctParagraphStyleRef = CTParagraphStyleCreate (settings, (size_t) numElementsInArray (settings));
  242. CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
  243. kCTParagraphStyleAttributeName, ctParagraphStyleRef);
  244. CFRelease (ctParagraphStyleRef);
  245. #if JUCE_IOS
  246. CGColorSpaceRelease (rgbColourSpace);
  247. #endif
  248. return attribString;
  249. }
  250. static CTFramesetterRef createCTFramesetter (const AttributedString& text)
  251. {
  252. auto attribString = createCFAttributedString (text);
  253. auto framesetter = CTFramesetterCreateWithAttributedString (attribString);
  254. CFRelease (attribString);
  255. return framesetter;
  256. }
  257. static CTFrameRef createCTFrame (CTFramesetterRef framesetter, CGRect bounds)
  258. {
  259. auto path = CGPathCreateMutable();
  260. CGPathAddRect (path, nullptr, bounds);
  261. auto frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr);
  262. CGPathRelease (path);
  263. return frame;
  264. }
  265. static CTFrameRef createCTFrame (const AttributedString& text, CGRect bounds)
  266. {
  267. auto framesetter = createCTFramesetter (text);
  268. auto frame = createCTFrame (framesetter, bounds);
  269. CFRelease (framesetter);
  270. return frame;
  271. }
  272. static Range<float> getLineVerticalRange (CTFrameRef frame, CFArrayRef lines, int lineIndex)
  273. {
  274. LineInfo info (frame, (CTLineRef) CFArrayGetValueAtIndex (lines, lineIndex), lineIndex);
  275. return { (float) (info.origin.y - info.descent),
  276. (float) (info.origin.y + info.ascent) };
  277. }
  278. static float findCTFrameHeight (CTFrameRef frame)
  279. {
  280. auto lines = CTFrameGetLines (frame);
  281. auto numLines = CFArrayGetCount (lines);
  282. if (numLines == 0)
  283. return 0;
  284. auto range = getLineVerticalRange (frame, lines, 0);
  285. if (numLines > 1)
  286. range = range.getUnionWith (getLineVerticalRange (frame, lines, (int) numLines - 1));
  287. return range.getLength();
  288. }
  289. static void drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
  290. const CGContextRef& context, float flipHeight)
  291. {
  292. auto framesetter = createCTFramesetter (text);
  293. // Ugly hack to fix a bug in OS X Sierra where the CTFrame needs to be slightly
  294. // larger than the font height - otherwise the CTFrame will be invalid
  295. CFRange fitrange;
  296. auto suggestedSingleLineFrameSize =
  297. CTFramesetterSuggestFrameSizeWithConstraints (framesetter, CFRangeMake (0, 0), nullptr,
  298. CGSizeMake (CGFLOAT_MAX, CGFLOAT_MAX), &fitrange);
  299. auto minCTFrameHeight = (float) suggestedSingleLineFrameSize.height;
  300. auto verticalJustification = text.getJustification().getOnlyVerticalFlags();
  301. const Rectangle<float> ctFrameArea = [area, minCTFrameHeight, verticalJustification]
  302. {
  303. if (minCTFrameHeight < area.getHeight())
  304. return Rectangle<float> (area);
  305. if (verticalJustification == Justification::verticallyCentred)
  306. return area.withSizeKeepingCentre (area.getWidth(), minCTFrameHeight);
  307. const Rectangle<float> frameArea = area.withHeight (minCTFrameHeight);
  308. if (verticalJustification == Justification::bottom)
  309. return frameArea.withBottomY (area.getBottom());
  310. return Rectangle<float> (frameArea);
  311. }();
  312. auto frame = createCTFrame (framesetter, CGRectMake ((CGFloat) ctFrameArea.getX(), flipHeight - (CGFloat) ctFrameArea.getBottom(),
  313. (CGFloat) ctFrameArea.getWidth(), (CGFloat) ctFrameArea.getHeight()));
  314. CFRelease (framesetter);
  315. auto textMatrix = CGContextGetTextMatrix (context);
  316. CGContextSaveGState (context);
  317. if (verticalJustification == Justification::verticallyCentred
  318. || verticalJustification == Justification::bottom)
  319. {
  320. auto adjust = ctFrameArea.getHeight() - findCTFrameHeight (frame);
  321. if (verticalJustification == Justification::verticallyCentred)
  322. adjust *= 0.5f;
  323. CGContextTranslateCTM (context, 0, -adjust);
  324. }
  325. CTFrameDraw (frame, context);
  326. CGContextRestoreGState (context);
  327. CGContextSetTextMatrix (context, textMatrix);
  328. CFRelease (frame);
  329. }
  330. static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
  331. {
  332. auto boundsHeight = glyphLayout.getHeight();
  333. auto frame = createCTFrame (text, CGRectMake (0, 0, glyphLayout.getWidth(), boundsHeight));
  334. auto lines = CTFrameGetLines (frame);
  335. auto numLines = CFArrayGetCount (lines);
  336. glyphLayout.ensureStorageAllocated ((int) numLines);
  337. for (CFIndex i = 0; i < numLines; ++i)
  338. {
  339. auto line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
  340. auto runs = CTLineGetGlyphRuns (line);
  341. auto numRuns = CFArrayGetCount (runs);
  342. auto cfrlineStringRange = CTLineGetStringRange (line);
  343. auto lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length;
  344. Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
  345. LineInfo lineInfo (frame, line, i);
  346. auto glyphLine = std::make_unique<TextLayout::Line> (lineStringRange,
  347. Point<float> ((float) lineInfo.origin.x,
  348. (float) (boundsHeight - lineInfo.origin.y)),
  349. (float) lineInfo.ascent,
  350. (float) lineInfo.descent,
  351. (float) lineInfo.leading,
  352. (int) numRuns);
  353. for (CFIndex j = 0; j < numRuns; ++j)
  354. {
  355. auto run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
  356. auto numGlyphs = CTRunGetGlyphCount (run);
  357. auto runStringRange = CTRunGetStringRange (run);
  358. auto glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
  359. (int) (runStringRange.location + runStringRange.length - 1)),
  360. (int) numGlyphs);
  361. glyphLine->runs.add (glyphRun);
  362. CFDictionaryRef runAttributes = CTRunGetAttributes (run);
  363. CTFontRef ctRunFont;
  364. if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void**) &ctRunFont))
  365. {
  366. auto cfsFontName = CTFontCopyPostScriptName (ctRunFont);
  367. auto ctFontRef = CTFontCreateWithName (cfsFontName, referenceFontSize, nullptr);
  368. CFRelease (cfsFontName);
  369. auto fontHeightToPointsFactor = getHeightToPointsFactor (ctFontRef);
  370. CFRelease (ctFontRef);
  371. auto cfsFontFamily = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontFamilyNameAttribute);
  372. auto cfsFontStyle = (CFStringRef) CTFontCopyAttribute (ctRunFont, kCTFontStyleNameAttribute);
  373. glyphRun->font = Font (String::fromCFString (cfsFontFamily),
  374. String::fromCFString (cfsFontStyle),
  375. (float) (CTFontGetSize (ctRunFont) / fontHeightToPointsFactor));
  376. auto isUnderlined = [&]
  377. {
  378. CFNumberRef underlineStyle;
  379. if (CFDictionaryGetValueIfPresent (runAttributes, kCTUnderlineStyleAttributeName, (const void**) &underlineStyle))
  380. {
  381. if (CFGetTypeID (underlineStyle) == CFNumberGetTypeID())
  382. {
  383. int value = 0;
  384. CFNumberGetValue (underlineStyle, kCFNumberLongType, (void*) &value);
  385. return value != 0;
  386. }
  387. }
  388. return false;
  389. }();
  390. glyphRun->font.setUnderline (isUnderlined);
  391. CFRelease (cfsFontStyle);
  392. CFRelease (cfsFontFamily);
  393. }
  394. CGColorRef cgRunColor;
  395. if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
  396. && CGColorGetNumberOfComponents (cgRunColor) == 4)
  397. {
  398. auto* components = CGColorGetComponents (cgRunColor);
  399. glyphRun->colour = Colour::fromFloatRGBA ((float) components[0],
  400. (float) components[1],
  401. (float) components[2],
  402. (float) components[3]);
  403. }
  404. const Glyphs glyphs (run, (size_t) numGlyphs);
  405. const Advances advances (run, numGlyphs);
  406. const Positions positions (run, (size_t) numGlyphs);
  407. for (CFIndex k = 0; k < numGlyphs; ++k)
  408. glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> ((float) positions.points[k].x,
  409. (float) positions.points[k].y),
  410. (float) advances.advances[k].width));
  411. }
  412. glyphLayout.addLine (std::move (glyphLine));
  413. }
  414. CFRelease (frame);
  415. }
  416. }
  417. //==============================================================================
  418. class OSXTypeface : public Typeface
  419. {
  420. public:
  421. OSXTypeface (const Font& font)
  422. : Typeface (font.getTypefaceName(), font.getTypefaceStyle()), canBeUsedForLayout (true)
  423. {
  424. ctFontRef = CoreTextTypeLayout::createCTFont (font, referenceFontSize, renderingTransform);
  425. if (ctFontRef != nullptr)
  426. {
  427. fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  428. initialiseMetrics();
  429. }
  430. }
  431. OSXTypeface (const void* data, size_t dataSize)
  432. : Typeface ({}, {}), canBeUsedForLayout (false), dataCopy (data, dataSize)
  433. {
  434. // We can't use CFDataCreate here as this triggers a false positive in ASAN
  435. // so copy the data manually and use CFDataCreateWithBytesNoCopy
  436. auto cfData = CFDataCreateWithBytesNoCopy (kCFAllocatorDefault, (const UInt8*) dataCopy.getData(),
  437. (CFIndex) dataCopy.getSize(), kCFAllocatorNull);
  438. auto provider = CGDataProviderCreateWithCFData (cfData);
  439. CFRelease (cfData);
  440. #if JUCE_IOS
  441. // Workaround for a an obscure iOS bug which can cause the app to dead-lock
  442. // when loading custom type faces. See: http://www.openradar.me/18778790 and
  443. // http://stackoverflow.com/questions/40242370/app-hangs-in-simulator
  444. [UIFont systemFontOfSize: 12];
  445. #endif
  446. fontRef = CGFontCreateWithDataProvider (provider);
  447. CGDataProviderRelease (provider);
  448. if (fontRef != nullptr)
  449. {
  450. #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
  451. if (SystemStats::getOperatingSystemType() >= SystemStats::OperatingSystemType::MacOSX_10_11)
  452. canBeUsedForLayout = CTFontManagerRegisterGraphicsFont (fontRef, nullptr);
  453. #endif
  454. ctFontRef = CTFontCreateWithGraphicsFont (fontRef, referenceFontSize, nullptr, nullptr);
  455. if (ctFontRef != nullptr)
  456. {
  457. if (auto fontName = CTFontCopyName (ctFontRef, kCTFontFamilyNameKey))
  458. {
  459. name = String::fromCFString (fontName);
  460. CFRelease (fontName);
  461. }
  462. if (auto fontStyle = CTFontCopyName (ctFontRef, kCTFontStyleNameKey))
  463. {
  464. style = String::fromCFString (fontStyle);
  465. CFRelease (fontStyle);
  466. }
  467. initialiseMetrics();
  468. }
  469. }
  470. }
  471. void initialiseMetrics()
  472. {
  473. auto ctAscent = std::abs ((float) CTFontGetAscent (ctFontRef));
  474. auto ctDescent = std::abs ((float) CTFontGetDescent (ctFontRef));
  475. auto ctTotalHeight = ctAscent + ctDescent;
  476. ascent = ctAscent / ctTotalHeight;
  477. unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
  478. pathTransform = AffineTransform::scale (unitsToHeightScaleFactor);
  479. fontHeightToPointsFactor = referenceFontSize / ctTotalHeight;
  480. const short zero = 0;
  481. auto numberRef = CFNumberCreate (nullptr, kCFNumberShortType, &zero);
  482. CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
  483. CFTypeRef values[] = { ctFontRef, numberRef };
  484. attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys,
  485. (const void**) &values, numElementsInArray (keys),
  486. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  487. CFRelease (numberRef);
  488. }
  489. ~OSXTypeface() override
  490. {
  491. if (attributedStringAtts != nullptr)
  492. CFRelease (attributedStringAtts);
  493. if (fontRef != nullptr)
  494. {
  495. #if JUCE_MAC && defined (MAC_OS_X_VERSION_10_8) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_8
  496. if (dataCopy.getSize() != 0)
  497. CTFontManagerUnregisterGraphicsFont (fontRef, nullptr);
  498. #endif
  499. CGFontRelease (fontRef);
  500. }
  501. if (ctFontRef != nullptr)
  502. CFRelease (ctFontRef);
  503. }
  504. float getAscent() const override { return ascent; }
  505. float getDescent() const override { return 1.0f - ascent; }
  506. float getHeightToPointsFactor() const override { return fontHeightToPointsFactor; }
  507. float getStringWidth (const String& text) override
  508. {
  509. float x = 0;
  510. if (ctFontRef != nullptr && text.isNotEmpty())
  511. {
  512. auto cfText = text.toCFString();
  513. auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  514. CFRelease (cfText);
  515. auto line = CTLineCreateWithAttributedString (attribString);
  516. auto runArray = CTLineGetGlyphRuns (line);
  517. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  518. {
  519. auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  520. auto length = CTRunGetGlyphCount (run);
  521. const CoreTextTypeLayout::Advances advances (run, length);
  522. for (int j = 0; j < length; ++j)
  523. x += (float) advances.advances[j].width;
  524. }
  525. CFRelease (line);
  526. CFRelease (attribString);
  527. x *= unitsToHeightScaleFactor;
  528. }
  529. return x;
  530. }
  531. void getGlyphPositions (const String& text, Array<int>& resultGlyphs, Array<float>& xOffsets) override
  532. {
  533. xOffsets.add (0);
  534. if (ctFontRef != nullptr && text.isNotEmpty())
  535. {
  536. float x = 0;
  537. auto cfText = text.toCFString();
  538. auto attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  539. CFRelease (cfText);
  540. auto line = CTLineCreateWithAttributedString (attribString);
  541. auto runArray = CTLineGetGlyphRuns (line);
  542. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  543. {
  544. auto run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  545. auto length = CTRunGetGlyphCount (run);
  546. const CoreTextTypeLayout::Advances advances (run, length);
  547. const CoreTextTypeLayout::Glyphs glyphs (run, (size_t) length);
  548. for (int j = 0; j < length; ++j)
  549. {
  550. x += (float) advances.advances[j].width;
  551. xOffsets.add (x * unitsToHeightScaleFactor);
  552. resultGlyphs.add (glyphs.glyphs[j]);
  553. }
  554. }
  555. CFRelease (line);
  556. CFRelease (attribString);
  557. }
  558. }
  559. bool getOutlineForGlyph (int glyphNumber, Path& path) override
  560. {
  561. jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
  562. if (auto pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform))
  563. {
  564. CGPathApply (pathRef, &path, pathApplier);
  565. CFRelease (pathRef);
  566. if (! pathTransform.isIdentity())
  567. path.applyTransform (pathTransform);
  568. return true;
  569. }
  570. return false;
  571. }
  572. //==============================================================================
  573. CGFontRef fontRef = {};
  574. CTFontRef ctFontRef = {};
  575. float fontHeightToPointsFactor = 1.0f;
  576. CGAffineTransform renderingTransform = CGAffineTransformIdentity;
  577. bool canBeUsedForLayout;
  578. private:
  579. MemoryBlock dataCopy;
  580. CFDictionaryRef attributedStringAtts = {};
  581. float ascent = 0, unitsToHeightScaleFactor = 0;
  582. AffineTransform pathTransform;
  583. static void pathApplier (void* info, const CGPathElement* element)
  584. {
  585. auto& path = *static_cast<Path*> (info);
  586. auto* p = element->points;
  587. switch (element->type)
  588. {
  589. case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  590. case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  591. case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
  592. (float) p[1].x, (float) -p[1].y); break;
  593. case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  594. (float) p[1].x, (float) -p[1].y,
  595. (float) p[2].x, (float) -p[2].y); break;
  596. case kCGPathElementCloseSubpath: path.closeSubPath(); break;
  597. default: jassertfalse; break;
  598. }
  599. }
  600. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface)
  601. };
  602. CTFontRef getCTFontFromTypeface (const Font& f)
  603. {
  604. if (auto* tf = dynamic_cast<OSXTypeface*> (f.getTypeface()))
  605. return tf->ctFontRef;
  606. return {};
  607. }
  608. StringArray Font::findAllTypefaceNames()
  609. {
  610. StringArray names;
  611. #if JUCE_MAC
  612. // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
  613. auto fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
  614. for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
  615. {
  616. auto family = String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i));
  617. if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
  618. names.addIfNotAlreadyThere (family);
  619. }
  620. CFRelease (fontFamilyArray);
  621. #else
  622. auto fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
  623. auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  624. CFRelease (fontCollectionRef);
  625. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  626. {
  627. auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  628. auto cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
  629. names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
  630. CFRelease (cfsFontFamily);
  631. }
  632. CFRelease (fontDescriptorArray);
  633. #endif
  634. names.sort (true);
  635. return names;
  636. }
  637. StringArray Font::findAllTypefaceStyles (const String& family)
  638. {
  639. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  640. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  641. StringArray results;
  642. auto cfsFontFamily = family.toCFString();
  643. CFStringRef keys[] = { kCTFontFamilyNameAttribute };
  644. CFTypeRef values[] = { cfsFontFamily };
  645. auto fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
  646. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  647. CFRelease (cfsFontFamily);
  648. auto ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  649. CFRelease (fontDescAttributes);
  650. auto fontFamilyArray = CFArrayCreate (kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
  651. CFRelease (ctFontDescRef);
  652. auto fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
  653. CFRelease (fontFamilyArray);
  654. auto fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  655. CFRelease (fontCollectionRef);
  656. if (fontDescriptorArray != nullptr)
  657. {
  658. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  659. {
  660. auto ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  661. auto cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
  662. results.add (String::fromCFString (cfsFontStyle));
  663. CFRelease (cfsFontStyle);
  664. }
  665. CFRelease (fontDescriptorArray);
  666. }
  667. return results;
  668. }
  669. //==============================================================================
  670. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font) { return *new OSXTypeface (font); }
  671. Typeface::Ptr Typeface::createSystemTypefaceFor (const void* data, size_t size) { return *new OSXTypeface (data, size); }
  672. void Typeface::scanFolderForFonts (const File& folder)
  673. {
  674. for (auto& file : folder.findChildFiles (File::findFiles, false, "*.otf;*.ttf"))
  675. {
  676. if (auto urlref = CFURLCreateWithFileSystemPath (kCFAllocatorDefault, file.getFullPathName().toCFString(), kCFURLPOSIXPathStyle, true))
  677. {
  678. CTFontManagerRegisterFontsForURL (urlref, kCTFontManagerScopeProcess, nullptr);
  679. CFRelease (urlref);
  680. }
  681. }
  682. }
  683. struct DefaultFontNames
  684. {
  685. #if JUCE_IOS
  686. String defaultSans { "Helvetica" },
  687. defaultSerif { "Times New Roman" },
  688. defaultFixed { "Courier New" };
  689. #else
  690. String defaultSans { "Lucida Grande" },
  691. defaultSerif { "Times New Roman" },
  692. defaultFixed { "Menlo" };
  693. #endif
  694. };
  695. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  696. {
  697. static DefaultFontNames defaultNames;
  698. auto newFont = font;
  699. auto& faceName = font.getTypefaceName();
  700. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  701. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  702. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  703. if (font.getTypefaceStyle() == getDefaultStyle())
  704. newFont.setTypefaceStyle ("Regular");
  705. return Typeface::createSystemTypefaceFor (newFont);
  706. }
  707. static bool canAllTypefacesBeUsedInLayout (const AttributedString& text)
  708. {
  709. auto numCharacterAttributes = text.getNumAttributes();
  710. for (int i = 0; i < numCharacterAttributes; ++i)
  711. {
  712. if (auto tf = dynamic_cast<OSXTypeface*> (text.getAttribute(i).font.getTypeface()))
  713. if (tf->canBeUsedForLayout)
  714. continue;
  715. return false;
  716. }
  717. return true;
  718. }
  719. bool TextLayout::createNativeLayout (const AttributedString& text)
  720. {
  721. if (canAllTypefacesBeUsedInLayout (text))
  722. {
  723. CoreTextTypeLayout::createLayout (*this, text);
  724. return true;
  725. }
  726. return false;
  727. }
  728. } // namespace juce