/* ============================================================================== This file is part of the JUCE library - "Jules' Utility Class Extensions" Copyright 2004-7 by Raw Material Software ltd. ------------------------------------------------------------------------------ JUCE can be redistributed and/or modified under the terms of the GNU General Public License, as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. JUCE is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with JUCE; if not, visit www.gnu.org/licenses or write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ------------------------------------------------------------------------------ If you'd like to release a closed-source product which uses JUCE, commercial licenses are also available: visit www.rawmaterialsoftware.com/juce for more information. ============================================================================== */ // (This file gets included by juce_mac_NativeCode.mm, rather than being // compiled on its own). #ifdef JUCE_INCLUDED_FILE //============================================================================== class FontHelper { NSFont* font; public: String name; bool isBold, isItalic, needsItalicTransform; float fontSize, totalSize, ascent; int refCount; NSMutableDictionary* attributes; FontHelper (const String& name_, const bool bold_, const bool italic_, const float size_) : font (0), name (name_), isBold (bold_), isItalic (italic_), needsItalicTransform (false), fontSize (size_), refCount (1) { attributes = [[NSMutableDictionary dictionaryWithObject: [NSNumber numberWithInt: 0] forKey: NSLigatureAttributeName] retain]; font = [NSFont fontWithName: juceStringToNS (name_) size: size_]; if (italic_) { NSFont* newFont = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSItalicFontMask]; if (newFont == font) needsItalicTransform = true; // couldn't find a proper italic version, so fake it with a transform.. font = newFont; } if (bold_) font = [[NSFontManager sharedFontManager] convertFont: font toHaveTrait: NSBoldFontMask]; [font retain]; ascent = fabsf ([font ascender]); totalSize = ascent + fabsf ([font descender]); } ~FontHelper() { [font release]; [attributes release]; } bool getPathAndKerning (const juce_wchar char1, const juce_wchar char2, Path* path, float& kerning, float* ascent, float* descent) { const ScopedAutoReleasePool pool; if (font == 0 || ! [[font coveredCharacterSet] longCharacterIsMember: (UTF32Char) char1]) return false; String chars; chars << ' ' << char1 << char2; NSTextStorage* textStorage = [[[NSTextStorage alloc] initWithString: juceStringToNS (chars) attributes: attributes] autorelease]; NSLayoutManager* layoutManager = [[[NSLayoutManager alloc] init] autorelease]; NSTextContainer* textContainer = [[[NSTextContainer alloc] init] autorelease]; [layoutManager addTextContainer: textContainer]; [textStorage addLayoutManager: layoutManager]; [textStorage setFont: font]; unsigned int glyphIndex = [layoutManager glyphRangeForCharacterRange: NSMakeRange (1, 1) actualCharacterRange: 0].location; NSPoint p1 = [layoutManager locationForGlyphAtIndex: glyphIndex]; NSPoint p2 = [layoutManager locationForGlyphAtIndex: glyphIndex + 1]; kerning = p2.x - p1.x; if (ascent != 0) *ascent = this->ascent; if (descent != 0) *descent = fabsf ([font descender]); if (path != 0) { NSBezierPath* bez = [NSBezierPath bezierPath]; [bez moveToPoint: NSMakePoint (0, 0)]; [bez appendBezierPathWithGlyph: [layoutManager glyphAtIndex: glyphIndex] inFont: font]; for (int i = 0; i < [bez elementCount]; ++i) { NSPoint p[3]; switch ([bez elementAtIndex: i associatedPoints: p]) { case NSMoveToBezierPathElement: path->startNewSubPath (p[0].x, -p[0].y); break; case NSLineToBezierPathElement: path->lineTo (p[0].x, -p[0].y); break; case NSCurveToBezierPathElement: path->cubicTo (p[0].x, -p[0].y, p[1].x, -p[1].y, p[2].x, -p[2].y); break; case NSClosePathBezierPathElement: path->closeSubPath(); break; default: jassertfalse break; } } if (needsItalicTransform) path->applyTransform (AffineTransform::identity.sheared (-0.15, 0)); } return kerning != 0; } juce_wchar getDefaultChar() { return 0; } }; //============================================================================== class FontHelperCache : public Timer, public DeletedAtShutdown { VoidArray cache; public: FontHelperCache() { } ~FontHelperCache() { for (int i = cache.size(); --i >= 0;) { FontHelper* const f = (FontHelper*) cache.getUnchecked(i); delete f; } clearSingletonInstance(); } FontHelper* getFont (const String& name, const bool bold, const bool italic, const float size = 1024) { for (int i = cache.size(); --i >= 0;) { FontHelper* const f = (FontHelper*) cache.getUnchecked(i); if (f->name == name && f->isBold == bold && f->isItalic == italic && f->fontSize == size) { f->refCount++; return f; } } FontHelper* const f = new FontHelper (name, bold, italic, size); cache.add (f); return f; } void releaseFont (FontHelper* f) { for (int i = cache.size(); --i >= 0;) { FontHelper* const f2 = (FontHelper*) cache.getUnchecked(i); if (f == f2) { f->refCount--; if (f->refCount == 0) startTimer (5000); break; } } } void timerCallback() { stopTimer(); for (int i = cache.size(); --i >= 0;) { FontHelper* const f = (FontHelper*) cache.getUnchecked(i); if (f->refCount == 0) { cache.remove (i); delete f; } } if (cache.size() == 0) delete this; } juce_DeclareSingleton_SingleThreaded_Minimal (FontHelperCache) }; juce_ImplementSingleton_SingleThreaded (FontHelperCache) //============================================================================== void Typeface::initialiseTypefaceCharacteristics (const String& fontName, bool bold, bool italic, bool addAllGlyphsToFont) throw() { // This method is only safe to be called from the normal UI thread.. jassert (MessageManager::getInstance()->isThisTheMessageThread()); FontHelper* const helper = FontHelperCache::getInstance() ->getFont (fontName, bold, italic); clear(); setAscent (helper->ascent / helper->totalSize); setName (fontName); setDefaultCharacter (helper->getDefaultChar()); setBold (bold); setItalic (italic); if (addAllGlyphsToFont) { //xxx jassertfalse } FontHelperCache::getInstance()->releaseFont (helper); } bool Typeface::findAndAddSystemGlyph (juce_wchar character) throw() { // This method is only safe to be called from the normal UI thread.. jassert (MessageManager::getInstance()->isThisTheMessageThread()); FontHelper* const helper = FontHelperCache::getInstance() ->getFont (getName(), isBold(), isItalic()); Path path; float width; bool foundOne = false; if (helper->getPathAndKerning (character, T('I'), &path, width, 0, 0)) { path.applyTransform (AffineTransform::scale (1.0f / helper->totalSize, 1.0f / helper->totalSize)); addGlyph (character, path, width / helper->totalSize); for (int i = 0; i < glyphs.size(); ++i) { const TypefaceGlyphInfo* const g = (const TypefaceGlyphInfo*) glyphs.getUnchecked(i); float kerning; if (helper->getPathAndKerning (character, g->getCharacter(), 0, kerning, 0, 0)) { kerning = (kerning - width) / helper->totalSize; if (kerning != 0) addKerningPair (character, g->getCharacter(), kerning); } if (helper->getPathAndKerning (g->getCharacter(), character, 0, kerning, 0, 0)) { kerning = kerning / helper->totalSize - g->width; if (kerning != 0) addKerningPair (g->getCharacter(), character, kerning); } } foundOne = true; } FontHelperCache::getInstance()->releaseFont (helper); return foundOne; } //============================================================================== const StringArray Font::findAllTypefaceNames() throw() { StringArray names; const ScopedAutoReleasePool pool; NSArray* fonts = [[NSFontManager sharedFontManager] availableFontFamilies]; for (int i = 0; i < [fonts count]; ++i) names.add (nsStringToJuce ((NSString*) [fonts objectAtIndex: i])); names.sort (true); return names; } void Typeface::getDefaultFontNames (String& defaultSans, String& defaultSerif, String& defaultFixed) throw() { defaultSans = "Lucida Grande"; defaultSerif = "Times New Roman"; defaultFixed = "Monaco"; } #endif