1 /*
   2  * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #import <JavaNativeFoundation/JavaNativeFoundation.h>
  27 
  28 #import "java_awt_Font.h"
  29 #import "sun_awt_PlatformFont.h"
  30 #import "sun_awt_FontDescriptor.h"
  31 #import "sun_font_CFont.h"
  32 #import "sun_font_CFontManager.h"
  33 
  34 #import "AWTFont.h"
  35 #import "AWTStrike.h"
  36 #import "CoreTextSupport.h"
  37 
  38 
  39 #define DEBUG
  40 
  41 @implementation AWTFont
  42 
  43 - (id) initWithFont:(NSFont *)font isFakeItalic:(BOOL)isFakeItalic {
  44     self = [super init];
  45     if (self) {
  46         fIsFakeItalic = isFakeItalic;
  47         fFont = [font retain];
  48         fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
  49     }
  50     return self;
  51 }
  52 
  53 - (void) dealloc {
  54     [fFont release];
  55     fFont = nil;
  56 
  57     if (fNativeCGFont) {
  58         CGFontRelease(fNativeCGFont);
  59     fNativeCGFont = NULL;
  60     }
  61 
  62     [super dealloc];
  63 }
  64 
  65 - (void) finalize {
  66     if (fNativeCGFont) {
  67         CGFontRelease(fNativeCGFont);
  68     fNativeCGFont = NULL;
  69     }
  70     [super finalize];
  71 }
  72 
  73 + (AWTFont *) awtFontForName:(NSString *)name
  74                        style:(int)style
  75                 isFakeItalic:(BOOL)isFakeItalic
  76 {
  77     // create font with family & size
  78     NSFont *nsFont = [NSFont fontWithName:name size:1.0];
  79 
  80     if (nsFont == nil) {
  81         // if can't get font of that name, substitute system default font
  82         nsFont = [NSFont fontWithName:@"Lucida Grande" size:1.0];
  83 #ifdef DEBUG
  84         NSLog(@"needed to substitute Lucida Grande for: %@", name);
  85 #endif
  86     }
  87 
  88     // create an italic style (if one is installed)
  89     if (style & java_awt_Font_ITALIC) {
  90         nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSItalicFontMask];
  91     }
  92 
  93     // create a bold style (if one is installed)
  94     if (style & java_awt_Font_BOLD) {
  95         nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSBoldFontMask];
  96     }
  97 
  98     return [[[AWTFont alloc] initWithFont:nsFont isFakeItalic:isFakeItalic] autorelease];
  99 }
 100 
 101 + (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env {
 102     if (javaFont == NULL) {
 103 #ifdef DEBUG
 104         NSLog(@"nil font");
 105 #endif
 106         return nil;
 107     }
 108 
 109     static JNF_CLASS_CACHE(jc_Font, "java/awt/Font");
 110 
 111     // obtain the Font2D
 112     static JNF_MEMBER_CACHE(jm_Font_getFont2D, jc_Font, "getFont2D", "()Lsun/font/Font2D;");
 113     jobject font2d = JNFCallObjectMethod(env, javaFont, jm_Font_getFont2D);
 114     if (font2d == NULL) {
 115 #ifdef DEBUG
 116         NSLog(@"nil font2d");
 117 #endif
 118         return nil;
 119     }
 120 
 121     // if it's not a CFont, it's likely one of TTF or OTF fonts
 122     // from the Sun rendering loops
 123     static JNF_CLASS_CACHE(jc_CFont, "sun/font/CFont");
 124     if (!JNFIsInstanceOf(env, font2d, &jc_CFont)) {
 125 #ifdef DEBUG
 126         NSLog(@"font2d !instanceof CFont");
 127 #endif
 128         return nil;
 129     }
 130 
 131     static JNF_MEMBER_CACHE(jm_CFont_getFontStrike, jc_CFont, "getStrike", "(Ljava/awt/Font;)Lsun/font/FontStrike;");
 132     jobject fontStrike = JNFCallObjectMethod(env, font2d, jm_CFont_getFontStrike, javaFont);
 133 
 134     static JNF_CLASS_CACHE(jc_CStrike, "sun/font/CStrike");
 135     if (!JNFIsInstanceOf(env, fontStrike, &jc_CStrike)) {
 136 #ifdef DEBUG
 137         NSLog(@"fontStrike !instanceof CStrike");
 138 #endif
 139         return nil;
 140     }
 141 
 142     static JNF_MEMBER_CACHE(jm_CStrike_nativeStrikePtr, jc_CStrike, "getNativeStrikePtr", "()J");
 143     jlong awtStrikePtr = JNFCallLongMethod(env, fontStrike, jm_CStrike_nativeStrikePtr);
 144     if (awtStrikePtr == 0L) {
 145 #ifdef DEBUG
 146         NSLog(@"nil nativeFontPtr from CFont");
 147 #endif
 148         return nil;
 149     }
 150 
 151     AWTStrike *strike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
 152 
 153     return [NSFont fontWithName:[strike->fAWTFont->fFont fontName] matrix:(CGFloat *)(&(strike->fAltTx))];
 154 }
 155 
 156 @end
 157 
 158 
 159 #pragma mark --- Font Discovery and Loading ---
 160 
 161 static NSArray* sFilteredFonts = nil;
 162 static NSDictionary* sFontFamilyTable = nil;
 163 
 164 static NSString*
 165 GetFamilyNameForFontName(NSString* fontname)
 166 {
 167     return [sFontFamilyTable objectForKey:fontname];
 168 }
 169 
 170 static NSArray*
 171 GetFilteredFonts()
 172 {
 173     if (sFilteredFonts == nil) {
 174         NSFontManager *fontManager = [NSFontManager sharedFontManager];
 175         NSUInteger fontCount = [[fontManager availableFonts] count];
 176 
 177         NSMutableArray *allFonts = [[NSMutableArray alloc] initWithCapacity:fontCount];
 178         NSMutableDictionary* fontFamilyTable = [[NSMutableDictionary alloc] initWithCapacity:fontCount];
 179         NSArray *allFamilies = [fontManager availableFontFamilies];
 180 
 181         NSUInteger familyCount = [allFamilies count];
 182 
 183         NSUInteger familyIndex;
 184         for (familyIndex = 0; familyIndex < familyCount; familyIndex++) {
 185             NSString *family = [allFamilies objectAtIndex:familyIndex];
 186 
 187             if ((family == nil) || [family characterAtIndex:0] == '.') {
 188                 continue;
 189             }
 190 
 191             NSArray *fontFaces = [fontManager availableMembersOfFontFamily:family];
 192             NSUInteger faceCount = [fontFaces count];
 193 
 194             NSUInteger faceIndex;
 195             for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
 196                 NSString* face = [[fontFaces objectAtIndex:faceIndex] objectAtIndex:0];
 197                 if (face != nil) {
 198                     [allFonts addObject:face];
 199                     [fontFamilyTable setObject:family forKey:face];
 200                 }
 201             }
 202         }
 203 
 204         sFilteredFonts = allFonts;
 205         sFontFamilyTable = fontFamilyTable;
 206     }
 207 
 208     return sFilteredFonts;
 209 }
 210 
 211 #pragma mark --- sun.font.CFontManager JNI ---
 212 
 213 static OSStatus CreateFSRef(FSRef *myFSRefPtr, NSString *inPath)
 214 {
 215     return FSPathMakeRef((UInt8 *)[inPath fileSystemRepresentation],
 216                          myFSRefPtr, NULL);
 217 }
 218 
 219 // /*
 220 //  * Class:     sun_font_CFontManager
 221 //  * Method:    loadFileFont
 222 //  * Signature: (Ljava/lang/String;)Lsun/font/Font2D;
 223 //  */
 224 // JNIEXPORT /* sun.font.CFont */ jobject JNICALL
 225 // Java_sun_font_CFontManager_loadFileFont
 226 //     (JNIEnv *env, jclass obj, jstring fontpath)
 227 // {
 228 //     jobject result = NULL;
 229 //
 230 // JNF_COCOA_ENTER(env);
 231 //
 232 //     NSString *nsFilePath = JNFJavaToNSString(env, fontpath);
 233 //     jstring javaFontName = NULL;
 234 //
 235 //     //
 236 //     // Note: This API uses ATS and can therefore return Carbon error codes.
 237 //     // These codes can be found at:
 238 //     // http://developer.apple.com/techpubs/macosx/Carbon/Files/FileManager/File_Manager/ResultCodes/ResultCodes.html
 239 //     //
 240 //
 241 //     FSRef iFile;
 242 //     OSStatus status = CreateFSRef(&iFile, nsFilePath);
 243 //
 244 //     if (status == noErr) {
 245 //         ATSFontContainerRef oContainer;
 246 //         status = ATSFontActivateFromFileReference(&iFile, kATSFontContextLocal,
 247 //                                                   kATSFontFormatUnspecified,
 248 //                                                   NULL,
 249 //                                                   kATSOptionFlagsUseDataFork,
 250 //                                                   &oContainer);
 251 //         if (status == noErr) {
 252 //             ATSFontRef ioArray[1];
 253 //             ItemCount oCount;
 254 //             status = ATSFontFindFromContainer(oContainer,
 255 //                                               kATSOptionFlagsUseDataFork,
 256 //                                               1, ioArray, &oCount);
 257 //
 258 //             if (status == noErr) {
 259 //                 CFStringRef oName;
 260 //                 status = ATSFontGetPostScriptName(ioArray[0],
 261 //                                                   kATSOptionFlagsUseDataFork,
 262 //                                                   &oName);
 263 //                 if (status == noErr) {
 264 //                     javaFontName = JNFNSToJavaString(env, (NSString *)oName);
 265 //                     CFRelease(oName);
 266 //                 }
 267 //             }
 268 //         }
 269 //     }
 270 //
 271 //     if (javaFontName != NULL) {
 272 //         // create the CFont!
 273 //         static JNF_CLASS_CACHE(sjc_CFont, "sun/font/CFont");
 274 //         static JNF_CTOR_CACHE(sjf_CFont_ctor,
 275 //                               sjc_CFont, "(Ljava/lang/String;)V");
 276 //         result = JNFNewObject(env, sjf_CFont_ctor, javaFontName);
 277 //     }
 278 //
 279 // JNF_COCOA_EXIT(env);
 280 //
 281 //     return result;
 282 // }
 283 
 284 /*
 285  * Class:     sun_font_CFontManager
 286  * Method:    loadNativeFonts
 287  * Signature: ()V
 288  */
 289 JNIEXPORT void JNICALL
 290 Java_sun_font_CFontManager_loadNativeFonts
 291     (JNIEnv *env, jobject jthis)
 292 {
 293     static JNF_CLASS_CACHE(jc_CFontManager,
 294                            "sun/font/CFontManager");
 295     static JNF_MEMBER_CACHE(jm_registerFont, jc_CFontManager,
 296                             "registerFont",
 297                             "(Ljava/lang/String;Ljava/lang/String;)V");
 298 
 299     jint num = 0;
 300 
 301 JNF_COCOA_ENTER(env);
 302 
 303     NSArray *filteredFonts = GetFilteredFonts();
 304     num = (jint)[filteredFonts count];
 305 
 306     jint i;
 307     for (i = 0; i < num; i++) {
 308         NSString *fontname = [filteredFonts objectAtIndex:i];
 309         jobject jFontName = JNFNSToJavaString(env, fontname);
 310         jobject jFontFamilyName =
 311             JNFNSToJavaString(env, GetFamilyNameForFontName(fontname));
 312 
 313         JNFCallVoidMethod(env, jthis,
 314                           jm_registerFont, jFontName, jFontFamilyName);
 315     }
 316 
 317 JNF_COCOA_EXIT(env);
 318 }
 319 
 320 /*
 321  * Class:     Java_sun_font_CFontManager_loadNativeDirFonts
 322  * Method:    loadNativeDirFonts
 323  * Signature: (Ljava/lang/String;)V;
 324  */
 325 JNIEXPORT void JNICALL
 326 Java_sun_font_CFontManager_loadNativeDirFonts
 327 (JNIEnv *env, jclass clz, jstring filename)
 328 {
 329 JNF_COCOA_ENTER(env);
 330 
 331     NSString *nsFilePath = JNFJavaToNSString(env, filename);
 332 
 333     FSRef iFile;
 334     OSStatus status = CreateFSRef(&iFile, nsFilePath);
 335 
 336     if (status == noErr) {
 337         ATSFontContainerRef oContainer;
 338         status = ATSFontActivateFromFileReference(&iFile, kATSFontContextLocal,
 339                                                   kATSFontFormatUnspecified,
 340                                                   NULL, kNilOptions,
 341                                                   &oContainer);
 342     }
 343 
 344 JNF_COCOA_EXIT(env);
 345 }
 346 
 347 #pragma mark --- sun.font.CFont JNI ---
 348 
 349 /*
 350  * Class:     sun_font_CFont
 351  * Method:    initNativeFont
 352  * Signature: (Ljava/lang/String;I)J
 353  */
 354 JNIEXPORT jlong JNICALL
 355 Java_sun_font_CFont_createNativeFont
 356     (JNIEnv *env, jclass clazz,
 357      jstring nativeFontName, jint style, jboolean isFakeItalic)
 358 {
 359     AWTFont *awtFont = nil;
 360 
 361 JNF_COCOA_ENTER(env);
 362 
 363     awtFont =
 364         [AWTFont awtFontForName:JNFJavaToNSString(env, nativeFontName)
 365          style:style
 366          isFakeItalic:isFakeItalic]; // autoreleased
 367 
 368     if (awtFont) {
 369         CFRetain(awtFont); // GC
 370     }
 371 
 372 JNF_COCOA_EXIT(env);
 373 
 374     return ptr_to_jlong(awtFont);
 375 }
 376 
 377 /*
 378  * Class:     sun_font_CFont
 379  * Method:    disposeNativeFont
 380  * Signature: (J)V
 381  */
 382 JNIEXPORT void JNICALL
 383 Java_sun_font_CFont_disposeNativeFont
 384     (JNIEnv *env, jclass clazz, jlong awtFontPtr)
 385 {
 386 JNF_COCOA_ENTER(env);
 387 
 388     if (awtFontPtr) {
 389         CFRelease((AWTFont *)jlong_to_ptr(awtFontPtr)); // GC
 390     }
 391 
 392 JNF_COCOA_EXIT(env);
 393 }
 394 
 395 
 396 #pragma mark --- Miscellaneous JNI ---
 397 
 398 #ifndef HEADLESS
 399 /*
 400  * Class:     sun_awt_PlatformFont
 401  * Method:    initIDs
 402  * Signature: ()V
 403  */
 404 JNIEXPORT void JNICALL
 405 Java_sun_awt_PlatformFont_initIDs
 406     (JNIEnv *env, jclass cls)
 407 {
 408 }
 409 
 410 /*
 411  * Class:     sun_awt_FontDescriptor
 412  * Method:    initIDs
 413  * Signature: ()V
 414  */
 415 JNIEXPORT void JNICALL
 416 Java_sun_awt_FontDescriptor_initIDs
 417     (JNIEnv *env, jclass cls)
 418 {
 419 }
 420 #endif