The JUCE cross-platform C++ framework, with DISTRHO/KXStudio specific changes
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.

1140 lines
45KB

  1. /*
  2. ==============================================================================
  3. This file is part of the JUCE library - "Jules' Utility Class Extensions"
  4. Copyright 2004-11 by Raw Material Software Ltd.
  5. ------------------------------------------------------------------------------
  6. JUCE can be redistributed and/or modified under the terms of the GNU General
  7. Public License (Version 2), as published by the Free Software Foundation.
  8. A copy of the license is included in the JUCE distribution, or can be found
  9. online at www.gnu.org/licenses.
  10. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY
  11. WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. ------------------------------------------------------------------------------
  14. To release a closed-source product which uses JUCE, commercial licenses are
  15. available: visit www.rawmaterialsoftware.com/juce for more information.
  16. ==============================================================================
  17. */
  18. #if (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 \
  19. && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5) \
  20. || (JUCE_IOS && defined (__IPHONE_3_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2)
  21. #define JUCE_CORETEXT_AVAILABLE 1
  22. #endif
  23. #if JUCE_CORETEXT_AVAILABLE
  24. namespace CoreTextTypeLayout
  25. {
  26. static CTFontRef createCTFont (const Font& font, const float fontSize,
  27. const bool applyScaleFactor)
  28. {
  29. CFStringRef cfFontFamily = font.getTypefaceName().toCFString();
  30. CFStringRef cfFontStyle = font.getTypefaceStyle().toCFString();
  31. CFStringRef keys[] = { kCTFontFamilyNameAttribute, kCTFontStyleNameAttribute };
  32. CFTypeRef values[] = { cfFontFamily, cfFontStyle };
  33. CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys,
  34. (const void**) &values,
  35. numElementsInArray (keys),
  36. &kCFTypeDictionaryKeyCallBacks,
  37. &kCFTypeDictionaryValueCallBacks);
  38. CFRelease (cfFontStyle);
  39. CFRelease (cfFontFamily);
  40. CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  41. CFRelease (fontDescAttributes);
  42. CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, fontSize, nullptr);
  43. CFRelease (ctFontDescRef);
  44. if (applyScaleFactor)
  45. {
  46. CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  47. const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef));
  48. const float factor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight;
  49. CGFontRelease (cgFontRef);
  50. CTFontRef newFont = CTFontCreateCopyWithAttributes (ctFontRef, fontSize * factor, nullptr, nullptr);
  51. CFRelease (ctFontRef);
  52. ctFontRef = newFont;
  53. }
  54. return ctFontRef;
  55. }
  56. //==============================================================================
  57. struct Advances
  58. {
  59. Advances (CTRunRef run, const CFIndex numGlyphs)
  60. : advances (CTRunGetAdvancesPtr (run))
  61. {
  62. if (advances == nullptr)
  63. {
  64. local.malloc (numGlyphs);
  65. CTRunGetAdvances (run, CFRangeMake (0, 0), local);
  66. advances = local;
  67. }
  68. }
  69. const CGSize* advances;
  70. HeapBlock<CGSize> local;
  71. };
  72. struct Glyphs
  73. {
  74. Glyphs (CTRunRef run, const int numGlyphs)
  75. : glyphs (CTRunGetGlyphsPtr (run))
  76. {
  77. if (glyphs == nullptr)
  78. {
  79. local.malloc (numGlyphs);
  80. CTRunGetGlyphs (run, CFRangeMake (0, 0), local);
  81. glyphs = local;
  82. }
  83. }
  84. const CGGlyph* glyphs;
  85. HeapBlock<CGGlyph> local;
  86. };
  87. struct Positions
  88. {
  89. Positions (CTRunRef run, const int numGlyphs)
  90. : points (CTRunGetPositionsPtr (run))
  91. {
  92. if (points == nullptr)
  93. {
  94. local.malloc (numGlyphs);
  95. CTRunGetPositions (run, CFRangeMake (0, 0), local);
  96. points = local;
  97. }
  98. }
  99. const CGPoint* points;
  100. HeapBlock<CGPoint> local;
  101. };
  102. //==============================================================================
  103. static CFAttributedStringRef createCFAttributedString (const AttributedString& text)
  104. {
  105. #if JUCE_IOS
  106. CGColorSpaceRef rgbColourSpace = CGColorSpaceCreateDeviceRGB();
  107. #endif
  108. CFStringRef cfText = text.getText().toCFString();
  109. CFMutableAttributedStringRef attribString = CFAttributedStringCreateMutable (kCFAllocatorDefault, 0);
  110. CFAttributedStringReplaceString (attribString, CFRangeMake(0, 0), cfText);
  111. CFRelease (cfText);
  112. const int numCharacterAttributes = text.getNumAttributes();
  113. for (int i = 0; i < numCharacterAttributes; ++i)
  114. {
  115. const AttributedString::Attribute* const attr = text.getAttribute (i);
  116. if (attr->range.getStart() > CFAttributedStringGetLength (attribString))
  117. continue;
  118. Range<int> range (attr->range);
  119. range.setEnd (jmin (range.getEnd(), (int) CFAttributedStringGetLength (attribString)));
  120. if (attr->getFont() != nullptr)
  121. {
  122. const Font& f = *attr->getFont();
  123. CTFontRef ctFontRef = createCTFont (f, f.getHeight(), true);
  124. CFAttributedStringSetAttribute (attribString, CFRangeMake (range.getStart(), range.getLength()),
  125. kCTFontAttributeName, ctFontRef);
  126. CFRelease (ctFontRef);
  127. }
  128. if (attr->getColour() != nullptr)
  129. {
  130. #if JUCE_IOS
  131. const CGFloat components[] = { attr->getColour()->getFloatRed(),
  132. attr->getColour()->getFloatGreen(),
  133. attr->getColour()->getFloatBlue(),
  134. attr->getColour()->getFloatAlpha() };
  135. CGColorRef colour = CGColorCreate (rgbColourSpace, components);
  136. #else
  137. CGColorRef colour = CGColorCreateGenericRGB (attr->getColour()->getFloatRed(),
  138. attr->getColour()->getFloatGreen(),
  139. attr->getColour()->getFloatBlue(),
  140. attr->getColour()->getFloatAlpha());
  141. #endif
  142. CFAttributedStringSetAttribute (attribString,
  143. CFRangeMake (range.getStart(), range.getLength()),
  144. kCTForegroundColorAttributeName, colour);
  145. CGColorRelease (colour);
  146. }
  147. }
  148. // Paragraph Attributes
  149. CTTextAlignment ctTextAlignment = kCTLeftTextAlignment;
  150. CTLineBreakMode ctLineBreakMode = kCTLineBreakByWordWrapping;
  151. const CGFloat ctLineSpacing = text.getLineSpacing();
  152. switch (text.getJustification().getOnlyHorizontalFlags())
  153. {
  154. case Justification::left: break;
  155. case Justification::right: ctTextAlignment = kCTRightTextAlignment; break;
  156. case Justification::horizontallyCentred: ctTextAlignment = kCTCenterTextAlignment; break;
  157. case Justification::horizontallyJustified: ctTextAlignment = kCTJustifiedTextAlignment; break;
  158. default: jassertfalse; break; // Illegal justification flags
  159. }
  160. switch (text.getWordWrap())
  161. {
  162. case AttributedString::byWord: break;
  163. case AttributedString::none: ctLineBreakMode = kCTLineBreakByClipping; break;
  164. case AttributedString::byChar: ctLineBreakMode = kCTLineBreakByCharWrapping; break;
  165. default: break;
  166. }
  167. CTParagraphStyleSetting settings[] =
  168. {
  169. { kCTParagraphStyleSpecifierAlignment, sizeof (CTTextAlignment), &ctTextAlignment },
  170. { kCTParagraphStyleSpecifierLineBreakMode, sizeof (CTLineBreakMode), &ctLineBreakMode },
  171. #if defined (MAC_OS_X_VERSION_10_7) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_7
  172. { kCTParagraphStyleSpecifierLineSpacingAdjustment, sizeof (CGFloat), &ctLineSpacing }
  173. #else
  174. { kCTParagraphStyleSpecifierLineSpacing, sizeof (CGFloat), &ctLineSpacing }
  175. #endif
  176. };
  177. CTParagraphStyleRef ctParagraphStyleRef = CTParagraphStyleCreate (settings, numElementsInArray (settings));
  178. CFAttributedStringSetAttribute (attribString, CFRangeMake (0, CFAttributedStringGetLength (attribString)),
  179. kCTParagraphStyleAttributeName, ctParagraphStyleRef);
  180. CFRelease (ctParagraphStyleRef);
  181. #if JUCE_IOS
  182. CGColorSpaceRelease (rgbColourSpace);
  183. #endif
  184. return attribString;
  185. }
  186. static void drawToCGContext (const AttributedString& text, const Rectangle<float>& area,
  187. const CGContextRef& context, const float flipHeight)
  188. {
  189. CFAttributedStringRef attribString = CoreTextTypeLayout::createCFAttributedString (text);
  190. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString);
  191. CFRelease (attribString);
  192. CGMutablePathRef path = CGPathCreateMutable();
  193. CGRect bounds = CGRectMake ((CGFloat) area.getX(), flipHeight - (CGFloat) area.getBottom(),
  194. (CGFloat) area.getWidth(), (CGFloat) area.getHeight());
  195. CGPathAddRect (path, nullptr, bounds);
  196. CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake (0, 0), path, nullptr);
  197. CFRelease (framesetter);
  198. CGPathRelease (path);
  199. CTFrameDraw (frame, context);
  200. CFRelease (frame);
  201. }
  202. static void createLayout (TextLayout& glyphLayout, const AttributedString& text)
  203. {
  204. CFAttributedStringRef attribString = CoreTextTypeLayout::createCFAttributedString (text);
  205. CTFramesetterRef framesetter = CTFramesetterCreateWithAttributedString (attribString);
  206. CFRelease (attribString);
  207. CGMutablePathRef path = CGPathCreateMutable();
  208. const CGRect bounds = CGRectMake (0, 0, glyphLayout.getWidth(), 1.0e6f);
  209. CGPathAddRect (path, nullptr, bounds);
  210. CTFrameRef frame = CTFramesetterCreateFrame (framesetter, CFRangeMake(0, 0), path, nullptr);
  211. CFRelease (framesetter);
  212. CGPathRelease (path);
  213. CFArrayRef lines = CTFrameGetLines (frame);
  214. const CFIndex numLines = CFArrayGetCount (lines);
  215. glyphLayout.ensureStorageAllocated (numLines);
  216. for (CFIndex i = 0; i < numLines; ++i)
  217. {
  218. CTLineRef line = (CTLineRef) CFArrayGetValueAtIndex (lines, i);
  219. CFArrayRef runs = CTLineGetGlyphRuns (line);
  220. const CFIndex numRuns = CFArrayGetCount (runs);
  221. const CFRange cfrlineStringRange = CTLineGetStringRange (line);
  222. const CFIndex lineStringEnd = cfrlineStringRange.location + cfrlineStringRange.length - 1;
  223. const Range<int> lineStringRange ((int) cfrlineStringRange.location, (int) lineStringEnd);
  224. CGPoint cgpLineOrigin;
  225. CTFrameGetLineOrigins (frame, CFRangeMake(i, 1), &cgpLineOrigin);
  226. Point<float> lineOrigin ((float) cgpLineOrigin.x, bounds.size.height - (float) cgpLineOrigin.y);
  227. CGFloat ascent, descent, leading;
  228. CTLineGetTypographicBounds (line, &ascent, &descent, &leading);
  229. TextLayout::Line* const glyphLine = new TextLayout::Line (lineStringRange, lineOrigin,
  230. (float) ascent, (float) descent, (float) leading,
  231. (int) numRuns);
  232. glyphLayout.addLine (glyphLine);
  233. for (CFIndex j = 0; j < numRuns; ++j)
  234. {
  235. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runs, j);
  236. const CFIndex numGlyphs = CTRunGetGlyphCount (run);
  237. const CFRange runStringRange = CTRunGetStringRange (run);
  238. TextLayout::Run* const glyphRun = new TextLayout::Run (Range<int> ((int) runStringRange.location,
  239. (int) (runStringRange.location + runStringRange.length - 1)),
  240. (int) numGlyphs);
  241. glyphLine->runs.add (glyphRun);
  242. CFDictionaryRef runAttributes = CTRunGetAttributes (run);
  243. CTFontRef ctRunFont;
  244. if (CFDictionaryGetValueIfPresent (runAttributes, kCTFontAttributeName, (const void **) &ctRunFont))
  245. {
  246. CTFontDescriptorRef ctFontDescRef = CTFontCopyFontDescriptor (ctRunFont);
  247. CFDictionaryRef fontDescAttributes = CTFontDescriptorCopyAttributes (ctFontDescRef);
  248. CTFontRef ctFontRef = CTFontCreateWithFontDescriptor (ctFontDescRef, 1024, nullptr);
  249. CFRelease (ctFontDescRef);
  250. CGFontRef cgFontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  251. CFRelease (ctFontRef);
  252. const int totalHeight = std::abs (CGFontGetAscent (cgFontRef)) + std::abs (CGFontGetDescent (cgFontRef));
  253. const float fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (cgFontRef) / (float) totalHeight;
  254. CGFontRelease (cgFontRef);
  255. CFStringRef cfsFontFamily = (CFStringRef) CFDictionaryGetValue (fontDescAttributes, kCTFontFamilyNameAttribute);
  256. CFStringRef cfsFontStyle = (CFStringRef) CFDictionaryGetValue (fontDescAttributes, kCTFontStyleNameAttribute);
  257. glyphRun->font = Font (String::fromCFString (cfsFontFamily),
  258. String::fromCFString (cfsFontStyle),
  259. CTFontGetSize (ctRunFont) / fontHeightToCGSizeFactor);
  260. CFRelease (fontDescAttributes);
  261. }
  262. CGColorRef cgRunColor;
  263. if (CFDictionaryGetValueIfPresent (runAttributes, kCTForegroundColorAttributeName, (const void**) &cgRunColor)
  264. && CGColorGetNumberOfComponents (cgRunColor) == 4)
  265. {
  266. const CGFloat* const components = CGColorGetComponents (cgRunColor);
  267. glyphRun->colour = Colour::fromFloatRGBA (components[0], components[1], components[2], components[3]);
  268. }
  269. const CoreTextTypeLayout::Glyphs glyphs (run, numGlyphs);
  270. const CoreTextTypeLayout::Advances advances (run, numGlyphs);
  271. const CoreTextTypeLayout::Positions positions (run, numGlyphs);
  272. for (CFIndex k = 0; k < numGlyphs; ++k)
  273. glyphRun->glyphs.add (TextLayout::Glyph (glyphs.glyphs[k], Point<float> (positions.points[k].x,
  274. positions.points[k].y),
  275. advances.advances[k].width));
  276. }
  277. }
  278. CFRelease (frame);
  279. }
  280. }
  281. //==============================================================================
  282. class OSXTypeface : public Typeface
  283. {
  284. public:
  285. OSXTypeface (const Font& font)
  286. : Typeface (font.getTypefaceName(),
  287. font.getTypefaceStyle()),
  288. fontRef (nullptr),
  289. fontHeightToCGSizeFactor (1.0f),
  290. renderingTransform (CGAffineTransformIdentity),
  291. ctFontRef (nullptr),
  292. attributedStringAtts (nullptr),
  293. ascent (0.0f),
  294. unitsToHeightScaleFactor (0.0f)
  295. {
  296. ctFontRef = CoreTextTypeLayout::createCTFont (font, 1024.0f, false);
  297. if (ctFontRef != nullptr)
  298. {
  299. ascent = std::abs ((float) CTFontGetAscent (ctFontRef));
  300. const float totalSize = ascent + std::abs ((float) CTFontGetDescent (ctFontRef));
  301. ascent /= totalSize;
  302. pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize);
  303. fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);
  304. const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef));
  305. const float ctTotalHeight = abs (CTFontGetAscent (ctFontRef)) + abs (CTFontGetDescent (ctFontRef));
  306. unitsToHeightScaleFactor = 1.0f / ctTotalHeight;
  307. fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight;
  308. const short zero = 0;
  309. CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);
  310. CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };
  311. CFTypeRef values[] = { ctFontRef, numberRef };
  312. attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),
  313. &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  314. CFRelease (numberRef);
  315. }
  316. }
  317. ~OSXTypeface()
  318. {
  319. if (attributedStringAtts != nullptr)
  320. CFRelease (attributedStringAtts);
  321. if (fontRef != nullptr)
  322. CGFontRelease (fontRef);
  323. if (ctFontRef != nullptr)
  324. CFRelease (ctFontRef);
  325. }
  326. float getAscent() const { return ascent; }
  327. float getDescent() const { return 1.0f - ascent; }
  328. float getStringWidth (const String& text)
  329. {
  330. float x = 0;
  331. if (ctFontRef != nullptr && text.isNotEmpty())
  332. {
  333. CFStringRef cfText = text.toCFString();
  334. CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  335. CFRelease (cfText);
  336. CTLineRef line = CTLineCreateWithAttributedString (attribString);
  337. CFArrayRef runArray = CTLineGetGlyphRuns (line);
  338. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  339. {
  340. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  341. CFIndex length = CTRunGetGlyphCount (run);
  342. const CoreTextTypeLayout::Advances advances (run, length);
  343. for (int j = 0; j < length; ++j)
  344. x += (float) advances.advances[j].width;
  345. }
  346. CFRelease (line);
  347. CFRelease (attribString);
  348. x *= unitsToHeightScaleFactor;
  349. }
  350. return x;
  351. }
  352. void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
  353. {
  354. xOffsets.add (0);
  355. if (ctFontRef != nullptr && text.isNotEmpty())
  356. {
  357. float x = 0;
  358. CFStringRef cfText = text.toCFString();
  359. CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);
  360. CFRelease (cfText);
  361. CTLineRef line = CTLineCreateWithAttributedString (attribString);
  362. CFArrayRef runArray = CTLineGetGlyphRuns (line);
  363. for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)
  364. {
  365. CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);
  366. CFIndex length = CTRunGetGlyphCount (run);
  367. const CoreTextTypeLayout::Advances advances (run, length);
  368. const CoreTextTypeLayout::Glyphs glyphs (run, length);
  369. for (int j = 0; j < length; ++j)
  370. {
  371. x += (float) advances.advances[j].width;
  372. xOffsets.add (x * unitsToHeightScaleFactor);
  373. resultGlyphs.add (glyphs.glyphs[j]);
  374. }
  375. }
  376. CFRelease (line);
  377. CFRelease (attribString);
  378. }
  379. }
  380. EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)
  381. {
  382. Path path;
  383. if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
  384. return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
  385. path, transform);
  386. return nullptr;
  387. }
  388. bool getOutlineForGlyph (int glyphNumber, Path& path)
  389. {
  390. jassert (path.isEmpty()); // we might need to apply a transform to the path, so this must be empty
  391. CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform);
  392. if (pathRef == 0)
  393. return false;
  394. CGPathApply (pathRef, &path, pathApplier);
  395. CFRelease (pathRef);
  396. if (! pathTransform.isIdentity())
  397. path.applyTransform (pathTransform);
  398. return true;
  399. }
  400. //==============================================================================
  401. CGFontRef fontRef;
  402. float fontHeightToCGSizeFactor;
  403. CGAffineTransform renderingTransform;
  404. private:
  405. CTFontRef ctFontRef;
  406. CFDictionaryRef attributedStringAtts;
  407. float ascent, unitsToHeightScaleFactor;
  408. AffineTransform pathTransform;
  409. static void pathApplier (void* info, const CGPathElement* const element)
  410. {
  411. Path& path = *static_cast<Path*> (info);
  412. const CGPoint* const p = element->points;
  413. switch (element->type)
  414. {
  415. case kCGPathElementMoveToPoint: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  416. case kCGPathElementAddLineToPoint: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  417. case kCGPathElementAddQuadCurveToPoint: path.quadraticTo ((float) p[0].x, (float) -p[0].y,
  418. (float) p[1].x, (float) -p[1].y); break;
  419. case kCGPathElementAddCurveToPoint: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  420. (float) p[1].x, (float) -p[1].y,
  421. (float) p[2].x, (float) -p[2].y); break;
  422. case kCGPathElementCloseSubpath: path.closeSubPath(); break;
  423. default: jassertfalse; break;
  424. }
  425. }
  426. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface);
  427. };
  428. StringArray Font::findAllTypefaceNames()
  429. {
  430. StringArray names;
  431. #if MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_5 && ! JUCE_IOS
  432. // CTFontManager only exists on OS X 10.6 and later, it does not exist on iOS
  433. CFArrayRef fontFamilyArray = CTFontManagerCopyAvailableFontFamilyNames();
  434. for (CFIndex i = 0; i < CFArrayGetCount (fontFamilyArray); ++i)
  435. {
  436. const String family (String::fromCFString ((CFStringRef) CFArrayGetValueAtIndex (fontFamilyArray, i)));
  437. if (! family.startsWithChar ('.')) // ignore fonts that start with a '.'
  438. names.addIfNotAlreadyThere (family);
  439. }
  440. CFRelease (fontFamilyArray);
  441. #else
  442. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateFromAvailableFonts (nullptr);
  443. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  444. CFRelease (fontCollectionRef);
  445. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  446. {
  447. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  448. CFStringRef cfsFontFamily = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontFamilyNameAttribute);
  449. names.addIfNotAlreadyThere (String::fromCFString (cfsFontFamily));
  450. CFRelease (cfsFontFamily);
  451. }
  452. CFRelease (fontDescriptorArray);
  453. #endif
  454. names.sort (true);
  455. return names;
  456. }
  457. StringArray Font::findAllTypefaceStyles (const String& family)
  458. {
  459. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  460. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  461. StringArray results;
  462. CFStringRef cfsFontFamily = family.toCFString();
  463. CFStringRef keys[] = { kCTFontFamilyNameAttribute };
  464. CFTypeRef values[] = { cfsFontFamily };
  465. CFDictionaryRef fontDescAttributes = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys), &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
  466. CFRelease (cfsFontFamily);
  467. CTFontDescriptorRef ctFontDescRef = CTFontDescriptorCreateWithAttributes (fontDescAttributes);
  468. CFRelease (fontDescAttributes);
  469. CFArrayRef fontFamilyArray = CFArrayCreate(kCFAllocatorDefault, (const void**) &ctFontDescRef, 1, &kCFTypeArrayCallBacks);
  470. CFRelease (ctFontDescRef);
  471. CTFontCollectionRef fontCollectionRef = CTFontCollectionCreateWithFontDescriptors (fontFamilyArray, nullptr);
  472. CFRelease (fontFamilyArray);
  473. CFArrayRef fontDescriptorArray = CTFontCollectionCreateMatchingFontDescriptors (fontCollectionRef);
  474. CFRelease (fontCollectionRef);
  475. if (fontDescriptorArray != nullptr)
  476. {
  477. for (CFIndex i = 0; i < CFArrayGetCount (fontDescriptorArray); ++i)
  478. {
  479. CTFontDescriptorRef ctFontDescriptorRef = (CTFontDescriptorRef) CFArrayGetValueAtIndex (fontDescriptorArray, i);
  480. CFStringRef cfsFontStyle = (CFStringRef) CTFontDescriptorCopyAttribute (ctFontDescriptorRef, kCTFontStyleNameAttribute);
  481. results.add (String::fromCFString (cfsFontStyle));
  482. CFRelease (cfsFontStyle);
  483. }
  484. CFRelease (fontDescriptorArray);
  485. }
  486. return results;
  487. }
  488. #else
  489. //==============================================================================
  490. // The stuff that follows is a mash-up that supports pre-OSX 10.5 and pre-iOS 3.2 APIs.
  491. // (Hopefully all of this can be ditched at some point in the future).
  492. //==============================================================================
  493. #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5
  494. #define SUPPORT_10_4_FONTS 1
  495. #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0)
  496. #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5
  497. #define SUPPORT_ONLY_10_4_FONTS 1
  498. #endif
  499. } // (juce namespace)
  500. @interface NSFont (PrivateHack)
  501. - (NSGlyph) _defaultGlyphForChar: (unichar) theChar;
  502. @end
  503. namespace juce
  504. {
  505. #endif
  506. //==============================================================================
  507. class OSXTypeface : public Typeface
  508. {
  509. public:
  510. OSXTypeface (const Font& font)
  511. : Typeface (font.getTypefaceName(), font.getTypefaceStyle())
  512. {
  513. JUCE_AUTORELEASEPOOL
  514. renderingTransform = CGAffineTransformIdentity;
  515. #if JUCE_IOS
  516. NSString* fontName = juceStringToNS (style);
  517. fontRef = CGFontCreateWithFontName ((CFStringRef) fontName);
  518. if (fontRef == 0)
  519. {
  520. // Sometimes, UIFont manages to handle names that CGFontCreateWithFontName fails on...
  521. UIFont* uiFont = [UIFont fontWithName: fontName size: 12];
  522. fontRef = CGFontCreateWithFontName ((CFStringRef) uiFont.fontName);
  523. }
  524. const int ascender = abs (CGFontGetAscent (fontRef));
  525. const float totalHeight = ascender + abs (CGFontGetDescent (fontRef));
  526. ascent = ascender / totalHeight;
  527. unitsToHeightScaleFactor = 1.0f / totalHeight;
  528. fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / totalHeight;
  529. #else
  530. NSDictionary* nsDict = [NSDictionary dictionaryWithObjectsAndKeys:
  531. juceStringToNS (name), NSFontFamilyAttribute,
  532. juceStringToNS (style), NSFontFaceAttribute, nil];
  533. NSFontDescriptor* nsFontDesc = [NSFontDescriptor fontDescriptorWithFontAttributes: nsDict];
  534. nsFont = [NSFont fontWithDescriptor: nsFontDesc size: 1024];
  535. [nsFont retain];
  536. ascent = std::abs ((float) [nsFont ascender]);
  537. float totalSize = ascent + std::abs ((float) [nsFont descender]);
  538. ascent /= totalSize;
  539. pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize);
  540. #if SUPPORT_ONLY_10_4_FONTS
  541. ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);
  542. if (atsFont == 0)
  543. atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);
  544. fontRef = CGFontCreateWithPlatformFont (&atsFont);
  545. const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]);
  546. unitsToHeightScaleFactor = 1.0f / totalHeight;
  547. fontHeightToCGSizeFactor = 1024.0f / totalHeight;
  548. #else
  549. #if SUPPORT_10_4_FONTS
  550. if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)
  551. {
  552. ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);
  553. if (atsFont == 0)
  554. atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);
  555. fontRef = CGFontCreateWithPlatformFont (&atsFont);
  556. const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]);
  557. unitsToHeightScaleFactor = 1.0f / totalHeight;
  558. fontHeightToCGSizeFactor = 1024.0f / totalHeight;
  559. }
  560. else
  561. #endif
  562. {
  563. fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]);
  564. const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef));
  565. unitsToHeightScaleFactor = 1.0f / totalHeight;
  566. fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight;
  567. }
  568. #endif
  569. #endif
  570. }
  571. ~OSXTypeface()
  572. {
  573. #if ! JUCE_IOS
  574. [nsFont release];
  575. #endif
  576. if (fontRef != 0)
  577. CGFontRelease (fontRef);
  578. }
  579. float getAscent() const { return ascent; }
  580. float getDescent() const { return 1.0f - ascent; }
  581. float getStringWidth (const String& text)
  582. {
  583. if (fontRef == 0 || text.isEmpty())
  584. return 0;
  585. const int length = text.length();
  586. HeapBlock <CGGlyph> glyphs;
  587. createGlyphsForString (text.getCharPointer(), length, glyphs);
  588. float x = 0;
  589. #if SUPPORT_ONLY_10_4_FONTS
  590. HeapBlock <NSSize> advances (length);
  591. [nsFont getAdvancements: advances forGlyphs: reinterpret_cast <NSGlyph*> (glyphs.getData()) count: length];
  592. for (int i = 0; i < length; ++i)
  593. x += advances[i].width;
  594. #else
  595. #if SUPPORT_10_4_FONTS
  596. if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)
  597. {
  598. HeapBlock <NSSize> advances (length);
  599. [nsFont getAdvancements: advances forGlyphs: reinterpret_cast<NSGlyph*> (glyphs.getData()) count: length];
  600. for (int i = 0; i < length; ++i)
  601. x += advances[i].width;
  602. }
  603. else
  604. #endif
  605. {
  606. HeapBlock <int> advances (length);
  607. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  608. for (int i = 0; i < length; ++i)
  609. x += advances[i];
  610. }
  611. #endif
  612. return x * unitsToHeightScaleFactor;
  613. }
  614. void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)
  615. {
  616. xOffsets.add (0);
  617. if (fontRef == 0 || text.isEmpty())
  618. return;
  619. const int length = text.length();
  620. HeapBlock <CGGlyph> glyphs;
  621. createGlyphsForString (text.getCharPointer(), length, glyphs);
  622. #if SUPPORT_ONLY_10_4_FONTS
  623. HeapBlock <NSSize> advances (length);
  624. [nsFont getAdvancements: advances forGlyphs: reinterpret_cast <NSGlyph*> (glyphs.getData()) count: length];
  625. int x = 0;
  626. for (int i = 0; i < length; ++i)
  627. {
  628. x += advances[i].width;
  629. xOffsets.add (x * unitsToHeightScaleFactor);
  630. resultGlyphs.add (reinterpret_cast <NSGlyph*> (glyphs.getData())[i]);
  631. }
  632. #else
  633. #if SUPPORT_10_4_FONTS
  634. if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)
  635. {
  636. HeapBlock <NSSize> advances (length);
  637. NSGlyph* const nsGlyphs = reinterpret_cast<NSGlyph*> (glyphs.getData());
  638. [nsFont getAdvancements: advances forGlyphs: nsGlyphs count: length];
  639. float x = 0;
  640. for (int i = 0; i < length; ++i)
  641. {
  642. x += advances[i].width;
  643. xOffsets.add (x * unitsToHeightScaleFactor);
  644. resultGlyphs.add (nsGlyphs[i]);
  645. }
  646. }
  647. else
  648. #endif
  649. {
  650. HeapBlock <int> advances (length);
  651. if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))
  652. {
  653. int x = 0;
  654. for (int i = 0; i < length; ++i)
  655. {
  656. x += advances [i];
  657. xOffsets.add (x * unitsToHeightScaleFactor);
  658. resultGlyphs.add (glyphs[i]);
  659. }
  660. }
  661. }
  662. #endif
  663. }
  664. EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)
  665. {
  666. Path path;
  667. if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())
  668. return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),
  669. path, transform);
  670. return nullptr;
  671. }
  672. bool getOutlineForGlyph (int glyphNumber, Path& path)
  673. {
  674. #if JUCE_IOS
  675. return false;
  676. #else
  677. if (nsFont == nil)
  678. return false;
  679. // we might need to apply a transform to the path, so it mustn't have anything else in it
  680. jassert (path.isEmpty());
  681. JUCE_AUTORELEASEPOOL
  682. NSBezierPath* bez = [NSBezierPath bezierPath];
  683. [bez moveToPoint: NSMakePoint (0, 0)];
  684. [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber
  685. inFont: nsFont];
  686. for (int i = 0; i < [bez elementCount]; ++i)
  687. {
  688. NSPoint p[3];
  689. switch ([bez elementAtIndex: i associatedPoints: p])
  690. {
  691. case NSMoveToBezierPathElement: path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;
  692. case NSLineToBezierPathElement: path.lineTo ((float) p[0].x, (float) -p[0].y); break;
  693. case NSCurveToBezierPathElement: path.cubicTo ((float) p[0].x, (float) -p[0].y,
  694. (float) p[1].x, (float) -p[1].y,
  695. (float) p[2].x, (float) -p[2].y); break;
  696. case NSClosePathBezierPathElement: path.closeSubPath(); break;
  697. default: jassertfalse; break;
  698. }
  699. }
  700. path.applyTransform (pathTransform);
  701. return true;
  702. #endif
  703. }
  704. //==============================================================================
  705. CGFontRef fontRef;
  706. float fontHeightToCGSizeFactor;
  707. CGAffineTransform renderingTransform;
  708. private:
  709. float ascent, unitsToHeightScaleFactor;
  710. #if ! JUCE_IOS
  711. NSFont* nsFont;
  712. AffineTransform pathTransform;
  713. #endif
  714. void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock <CGGlyph>& glyphs)
  715. {
  716. #if SUPPORT_10_4_FONTS
  717. #if ! SUPPORT_ONLY_10_4_FONTS
  718. if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)
  719. #endif
  720. {
  721. glyphs.malloc (sizeof (NSGlyph) * length, 1);
  722. NSGlyph* const nsGlyphs = reinterpret_cast<NSGlyph*> (glyphs.getData());
  723. for (int i = 0; i < length; ++i)
  724. nsGlyphs[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text.getAndAdvance()];
  725. return;
  726. }
  727. #endif
  728. #if ! SUPPORT_ONLY_10_4_FONTS
  729. if (charToGlyphMapper == nullptr)
  730. charToGlyphMapper = new CharToGlyphMapper (fontRef);
  731. glyphs.malloc (length);
  732. for (int i = 0; i < length; ++i)
  733. glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance());
  734. #endif
  735. }
  736. #if ! SUPPORT_ONLY_10_4_FONTS
  737. // Reads a CGFontRef's character map table to convert unicode into glyph numbers
  738. class CharToGlyphMapper
  739. {
  740. public:
  741. CharToGlyphMapper (CGFontRef fontRef)
  742. : segCount (0), endCode (0), startCode (0), idDelta (0),
  743. idRangeOffset (0), glyphIndexes (0)
  744. {
  745. CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap');
  746. if (cmapTable != 0)
  747. {
  748. const int numSubtables = getValue16 (cmapTable, 2);
  749. for (int i = 0; i < numSubtables; ++i)
  750. {
  751. if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0
  752. {
  753. const int offset = getValue32 (cmapTable, i * 8 + 8);
  754. if (getValue16 (cmapTable, offset) == 4) // check that it's format 4..
  755. {
  756. const int length = getValue16 (cmapTable, offset + 2);
  757. const int segCountX2 = getValue16 (cmapTable, offset + 6);
  758. segCount = segCountX2 / 2;
  759. const int endCodeOffset = offset + 14;
  760. const int startCodeOffset = endCodeOffset + 2 + segCountX2;
  761. const int idDeltaOffset = startCodeOffset + segCountX2;
  762. const int idRangeOffsetOffset = idDeltaOffset + segCountX2;
  763. const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2;
  764. endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2);
  765. startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2);
  766. idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2);
  767. idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2);
  768. glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset);
  769. }
  770. break;
  771. }
  772. }
  773. CFRelease (cmapTable);
  774. }
  775. }
  776. ~CharToGlyphMapper()
  777. {
  778. if (endCode != 0)
  779. {
  780. CFRelease (endCode);
  781. CFRelease (startCode);
  782. CFRelease (idDelta);
  783. CFRelease (idRangeOffset);
  784. CFRelease (glyphIndexes);
  785. }
  786. }
  787. int getGlyphForCharacter (const juce_wchar c) const
  788. {
  789. for (int i = 0; i < segCount; ++i)
  790. {
  791. if (getValue16 (endCode, i * 2) >= c)
  792. {
  793. const int start = getValue16 (startCode, i * 2);
  794. if (start > c)
  795. break;
  796. const int delta = getValue16 (idDelta, i * 2);
  797. const int rangeOffset = getValue16 (idRangeOffset, i * 2);
  798. if (rangeOffset == 0)
  799. return delta + c;
  800. else
  801. return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i)));
  802. }
  803. }
  804. // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts!
  805. return jmax (-1, (int) c - 29);
  806. }
  807. private:
  808. int segCount;
  809. CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes;
  810. static uint16 getValue16 (CFDataRef data, const int index)
  811. {
  812. return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index));
  813. }
  814. static uint32 getValue32 (CFDataRef data, const int index)
  815. {
  816. return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index));
  817. }
  818. };
  819. ScopedPointer <CharToGlyphMapper> charToGlyphMapper;
  820. #endif
  821. JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (OSXTypeface);
  822. };
  823. StringArray Font::findAllTypefaceNames()
  824. {
  825. StringArray names;
  826. JUCE_AUTORELEASEPOOL
  827. #if JUCE_IOS
  828. NSArray* fonts = [UIFont familyNames];
  829. #else
  830. NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies];
  831. #endif
  832. for (unsigned int i = 0; i < [fonts count]; ++i)
  833. names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i]));
  834. names.sort (true);
  835. return names;
  836. }
  837. StringArray Font::findAllTypefaceStyles (const String& family)
  838. {
  839. if (FontStyleHelpers::isPlaceholderFamilyName (family))
  840. return findAllTypefaceStyles (FontStyleHelpers::getConcreteFamilyNameFromPlaceholder (family));
  841. StringArray results;
  842. JUCE_AUTORELEASEPOOL
  843. #if JUCE_IOS
  844. NSArray* styles = [UIFont fontNamesForFamilyName: juceStringToNS (family)];
  845. #else
  846. NSArray* styles = [[NSFontManager sharedFontManager] availableMembersOfFontFamily: juceStringToNS (family)];
  847. #endif
  848. for (unsigned int i = 0; i < [styles count]; ++i)
  849. {
  850. #if JUCE_IOS
  851. // Fonts are returned in the form of "Arial-BoldMT"
  852. results.add (nsStringToJuce ((NSString*) [styles objectAtIndex: i]));
  853. #else
  854. NSArray* style = [styles objectAtIndex: i];
  855. results.add (nsStringToJuce ((NSString*) [style objectAtIndex: 1]));
  856. #endif
  857. }
  858. return results;
  859. }
  860. #endif
  861. //==============================================================================
  862. Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)
  863. {
  864. return new OSXTypeface (font);
  865. }
  866. struct DefaultFontNames
  867. {
  868. DefaultFontNames()
  869. #if JUCE_IOS
  870. : defaultSans ("Helvetica"),
  871. defaultSerif ("Times New Roman"),
  872. defaultFixed ("Courier New"),
  873. #else
  874. : defaultSans ("Lucida Grande"),
  875. defaultSerif ("Times New Roman"),
  876. defaultFixed ("Monaco"),
  877. #endif
  878. defaultFallback ("Arial Unicode MS")
  879. {
  880. }
  881. String defaultSans, defaultSerif, defaultFixed, defaultFallback;
  882. };
  883. Typeface::Ptr Font::getDefaultTypefaceForFont (const Font& font)
  884. {
  885. static DefaultFontNames defaultNames;
  886. Font newFont (font);
  887. const String& faceName = font.getTypefaceName();
  888. if (faceName == getDefaultSansSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSans);
  889. else if (faceName == getDefaultSerifFontName()) newFont.setTypefaceName (defaultNames.defaultSerif);
  890. else if (faceName == getDefaultMonospacedFontName()) newFont.setTypefaceName (defaultNames.defaultFixed);
  891. if (font.getTypefaceStyle() == getDefaultStyle())
  892. newFont.setTypefaceStyle ("Regular");
  893. #if JUCE_IOS && ! JUCE_CORETEXT_AVAILABLE
  894. // Fonts style names on Cocoa Touch are unusual like "Arial-BoldMT"
  895. // No font will be found for the style of "Regular" so we must modify the style
  896. if (newFont.getTypefaceStyle() == "Regular")
  897. newFont.setTypefaceStyle (faceName);
  898. #endif
  899. return Typeface::createSystemTypefaceFor (newFont);
  900. }
  901. bool TextLayout::createNativeLayout (const AttributedString& text)
  902. {
  903. #if JUCE_CORETEXT_AVAILABLE
  904. CoreTextTypeLayout::createLayout (*this, text);
  905. return true;
  906. #else
  907. (void) text;
  908. return false;
  909. #endif
  910. }