Add remaining files
[juce-lv2.git] / juce / source / src / native / mac / juce_mac_Fonts.mm
blob1adc5ae67f42551f7b862c103b2ae12bad782508
1 /*\r
2   ==============================================================================\r
3 \r
4    This file is part of the JUCE library - "Jules' Utility Class Extensions"\r
5    Copyright 2004-11 by Raw Material Software Ltd.\r
6 \r
7   ------------------------------------------------------------------------------\r
8 \r
9    JUCE can be redistributed and/or modified under the terms of the GNU General\r
10    Public License (Version 2), as published by the Free Software Foundation.\r
11    A copy of the license is included in the JUCE distribution, or can be found\r
12    online at www.gnu.org/licenses.\r
14    JUCE is distributed in the hope that it will be useful, but WITHOUT ANY\r
15    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR\r
16    A PARTICULAR PURPOSE.  See the GNU General Public License for more details.\r
18   ------------------------------------------------------------------------------\r
20    To release a closed-source product which uses JUCE, commercial licenses are\r
21    available: visit www.rawmaterialsoftware.com/juce for more information.\r
23   ==============================================================================\r
24 */\r
26 // (This file gets included by juce_mac_NativeCode.mm, rather than being\r
27 // compiled on its own).\r
28 #if JUCE_INCLUDED_FILE\r
30 #if (JUCE_MAC && defined (MAC_OS_X_VERSION_10_5) && MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5 \\r
31         && MAC_OS_X_VERSION_MAX_ALLOWED > MAC_OS_X_VERSION_10_5) \\r
32      || (JUCE_IOS && defined (__IPHONE_3_0) && __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_3_2)\r
33  #define JUCE_CORETEXT_AVAILABLE 1\r
34 #endif\r
36 #if JUCE_CORETEXT_AVAILABLE\r
38 //==============================================================================\r
39 class MacTypeface  : public Typeface\r
40 {\r
41 public:\r
42     //==============================================================================\r
43     MacTypeface (const Font& font)\r
44         : Typeface (font.getTypefaceName()),\r
45           fontRef (nullptr),\r
46           fontHeightToCGSizeFactor (1.0f),\r
47           renderingTransform (CGAffineTransformIdentity),\r
48           ctFontRef (nullptr),\r
49           attributedStringAtts (nullptr),\r
50           ascent (0.0f),\r
51           unitsToHeightScaleFactor (0.0f)\r
52     {\r
53         JUCE_AUTORELEASEPOOL\r
54         CFStringRef cfName = PlatformUtilities::juceStringToCFString (font.getTypefaceName());\r
55         ctFontRef = CTFontCreateWithName (cfName, 1024, nullptr);\r
56         CFRelease (cfName);\r
58         if (ctFontRef != nullptr)\r
59         {\r
60             bool needsItalicTransform = false;\r
62             if (font.isItalic())\r
63             {\r
64                 CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr,\r
65                                                                         kCTFontItalicTrait, kCTFontItalicTrait);\r
67                 if (newFont != nullptr)\r
68                 {\r
69                     CFRelease (ctFontRef);\r
70                     ctFontRef = newFont;\r
71                 }\r
72                 else\r
73                 {\r
74                     needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform..\r
75                 }\r
76             }\r
78             if (font.isBold())\r
79             {\r
80                 CTFontRef newFont = CTFontCreateCopyWithSymbolicTraits (ctFontRef, 0.0f, nullptr,\r
81                                                                         kCTFontBoldTrait, kCTFontBoldTrait);\r
82                 if (newFont != nullptr)\r
83                 {\r
84                     CFRelease (ctFontRef);\r
85                     ctFontRef = newFont;\r
86                 }\r
87             }\r
89             ascent = std::abs ((float) CTFontGetAscent (ctFontRef));\r
90             const float totalSize = ascent + std::abs ((float) CTFontGetDescent (ctFontRef));\r
91             ascent /= totalSize;\r
93             pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize);\r
95             if (needsItalicTransform)\r
96             {\r
97                 pathTransform = pathTransform.sheared (-0.15f, 0.0f);\r
98                 renderingTransform.c = 0.15f;\r
99             }\r
101             fontRef = CTFontCopyGraphicsFont (ctFontRef, nullptr);\r
103             const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef));\r
104             const float ctTotalHeight = abs (CTFontGetAscent (ctFontRef)) + abs (CTFontGetDescent (ctFontRef));\r
105             unitsToHeightScaleFactor = 1.0f / ctTotalHeight;\r
106             fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight;\r
108             const short zero = 0;\r
109             CFNumberRef numberRef = CFNumberCreate (0, kCFNumberShortType, &zero);\r
111             CFStringRef keys[] = { kCTFontAttributeName, kCTLigatureAttributeName };\r
112             CFTypeRef values[] = { ctFontRef, numberRef };\r
113             attributedStringAtts = CFDictionaryCreate (nullptr, (const void**) &keys, (const void**) &values, numElementsInArray (keys),\r
114                                                        &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);\r
115             CFRelease (numberRef);\r
116         }\r
117     }\r
119     ~MacTypeface()\r
120     {\r
121         if (attributedStringAtts != nullptr)\r
122             CFRelease (attributedStringAtts);\r
124         if (fontRef != nullptr)\r
125             CGFontRelease (fontRef);\r
127         if (ctFontRef != nullptr)\r
128             CFRelease (ctFontRef);\r
129     }\r
131     float getAscent() const     { return ascent; }\r
132     float getDescent() const    { return 1.0f - ascent; }\r
134     float getStringWidth (const String& text)\r
135     {\r
136         float x = 0;\r
138         if (ctFontRef != nullptr && text.isNotEmpty())\r
139         {\r
140             CFStringRef cfText = PlatformUtilities::juceStringToCFString (text);\r
141             CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);\r
142             CFRelease (cfText);\r
144             CTLineRef line = CTLineCreateWithAttributedString (attribString);\r
145             CFArrayRef runArray = CTLineGetGlyphRuns (line);\r
147             for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)\r
148             {\r
149                 CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);\r
150                 CFIndex length = CTRunGetGlyphCount (run);\r
151                 HeapBlock <CGSize> advances (length);\r
152                 CTRunGetAdvances (run, CFRangeMake (0, 0), advances);\r
154                 for (int j = 0; j < length; ++j)\r
155                     x += (float) advances[j].width;\r
156             }\r
158             CFRelease (line);\r
159             CFRelease (attribString);\r
161             x *= unitsToHeightScaleFactor;\r
162         }\r
164         return x;\r
165     }\r
167     void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)\r
168     {\r
169         xOffsets.add (0);\r
171         if (ctFontRef != nullptr && text.isNotEmpty())\r
172         {\r
173             float x = 0;\r
175             CFStringRef cfText = PlatformUtilities::juceStringToCFString (text);\r
176             CFAttributedStringRef attribString = CFAttributedStringCreate (kCFAllocatorDefault, cfText, attributedStringAtts);\r
177             CFRelease (cfText);\r
179             CTLineRef line = CTLineCreateWithAttributedString (attribString);\r
180             CFArrayRef runArray = CTLineGetGlyphRuns (line);\r
182             for (CFIndex i = 0; i < CFArrayGetCount (runArray); ++i)\r
183             {\r
184                 CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (runArray, i);\r
185                 CFIndex length = CTRunGetGlyphCount (run);\r
186                 HeapBlock <CGSize> advances (length);\r
187                 CTRunGetAdvances (run, CFRangeMake (0, 0), advances);\r
188                 HeapBlock <CGGlyph> glyphs (length);\r
189                 CTRunGetGlyphs (run, CFRangeMake (0, 0), glyphs);\r
191                 for (int j = 0; j < length; ++j)\r
192                 {\r
193                     x += (float) advances[j].width;\r
194                     xOffsets.add (x * unitsToHeightScaleFactor);\r
195                     resultGlyphs.add (glyphs[j]);\r
196                 }\r
197             }\r
199             CFRelease (line);\r
200             CFRelease (attribString);\r
201         }\r
202     }\r
204     EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)\r
205     {\r
206         Path path;\r
208         if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())\r
209             return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),\r
210                                   path, transform);\r
212         return nullptr;\r
213     }\r
215     bool getOutlineForGlyph (int glyphNumber, Path& path)\r
216     {\r
217         jassert (path.isEmpty());  // we might need to apply a transform to the path, so this must be empty\r
219         CGPathRef pathRef = CTFontCreatePathForGlyph (ctFontRef, (CGGlyph) glyphNumber, &renderingTransform);\r
220         if (pathRef == 0)\r
221             return false;\r
223         CGPathApply (pathRef, &path, pathApplier);\r
224         CFRelease (pathRef);\r
226         if (! pathTransform.isIdentity())\r
227             path.applyTransform (pathTransform);\r
229         return true;\r
230     }\r
232     //==============================================================================\r
233     CGFontRef fontRef;\r
235     float fontHeightToCGSizeFactor;\r
236     CGAffineTransform renderingTransform;\r
238 private:\r
239     CTFontRef ctFontRef;\r
240     CFDictionaryRef attributedStringAtts;\r
241     float ascent, unitsToHeightScaleFactor;\r
242     AffineTransform pathTransform;\r
244     static void pathApplier (void* info, const CGPathElement* const element)\r
245     {\r
246         Path& path = *static_cast<Path*> (info);\r
247         const CGPoint* const p = element->points;\r
249         switch (element->type)\r
250         {\r
251             case kCGPathElementMoveToPoint:         path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;\r
252             case kCGPathElementAddLineToPoint:      path.lineTo          ((float) p[0].x, (float) -p[0].y); break;\r
253             case kCGPathElementAddQuadCurveToPoint: path.quadraticTo     ((float) p[0].x, (float) -p[0].y,\r
254                                                                           (float) p[1].x, (float) -p[1].y); break;\r
255             case kCGPathElementAddCurveToPoint:     path.cubicTo         ((float) p[0].x, (float) -p[0].y,\r
256                                                                           (float) p[1].x, (float) -p[1].y,\r
257                                                                           (float) p[2].x, (float) -p[2].y); break;\r
258             case kCGPathElementCloseSubpath:        path.closeSubPath(); break;\r
259             default:                                jassertfalse; break;\r
260         }\r
261     }\r
263     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MacTypeface);\r
264 };\r
266 #else\r
268 //==============================================================================\r
269 // The stuff that follows is a mash-up that supports pre-OSX 10.5 and pre-iOS 3.2 APIs.\r
270 // (Hopefully all of this can be ditched at some point in the future).\r
272 //==============================================================================\r
273 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5\r
274   #define SUPPORT_10_4_FONTS 1\r
275   #define NEW_CGFONT_FUNCTIONS_UNAVAILABLE (CGFontCreateWithFontName == 0)\r
277   #if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5\r
278     #define SUPPORT_ONLY_10_4_FONTS 1\r
279   #endif\r
281   END_JUCE_NAMESPACE\r
282   @interface NSFont (PrivateHack)\r
283     - (NSGlyph) _defaultGlyphForChar: (unichar) theChar;\r
284   @end\r
285   BEGIN_JUCE_NAMESPACE\r
286 #endif\r
288 //==============================================================================\r
289 class MacTypeface  : public Typeface\r
291 public:\r
292     //==============================================================================\r
293     MacTypeface (const Font& font)\r
294         : Typeface (font.getTypefaceName())\r
295     {\r
296         JUCE_AUTORELEASEPOOL\r
297         renderingTransform = CGAffineTransformIdentity;\r
299         bool needsItalicTransform = false;\r
301 #if JUCE_IOS\r
302         NSString* fontName = juceStringToNS (font.getTypefaceName());\r
304         if (font.isItalic() || font.isBold())\r
305         {\r
306             NSArray* familyFonts = [UIFont fontNamesForFamilyName: juceStringToNS (font.getTypefaceName())];\r
308             for (NSString* i in familyFonts)\r
309             {\r
310                 const String fn (nsStringToJuce (i));\r
311                 const String afterDash (fn.fromFirstOccurrenceOf ("-", false, false));\r
313                 const bool probablyBold = afterDash.containsIgnoreCase ("bold") || fn.endsWithIgnoreCase ("bold");\r
314                 const bool probablyItalic = afterDash.containsIgnoreCase ("oblique")\r
315                                              || afterDash.containsIgnoreCase ("italic")\r
316                                              || fn.endsWithIgnoreCase ("oblique")\r
317                                              || fn.endsWithIgnoreCase ("italic");\r
319                 if (probablyBold == font.isBold()\r
320                      && probablyItalic == font.isItalic())\r
321                 {\r
322                     fontName = i;\r
323                     needsItalicTransform = false;\r
324                     break;\r
325                 }\r
326                 else if (probablyBold && (! probablyItalic) && probablyBold == font.isBold())\r
327                 {\r
328                     fontName = i;\r
329                     needsItalicTransform = true; // not ideal, so carry on in case we find a better one\r
330                 }\r
331             }\r
333             if (needsItalicTransform)\r
334                 renderingTransform.c = 0.15f;\r
335         }\r
337         fontRef = CGFontCreateWithFontName ((CFStringRef) fontName);\r
339         const int ascender = abs (CGFontGetAscent (fontRef));\r
340         const float totalHeight = ascender + abs (CGFontGetDescent (fontRef));\r
341         ascent = ascender / totalHeight;\r
342         unitsToHeightScaleFactor = 1.0f / totalHeight;\r
343         fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / totalHeight;\r
344 #else\r
345         nsFont = [NSFont fontWithName: juceStringToNS (font.getTypefaceName()) size: 1024];\r
347         if (font.isItalic())\r
348         {\r
349             NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: nsFont\r
350                                                                  toHaveTrait: NSItalicFontMask];\r
352             if (newFont == nsFont)\r
353                 needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform..\r
355             nsFont = newFont;\r
356         }\r
358         if (font.isBold())\r
359             nsFont = [[NSFontManager sharedFontManager] convertFont: nsFont toHaveTrait: NSBoldFontMask];\r
361         [nsFont retain];\r
363         ascent = std::abs ((float) [nsFont ascender]);\r
364         float totalSize = ascent + std::abs ((float) [nsFont descender]);\r
365         ascent /= totalSize;\r
367         pathTransform = AffineTransform::identity.scale (1.0f / totalSize, 1.0f / totalSize);\r
369         if (needsItalicTransform)\r
370         {\r
371             pathTransform = pathTransform.sheared (-0.15f, 0.0f);\r
372             renderingTransform.c = 0.15f;\r
373         }\r
375       #if SUPPORT_ONLY_10_4_FONTS\r
376         ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);\r
378         if (atsFont == 0)\r
379             atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);\r
381         fontRef = CGFontCreateWithPlatformFont (&atsFont);\r
383         const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]);\r
384         unitsToHeightScaleFactor = 1.0f / totalHeight;\r
385         fontHeightToCGSizeFactor = 1024.0f / totalHeight;\r
386       #else\r
387        #if SUPPORT_10_4_FONTS\r
388         if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)\r
389         {\r
390             ATSFontRef atsFont = ATSFontFindFromName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);\r
392             if (atsFont == 0)\r
393                 atsFont = ATSFontFindFromPostScriptName ((CFStringRef) [nsFont fontName], kATSOptionFlagsDefault);\r
395             fontRef = CGFontCreateWithPlatformFont (&atsFont);\r
397             const float totalHeight = std::abs ([nsFont ascender]) + std::abs ([nsFont descender]);\r
398             unitsToHeightScaleFactor = 1.0f / totalHeight;\r
399             fontHeightToCGSizeFactor = 1024.0f / totalHeight;\r
400         }\r
401         else\r
402        #endif\r
403         {\r
404             fontRef = CGFontCreateWithFontName ((CFStringRef) [nsFont fontName]);\r
406             const int totalHeight = abs (CGFontGetAscent (fontRef)) + abs (CGFontGetDescent (fontRef));\r
407             unitsToHeightScaleFactor = 1.0f / totalHeight;\r
408             fontHeightToCGSizeFactor = CGFontGetUnitsPerEm (fontRef) / (float) totalHeight;\r
409         }\r
410       #endif\r
412 #endif\r
413     }\r
415     ~MacTypeface()\r
416     {\r
417        #if ! JUCE_IOS\r
418         [nsFont release];\r
419        #endif\r
421         if (fontRef != 0)\r
422             CGFontRelease (fontRef);\r
423     }\r
425     float getAscent() const\r
426     {\r
427         return ascent;\r
428     }\r
430     float getDescent() const\r
431     {\r
432         return 1.0f - ascent;\r
433     }\r
435     float getStringWidth (const String& text)\r
436     {\r
437         if (fontRef == 0 || text.isEmpty())\r
438             return 0;\r
440         const int length = text.length();\r
441         HeapBlock <CGGlyph> glyphs;\r
442         createGlyphsForString (text.getCharPointer(), length, glyphs);\r
444         float x = 0;\r
446 #if SUPPORT_ONLY_10_4_FONTS\r
447         HeapBlock <NSSize> advances (length);\r
448         [nsFont getAdvancements: advances forGlyphs: reinterpret_cast <NSGlyph*> (glyphs.getData()) count: length];\r
450         for (int i = 0; i < length; ++i)\r
451             x += advances[i].width;\r
452 #else\r
453        #if SUPPORT_10_4_FONTS\r
454         if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)\r
455         {\r
456             HeapBlock <NSSize> advances (length);\r
457             [nsFont getAdvancements: advances forGlyphs: reinterpret_cast<NSGlyph*> (glyphs.getData()) count: length];\r
459             for (int i = 0; i < length; ++i)\r
460                 x += advances[i].width;\r
461         }\r
462         else\r
463        #endif\r
464         {\r
465             HeapBlock <int> advances (length);\r
467             if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))\r
468                 for (int i = 0; i < length; ++i)\r
469                     x += advances[i];\r
470         }\r
471 #endif\r
473         return x * unitsToHeightScaleFactor;\r
474     }\r
476     void getGlyphPositions (const String& text, Array <int>& resultGlyphs, Array <float>& xOffsets)\r
477     {\r
478         xOffsets.add (0);\r
480         if (fontRef == 0 || text.isEmpty())\r
481             return;\r
483         const int length = text.length();\r
484         HeapBlock <CGGlyph> glyphs;\r
485         createGlyphsForString (text.getCharPointer(), length, glyphs);\r
487 #if SUPPORT_ONLY_10_4_FONTS\r
488         HeapBlock <NSSize> advances (length);\r
489         [nsFont getAdvancements: advances forGlyphs: reinterpret_cast <NSGlyph*> (glyphs.getData()) count: length];\r
491         int x = 0;\r
492         for (int i = 0; i < length; ++i)\r
493         {\r
494             x += advances[i].width;\r
495             xOffsets.add (x * unitsToHeightScaleFactor);\r
496             resultGlyphs.add (reinterpret_cast <NSGlyph*> (glyphs.getData())[i]);\r
497         }\r
499 #else\r
500        #if SUPPORT_10_4_FONTS\r
501         if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)\r
502         {\r
503             HeapBlock <NSSize> advances (length);\r
504             NSGlyph* const nsGlyphs = reinterpret_cast<NSGlyph*> (glyphs.getData());\r
505             [nsFont getAdvancements: advances forGlyphs: nsGlyphs count: length];\r
507             float x = 0;\r
508             for (int i = 0; i < length; ++i)\r
509             {\r
510                 x += advances[i].width;\r
511                 xOffsets.add (x * unitsToHeightScaleFactor);\r
512                 resultGlyphs.add (nsGlyphs[i]);\r
513             }\r
514         }\r
515         else\r
516        #endif\r
517         {\r
518             HeapBlock <int> advances (length);\r
520             if (CGFontGetGlyphAdvances (fontRef, glyphs, length, advances))\r
521             {\r
522                 int x = 0;\r
523                 for (int i = 0; i < length; ++i)\r
524                 {\r
525                     x += advances [i];\r
526                     xOffsets.add (x * unitsToHeightScaleFactor);\r
527                     resultGlyphs.add (glyphs[i]);\r
528                 }\r
529             }\r
530         }\r
531 #endif\r
532     }\r
534     EdgeTable* getEdgeTableForGlyph (int glyphNumber, const AffineTransform& transform)\r
535     {\r
536         Path path;\r
538         if (getOutlineForGlyph (glyphNumber, path) && ! path.isEmpty())\r
539             return new EdgeTable (path.getBoundsTransformed (transform).getSmallestIntegerContainer().expanded (1, 0),\r
540                                   path, transform);\r
542         return nullptr;\r
543     }\r
545     bool getOutlineForGlyph (int glyphNumber, Path& path)\r
546     {\r
547        #if JUCE_IOS\r
548         return false;\r
549        #else\r
550         if (nsFont == nil)\r
551             return false;\r
553         // we might need to apply a transform to the path, so it mustn't have anything else in it\r
554         jassert (path.isEmpty());\r
556         JUCE_AUTORELEASEPOOL\r
558         NSBezierPath* bez = [NSBezierPath bezierPath];\r
559         [bez moveToPoint: NSMakePoint (0, 0)];\r
560         [bez appendBezierPathWithGlyph: (NSGlyph) glyphNumber\r
561                                 inFont: nsFont];\r
563         for (int i = 0; i < [bez elementCount]; ++i)\r
564         {\r
565             NSPoint p[3];\r
566             switch ([bez elementAtIndex: i associatedPoints: p])\r
567             {\r
568                 case NSMoveToBezierPathElement:     path.startNewSubPath ((float) p[0].x, (float) -p[0].y); break;\r
569                 case NSLineToBezierPathElement:     path.lineTo ((float) p[0].x, (float) -p[0].y); break;\r
570                 case NSCurveToBezierPathElement:    path.cubicTo ((float) p[0].x, (float) -p[0].y,\r
571                                                                   (float) p[1].x, (float) -p[1].y,\r
572                                                                   (float) p[2].x, (float) -p[2].y); break;\r
573                 case NSClosePathBezierPathElement:  path.closeSubPath(); break;\r
574                 default:                            jassertfalse; break;\r
575             }\r
576         }\r
578         path.applyTransform (pathTransform);\r
579         return true;\r
580        #endif\r
581     }\r
583     //==============================================================================\r
584     CGFontRef fontRef;\r
585     float fontHeightToCGSizeFactor;\r
586     CGAffineTransform renderingTransform;\r
588 private:\r
589     float ascent, unitsToHeightScaleFactor;\r
591    #if ! JUCE_IOS\r
592     NSFont* nsFont;\r
593     AffineTransform pathTransform;\r
594    #endif\r
596     void createGlyphsForString (String::CharPointerType text, const int length, HeapBlock <CGGlyph>& glyphs)\r
597     {\r
598       #if SUPPORT_10_4_FONTS\r
599        #if ! SUPPORT_ONLY_10_4_FONTS\r
600         if (NEW_CGFONT_FUNCTIONS_UNAVAILABLE)\r
601        #endif\r
602         {\r
603             glyphs.malloc (sizeof (NSGlyph) * length, 1);\r
604             NSGlyph* const nsGlyphs = reinterpret_cast<NSGlyph*> (glyphs.getData());\r
606             for (int i = 0; i < length; ++i)\r
607                 nsGlyphs[i] = (NSGlyph) [nsFont _defaultGlyphForChar: text.getAndAdvance()];\r
609             return;\r
610         }\r
611       #endif\r
613        #if ! SUPPORT_ONLY_10_4_FONTS\r
614         if (charToGlyphMapper == nullptr)\r
615             charToGlyphMapper = new CharToGlyphMapper (fontRef);\r
617         glyphs.malloc (length);\r
619         for (int i = 0; i < length; ++i)\r
620             glyphs[i] = (CGGlyph) charToGlyphMapper->getGlyphForCharacter (text.getAndAdvance());\r
621        #endif\r
622     }\r
624 #if ! SUPPORT_ONLY_10_4_FONTS\r
625     // Reads a CGFontRef's character map table to convert unicode into glyph numbers\r
626     class CharToGlyphMapper\r
627     {\r
628     public:\r
629         CharToGlyphMapper (CGFontRef fontRef)\r
630             : segCount (0), endCode (0), startCode (0), idDelta (0),\r
631               idRangeOffset (0), glyphIndexes (0)\r
632         {\r
633             CFDataRef cmapTable = CGFontCopyTableForTag (fontRef, 'cmap');\r
635             if (cmapTable != 0)\r
636             {\r
637                 const int numSubtables = getValue16 (cmapTable, 2);\r
639                 for (int i = 0; i < numSubtables; ++i)\r
640                 {\r
641                     if (getValue16 (cmapTable, i * 8 + 4) == 0) // check for platform ID of 0\r
642                     {\r
643                         const int offset = getValue32 (cmapTable, i * 8 + 8);\r
645                         if (getValue16 (cmapTable, offset) == 4) // check that it's format 4..\r
646                         {\r
647                             const int length = getValue16 (cmapTable, offset + 2);\r
648                             const int segCountX2 =  getValue16 (cmapTable, offset + 6);\r
649                             segCount = segCountX2 / 2;\r
650                             const int endCodeOffset = offset + 14;\r
651                             const int startCodeOffset = endCodeOffset + 2 + segCountX2;\r
652                             const int idDeltaOffset = startCodeOffset + segCountX2;\r
653                             const int idRangeOffsetOffset = idDeltaOffset + segCountX2;\r
654                             const int glyphIndexesOffset = idRangeOffsetOffset + segCountX2;\r
656                             endCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + endCodeOffset, segCountX2);\r
657                             startCode = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + startCodeOffset, segCountX2);\r
658                             idDelta = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idDeltaOffset, segCountX2);\r
659                             idRangeOffset = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + idRangeOffsetOffset, segCountX2);\r
660                             glyphIndexes = CFDataCreate (kCFAllocatorDefault, CFDataGetBytePtr (cmapTable) + glyphIndexesOffset, offset + length - glyphIndexesOffset);\r
661                         }\r
663                         break;\r
664                     }\r
665                 }\r
667                 CFRelease (cmapTable);\r
668             }\r
669         }\r
671         ~CharToGlyphMapper()\r
672         {\r
673             if (endCode != 0)\r
674             {\r
675                 CFRelease (endCode);\r
676                 CFRelease (startCode);\r
677                 CFRelease (idDelta);\r
678                 CFRelease (idRangeOffset);\r
679                 CFRelease (glyphIndexes);\r
680             }\r
681         }\r
683         int getGlyphForCharacter (const juce_wchar c) const\r
684         {\r
685             for (int i = 0; i < segCount; ++i)\r
686             {\r
687                 if (getValue16 (endCode, i * 2) >= c)\r
688                 {\r
689                     const int start = getValue16 (startCode, i * 2);\r
690                     if (start > c)\r
691                         break;\r
693                     const int delta = getValue16 (idDelta, i * 2);\r
694                     const int rangeOffset = getValue16 (idRangeOffset, i * 2);\r
696                     if (rangeOffset == 0)\r
697                         return delta + c;\r
698                     else\r
699                         return getValue16 (glyphIndexes, 2 * ((rangeOffset / 2) + (c - start) - (segCount - i)));\r
700                 }\r
701             }\r
703             // If we failed to find it "properly", this dodgy fall-back seems to do the trick for most fonts!\r
704             return jmax (-1, (int) c - 29);\r
705         }\r
707     private:\r
708         int segCount;\r
709         CFDataRef endCode, startCode, idDelta, idRangeOffset, glyphIndexes;\r
711         static uint16 getValue16 (CFDataRef data, const int index)\r
712         {\r
713             return CFSwapInt16BigToHost (*(UInt16*) (CFDataGetBytePtr (data) + index));\r
714         }\r
716         static uint32 getValue32 (CFDataRef data, const int index)\r
717         {\r
718             return CFSwapInt32BigToHost (*(UInt32*) (CFDataGetBytePtr (data) + index));\r
719         }\r
720     };\r
722     ScopedPointer <CharToGlyphMapper> charToGlyphMapper;\r
723 #endif\r
725     JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MacTypeface);\r
726 };\r
728 #endif\r
730 //==============================================================================\r
731 Typeface::Ptr Typeface::createSystemTypefaceFor (const Font& font)\r
733     return new MacTypeface (font);\r
736 StringArray Font::findAllTypefaceNames()\r
738     StringArray names;\r
740     JUCE_AUTORELEASEPOOL\r
742    #if JUCE_IOS\r
743     NSArray* fonts = [UIFont familyNames];\r
744    #else\r
745     NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies];\r
746    #endif\r
748     for (unsigned int i = 0; i < [fonts count]; ++i)\r
749         names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i]));\r
751     names.sort (true);\r
752     return names;\r
755 void Font::getPlatformDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed, String& defaultFallback)\r
757    #if JUCE_IOS\r
758     defaultSans  = "Helvetica";\r
759     defaultSerif = "Times New Roman";\r
760     defaultFixed = "Courier New";\r
761    #else\r
762     defaultSans  = "Lucida Grande";\r
763     defaultSerif = "Times New Roman";\r
764     defaultFixed = "Monaco";\r
765    #endif\r
767     defaultFallback = "Arial Unicode MS";\r
770 #endif\r