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.

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