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 "java_awt_Font.h"
27#import "sun_awt_PlatformFont.h"
28#import "sun_awt_FontDescriptor.h"
29#import "sun_font_CFont.h"
30#import "sun_font_CFontManager.h"
31
32#import "AWTFont.h"
33#import "AWTStrike.h"
34#import "CoreTextSupport.h"
35#import "JNIUtilities.h"
36
37@implementation AWTFont
38
39- (id) initWithFont:(NSFont *)font {
40    self = [super init];
41    if (self) {
42        fFont = [font retain];
43        fNativeCGFont = CTFontCopyGraphicsFont((CTFontRef)font, NULL);
44    }
45    return self;
46}
47
48- (void) dealloc {
49    [fFont release];
50    fFont = nil;
51
52    if (fNativeCGFont) {
53        CGFontRelease(fNativeCGFont);
54    fNativeCGFont = NULL;
55    }
56
57    [super dealloc];
58}
59
60- (void) finalize {
61    if (fNativeCGFont) {
62        CGFontRelease(fNativeCGFont);
63    fNativeCGFont = NULL;
64    }
65    [super finalize];
66}
67
68static NSString* uiName = nil;
69static NSString* uiBoldName = nil;
70
71+ (AWTFont *) awtFontForName:(NSString *)name
72                       style:(int)style
73{
74    // create font with family & size
75    NSFont *nsFont = nil;
76
77    if ((uiName != nil && [name isEqualTo:uiName]) ||
78        (uiBoldName != nil && [name isEqualTo:uiBoldName])) {
79        if (style & java_awt_Font_BOLD) {
80            nsFont = [NSFont boldSystemFontOfSize:1.0];
81        } else {
82            nsFont = [NSFont systemFontOfSize:1.0];
83        }
84#ifdef DEBUG
85        NSLog(@"nsFont-name is : %@", nsFont.familyName);
86        NSLog(@"nsFont-family is : %@", nsFont.fontName);
87        NSLog(@"nsFont-desc-name is : %@", nsFont.fontDescriptor.postscriptName);
88#endif
89
90
91    } else {
92           nsFont = [NSFont fontWithName:name size:1.0];
93    }
94
95    if (nsFont == nil) {
96        // if can't get font of that name, substitute system default font
97        nsFont = [NSFont fontWithName:@"Lucida Grande" size:1.0];
98#ifdef DEBUG
99        NSLog(@"needed to substitute Lucida Grande for: %@", name);
100#endif
101    }
102
103    // create an italic style (if one is installed)
104    if (style & java_awt_Font_ITALIC) {
105        nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSItalicFontMask];
106    }
107
108    // create a bold style (if one is installed)
109    if (style & java_awt_Font_BOLD) {
110        nsFont = [[NSFontManager sharedFontManager] convertFont:nsFont toHaveTrait:NSBoldFontMask];
111    }
112
113    return [[[AWTFont alloc] initWithFont:nsFont] autorelease];
114}
115
116+ (NSFont *) nsFontForJavaFont:(jobject)javaFont env:(JNIEnv *)env {
117    if (javaFont == NULL) {
118#ifdef DEBUG
119        NSLog(@"nil font");
120#endif
121        return nil;
122    }
123
124    DECLARE_CLASS_RETURN(jc_Font, "java/awt/Font", nil);
125
126    // obtain the Font2D
127    DECLARE_METHOD_RETURN(jm_Font_getFont2D, jc_Font, "getFont2D", "()Lsun/font/Font2D;", nil);
128    jobject font2d = (*env)->CallObjectMethod(env, javaFont, jm_Font_getFont2D);
129    CHECK_EXCEPTION();
130    if (font2d == NULL) {
131#ifdef DEBUG
132        NSLog(@"nil font2d");
133#endif
134        return nil;
135    }
136
137    // if it's not a CFont, it's likely one of TTF or OTF fonts
138    // from the Sun rendering loops
139    DECLARE_CLASS_RETURN(jc_CFont, "sun/font/CFont", nil);
140    if (!(*env)->IsInstanceOf(env, font2d, jc_CFont)) {
141#ifdef DEBUG
142        NSLog(@"font2d !instanceof CFont");
143#endif
144        return nil;
145    }
146
147    DECLARE_METHOD_RETURN(jm_CFont_getFontStrike, jc_CFont, "getStrike", "(Ljava/awt/Font;)Lsun/font/FontStrike;", nil);
148    jobject fontStrike = (*env)->CallObjectMethod(env, font2d, jm_CFont_getFontStrike, javaFont);
149    CHECK_EXCEPTION();
150    DECLARE_CLASS_RETURN(jc_CStrike, "sun/font/CStrike", nil);
151    if (!(*env)->IsInstanceOf(env, fontStrike, jc_CStrike)) {
152#ifdef DEBUG
153        NSLog(@"fontStrike !instanceof CStrike");
154#endif
155        return nil;
156    }
157
158    DECLARE_METHOD_RETURN(jm_CStrike_nativeStrikePtr, jc_CStrike, "getNativeStrikePtr", "()J", nil);
159    jlong awtStrikePtr = (*env)->CallLongMethod(env, fontStrike, jm_CStrike_nativeStrikePtr);
160    CHECK_EXCEPTION();
161    if (awtStrikePtr == 0L) {
162#ifdef DEBUG
163        NSLog(@"nil nativeFontPtr from CFont");
164#endif
165        return nil;
166    }
167
168    AWTStrike *strike = (AWTStrike *)jlong_to_ptr(awtStrikePtr);
169
170    return [NSFont fontWithName:[strike->fAWTFont->fFont fontName] matrix:(CGFloat *)(&(strike->fAltTx))];
171}
172
173@end
174
175
176#pragma mark --- Font Discovery and Loading ---
177
178static NSArray* sFilteredFonts = nil;
179static NSDictionary* sFontFamilyTable = nil;
180
181static NSString*
182GetFamilyNameForFontName(NSString* fontname)
183{
184    return [sFontFamilyTable objectForKey:fontname];
185}
186
187static void addFont(CTFontUIFontType uiType,
188                    NSMutableArray *allFonts,
189                    NSMutableDictionary* fontFamilyTable) {
190
191        CTFontRef font = CTFontCreateUIFontForLanguage(uiType, 0.0, NULL);
192        if (font == NULL) {
193            return;
194        }
195        CTFontDescriptorRef desc = CTFontCopyFontDescriptor(font);
196        if (desc == NULL) {
197            CFRelease(font);
198            return;
199        }
200        CFStringRef family = CTFontDescriptorCopyAttribute(desc, kCTFontFamilyNameAttribute);
201        if (family == NULL) {
202            CFRelease(desc);
203            CFRelease(font);
204            return;
205        }
206        CFStringRef name = CTFontDescriptorCopyAttribute(desc, kCTFontNameAttribute);
207        if (name == NULL) {
208            CFRelease(family);
209            CFRelease(desc);
210            CFRelease(font);
211            return;
212        }
213        if (uiType == kCTFontUIFontSystem) {
214            uiName = (NSString*)name;
215        }
216        if (uiType == kCTFontUIFontEmphasizedSystem) {
217            uiBoldName = (NSString*)name;
218        }
219        [allFonts addObject:name];
220        [fontFamilyTable setObject:family forKey:name];
221#ifdef DEBUG
222        NSLog(@"name is : %@", (NSString*)name);
223        NSLog(@"family is : %@", (NSString*)family);
224#endif
225        CFRelease(family);
226        CFRelease(name);
227        CFRelease(desc);
228        CFRelease(font);
229}
230
231static NSArray*
232GetFilteredFonts()
233{
234    if (sFilteredFonts == nil) {
235        NSFontManager *fontManager = [NSFontManager sharedFontManager];
236        NSUInteger fontCount = [[fontManager availableFonts] count];
237
238        NSMutableArray *allFonts = [[NSMutableArray alloc] initWithCapacity:fontCount];
239        NSMutableDictionary* fontFamilyTable = [[NSMutableDictionary alloc] initWithCapacity:fontCount];
240        NSArray *allFamilies = [fontManager availableFontFamilies];
241
242        NSUInteger familyCount = [allFamilies count];
243
244        NSUInteger familyIndex;
245        for (familyIndex = 0; familyIndex < familyCount; familyIndex++) {
246            NSString *family = [allFamilies objectAtIndex:familyIndex];
247
248            if ((family == nil) || [family characterAtIndex:0] == '.') {
249                continue;
250            }
251
252            NSArray *fontFaces = [fontManager availableMembersOfFontFamily:family];
253            NSUInteger faceCount = [fontFaces count];
254
255            NSUInteger faceIndex;
256            for (faceIndex = 0; faceIndex < faceCount; faceIndex++) {
257                NSString* face = [[fontFaces objectAtIndex:faceIndex] objectAtIndex:0];
258                if (face != nil) {
259                    [allFonts addObject:face];
260                    [fontFamilyTable setObject:family forKey:face];
261                }
262            }
263        }
264
265        /*
266         * JavaFX registers these fonts and so JDK needs to do so as well.
267         * If this isn't done we will have mis-matched rendering, since
268         * although these may include fonts that are enumerated normally
269         * they also demonstrably includes fonts that are not.
270         */
271        addFont(kCTFontUIFontSystem, allFonts, fontFamilyTable);
272        addFont(kCTFontUIFontEmphasizedSystem, allFonts, fontFamilyTable);
273
274        sFilteredFonts = allFonts;
275        sFontFamilyTable = fontFamilyTable;
276    }
277
278    return sFilteredFonts;
279}
280
281#pragma mark --- sun.font.CFontManager JNI ---
282
283static OSStatus CreateFSRef(FSRef *myFSRefPtr, NSString *inPath)
284{
285    return FSPathMakeRef((UInt8 *)[inPath fileSystemRepresentation],
286                         myFSRefPtr, NULL);
287}
288
289/*
290 * Class:     sun_font_CFontManager
291 * Method:    loadNativeFonts
292 * Signature: ()V
293 */
294JNIEXPORT void JNICALL
295Java_sun_font_CFontManager_loadNativeFonts
296    (JNIEnv *env, jobject jthis)
297{
298    DECLARE_CLASS(jc_CFontManager, "sun/font/CFontManager");
299    DECLARE_METHOD(jm_registerFont, jc_CFontManager, "registerFont", "(Ljava/lang/String;Ljava/lang/String;)V");
300
301    jint num = 0;
302
303JNI_COCOA_ENTER(env);
304
305    NSArray *filteredFonts = GetFilteredFonts();
306    num = (jint)[filteredFonts count];
307
308    jint i;
309    for (i = 0; i < num; i++) {
310        NSString *fontname = [filteredFonts objectAtIndex:i];
311        jobject jFontName = NSStringToJavaString(env, fontname);
312        jobject jFontFamilyName =
313            NSStringToJavaString(env, GetFamilyNameForFontName(fontname));
314
315        (*env)->CallVoidMethod(env, jthis, jm_registerFont, jFontName, jFontFamilyName);
316        CHECK_EXCEPTION();
317        (*env)->DeleteLocalRef(env, jFontName);
318        (*env)->DeleteLocalRef(env, jFontFamilyName);
319    }
320
321JNI_COCOA_EXIT(env);
322}
323
324/*
325 * Class:     Java_sun_font_CFontManager_loadNativeDirFonts
326 * Method:    loadNativeDirFonts
327 * Signature: (Ljava/lang/String;)V;
328 */
329JNIEXPORT void JNICALL
330Java_sun_font_CFontManager_loadNativeDirFonts
331(JNIEnv *env, jclass clz, jstring filename)
332{
333JNI_COCOA_ENTER(env);
334
335    NSString *path = JavaStringToNSString(env, filename);
336    NSURL *url = [NSURL fileURLWithPath:(NSString *)path];
337    bool res = CTFontManagerRegisterFontsForURL((CFURLRef)url, kCTFontManagerScopeProcess, nil);
338#ifdef DEBUG
339    NSLog(@"path is : %@", (NSString*)path);
340    NSLog(@"url is : %@", (NSString*)url);
341    printf("res is %d\n", res);
342#endif
343JNI_COCOA_EXIT(env);
344}
345
346#pragma mark --- sun.font.CFont JNI ---
347
348/*
349 * Class:     sun_font_CFont
350 * Method:    getPlatformFontPtrNative
351 * Signature: (JI)[B
352 */
353JNIEXPORT jlong JNICALL
354Java_sun_font_CFont_getCGFontPtrNative
355    (JNIEnv *env, jclass clazz,
356     jlong awtFontPtr)
357{
358    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
359    return (jlong)(awtFont->fNativeCGFont);
360}
361
362/*
363 * Class:     sun_font_CFont
364 * Method:    getTableBytesNative
365 * Signature: (JI)[B
366 */
367JNIEXPORT jbyteArray JNICALL
368Java_sun_font_CFont_getTableBytesNative
369    (JNIEnv *env, jclass clazz,
370     jlong awtFontPtr, jint jtag)
371{
372    jbyteArray jbytes = NULL;
373JNI_COCOA_ENTER(env);
374
375    CTFontTableTag tag = (CTFontTableTag)jtag;
376    int i, found = 0;
377    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
378    NSFont* nsFont = awtFont->fFont;
379    CTFontRef ctfont = (CTFontRef)nsFont;
380    CFArrayRef tagsArray =
381        CTFontCopyAvailableTables(ctfont, kCTFontTableOptionNoOptions);
382    CFIndex numTags = CFArrayGetCount(tagsArray);
383    for (i=0; i<numTags; i++) {
384        if (tag ==
385            (CTFontTableTag)(uintptr_t)CFArrayGetValueAtIndex(tagsArray, i)) {
386            found = 1;
387            break;
388        }
389    }
390    CFRelease(tagsArray);
391    if (!found) {
392        return NULL;
393    }
394    CFDataRef table = CTFontCopyTable(ctfont, tag, kCTFontTableOptionNoOptions);
395    if (table == NULL) {
396        return NULL;
397    }
398
399    char *tableBytes = (char*)(CFDataGetBytePtr(table));
400    size_t tableLength = CFDataGetLength(table);
401    if (tableBytes == NULL || tableLength == 0) {
402        CFRelease(table);
403        return NULL;
404    }
405
406    jbytes = (*env)->NewByteArray(env, (jsize)tableLength);
407    if (jbytes == NULL) {
408        return NULL;
409    }
410    (*env)->SetByteArrayRegion(env, jbytes, 0,
411                               (jsize)tableLength,
412                               (jbyte*)tableBytes);
413    CFRelease(table);
414
415JNI_COCOA_EXIT(env);
416
417    return jbytes;
418}
419
420/*
421 * Class:     sun_font_CFont
422 * Method:    initNativeFont
423 * Signature: (Ljava/lang/String;I)J
424 */
425JNIEXPORT jlong JNICALL
426Java_sun_font_CFont_createNativeFont
427    (JNIEnv *env, jclass clazz,
428     jstring nativeFontName, jint style)
429{
430    AWTFont *awtFont = nil;
431
432JNI_COCOA_ENTER(env);
433
434    awtFont =
435        [AWTFont awtFontForName:JavaStringToNSString(env, nativeFontName)
436         style:style]; // autoreleased
437
438    if (awtFont) {
439        CFRetain(awtFont); // GC
440    }
441
442JNI_COCOA_EXIT(env);
443
444    return ptr_to_jlong(awtFont);
445}
446
447/*
448 * Class:     sun_font_CFont
449 * Method:    getWidthNative
450 * Signature: (J)F
451 */
452JNIEXPORT jfloat JNICALL
453Java_sun_font_CFont_getWidthNative
454    (JNIEnv *env, jobject cfont, jlong awtFontPtr)
455{
456    float widthVal;
457JNI_COCOA_ENTER(env);
458
459    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
460    NSFont* nsFont = awtFont->fFont;
461    NSFontDescriptor *fontDescriptor = nsFont.fontDescriptor;
462    NSDictionary *fontTraits = [fontDescriptor objectForKey : NSFontTraitsAttribute];
463    NSNumber *width = [fontTraits objectForKey : NSFontWidthTrait];
464    widthVal = (float)[width floatValue];
465
466JNI_COCOA_EXIT(env);
467   return (jfloat)widthVal;
468}
469
470/*
471 * Class:     sun_font_CFont
472 * Method:    getWeightNative
473 * Signature: (J)F
474 */
475JNIEXPORT jfloat JNICALL
476Java_sun_font_CFont_getWeightNative
477    (JNIEnv *env, jobject cfont, jlong awtFontPtr)
478{
479    float weightVal;
480JNI_COCOA_ENTER(env);
481
482    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
483    NSFont* nsFont = awtFont->fFont;
484    NSFontDescriptor *fontDescriptor = nsFont.fontDescriptor;
485    NSDictionary *fontTraits = [fontDescriptor objectForKey : NSFontTraitsAttribute];
486    NSNumber *weight = [fontTraits objectForKey : NSFontWeightTrait];
487    weightVal = (float)[weight floatValue];
488
489JNI_COCOA_EXIT(env);
490   return (jfloat)weightVal;
491}
492
493/*
494 * Class:     sun_font_CFont
495 * Method:    disposeNativeFont
496 * Signature: (J)V
497 */
498JNIEXPORT void JNICALL
499Java_sun_font_CFont_disposeNativeFont
500    (JNIEnv *env, jclass clazz, jlong awtFontPtr)
501{
502JNI_COCOA_ENTER(env);
503
504    if (awtFontPtr) {
505        CFRelease((AWTFont *)jlong_to_ptr(awtFontPtr)); // GC
506    }
507
508JNI_COCOA_EXIT(env);
509}
510
511
512#pragma mark --- Miscellaneous JNI ---
513
514#ifndef HEADLESS
515/*
516 * Class:     sun_awt_PlatformFont
517 * Method:    initIDs
518 * Signature: ()V
519 */
520JNIEXPORT void JNICALL
521Java_sun_awt_PlatformFont_initIDs
522    (JNIEnv *env, jclass cls)
523{
524}
525
526/*
527 * Class:     sun_awt_FontDescriptor
528 * Method:    initIDs
529 * Signature: ()V
530 */
531JNIEXPORT void JNICALL
532Java_sun_awt_FontDescriptor_initIDs
533    (JNIEnv *env, jclass cls)
534{
535}
536#endif
537
538/*
539 * Class:     sun_awt_FontDescriptor
540 * Method:    initIDs
541 * Signature: ()V
542 */
543JNIEXPORT void JNICALL
544Java_sun_font_CFont_getCascadeList
545    (JNIEnv *env, jclass cls, jlong awtFontPtr, jobject arrayListOfString)
546{
547JNI_COCOA_ENTER(env);
548    jclass alc = (*env)->FindClass(env, "java/util/ArrayList");
549    if (alc == NULL) return;
550    jmethodID addMID = (*env)->GetMethodID(env, alc, "add", "(Ljava/lang/Object;)Z");
551    if (addMID == NULL) return;
552
553    CFIndex i;
554    AWTFont *awtFont = (AWTFont *)jlong_to_ptr(awtFontPtr);
555    NSFont* nsFont = awtFont->fFont;
556    CTFontRef font = (CTFontRef)nsFont;
557    CFArrayRef codes = CFLocaleCopyISOLanguageCodes();
558
559#ifdef DEBUG
560    CFStringRef base = CTFontCopyFullName(font);
561    NSLog(@"BaseFont is : %@", (NSString*)base);
562    CFRelease(base);
563#endif
564    CFArrayRef fds = CTFontCopyDefaultCascadeListForLanguages(font, codes);
565    CFRelease(codes);
566    CFIndex cnt = CFArrayGetCount(fds);
567    for (i=0; i<cnt; i++) {
568        CTFontDescriptorRef ref = CFArrayGetValueAtIndex(fds, i);
569        CFStringRef fontname =
570            CTFontDescriptorCopyAttribute(ref, kCTFontNameAttribute);
571#ifdef DEBUG
572        NSLog(@"Font is : %@", (NSString*)fontname);
573#endif
574        jstring jFontName = (jstring)NSStringToJavaString(env, fontname);
575        CFRelease(fontname);
576        (*env)->CallBooleanMethod(env, arrayListOfString, addMID, jFontName);
577        if ((*env)->ExceptionOccurred(env)) {
578            CFRelease(fds);
579            return;
580        }
581        (*env)->DeleteLocalRef(env, jFontName);
582    }
583    CFRelease(fds);
584JNI_COCOA_EXIT(env);
585}
586
587static CFStringRef EMOJI_FONT_NAME = CFSTR("Apple Color Emoji");
588
589bool IsEmojiFont(CTFontRef font)
590{
591    CFStringRef name = CTFontCopyFullName(font);
592    if (name == NULL) return false;
593    bool isFixedColor = CFStringCompare(name, EMOJI_FONT_NAME, 0) == kCFCompareEqualTo;
594    CFRelease(name);
595    return isFixedColor;
596}
597