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.

1188 lines
46KB

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