diff --git a/src/fontdetection.cpp b/src/fontdetection.cpp index 3064268636..943254ab37 100644 --- a/src/fontdetection.cpp +++ b/src/fontdetection.cpp @@ -446,11 +446,8 @@ FT_Error GetFontByFaceName(const char *font_name, FT_Face *face) bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, int winlangid, MissingGlyphSearcher *callback) { - const char *str; bool result = false; - callback->FindMissingGlyphs(&str); - #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_5) if (MacOSVersionIsAtLeast(10, 5, 0)) { /* Determine fallback font using CoreText. This uses the language isocode @@ -462,11 +459,6 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i } else if (strcmp(language_isocode, "zh_CN") == 0) { /* Simplified Chinese */ strecpy(lang, "zh-Hans", lastof(lang)); - } else if (strncmp(language_isocode, "ur", 2) == 0) { - /* The urdu alphabet is variant of persian. As OS X has no default - * font that advertises an urdu language code, search for persian - * support instead. */ - strecpy(lang, "fa", lastof(lang)); } else { /* Just copy the first part of the isocode. */ strecpy(lang, language_isocode, lastof(lang)); @@ -474,150 +466,87 @@ bool SetFallbackFont(FreeTypeSettings *settings, const char *language_isocode, i if (sep != NULL) *sep = '\0'; } - CFStringRef lang_code; - lang_code = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); + /* Create a font descriptor matching the wanted language and latin (english) glyphs. */ + CFStringRef lang_codes[2]; + lang_codes[0] = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); + lang_codes[1] = CFSTR("en"); + CFArrayRef lang_arr = CFArrayCreate(kCFAllocatorDefault, (const void **)lang_codes, lengthof(lang_codes), &kCFTypeArrayCallBacks); + CFDictionaryRef lang_attribs = CFDictionaryCreate(kCFAllocatorDefault, (const void**)&kCTFontLanguagesAttribute, (const void **)&lang_arr, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); + CTFontDescriptorRef lang_desc = CTFontDescriptorCreateWithAttributes(lang_attribs); + CFRelease(lang_arr); + CFRelease(lang_attribs); + CFRelease(lang_codes[0]); + + /* Get array of all font descriptors for the wanted language. */ + CFSetRef mandatory_attribs = CFSetCreate(kCFAllocatorDefault, (const void **)&kCTFontLanguagesAttribute, 1, &kCFTypeSetCallBacks); + CFArrayRef descs = CTFontDescriptorCreateMatchingFontDescriptors(lang_desc, mandatory_attribs); + CFRelease(mandatory_attribs); + CFRelease(lang_desc); + + for (CFIndex i = 0; descs != NULL && i < CFArrayGetCount(descs); i++) { + CTFontDescriptorRef font = (CTFontDescriptorRef)CFArrayGetValueAtIndex(descs, i); + + /* Get font name. */ + char name[128]; + CFStringRef font_name = (CFStringRef)CTFontDescriptorCopyAttribute(font, kCTFontDisplayNameAttribute); + CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8); + CFRelease(font_name); - /* Create a font iterator and iterate over all fonts that - * are available to the application. */ - ATSFontIterator itr; - ATSFontRef font; - ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsUnRestrictedScope, &itr); - while (!result && ATSFontIteratorNext(itr, &font) == noErr) { - /* Get CoreText font handle. */ - CTFontRef font_ref = CTFontCreateWithPlatformFont(font, 0.0, NULL, NULL); - CFArrayRef langs = CTFontCopySupportedLanguages(font_ref); - if (langs != NULL) { - /* Font has a list of supported languages. */ - for (CFIndex i = 0; i < CFArrayGetCount(langs); i++) { - CFStringRef lang = (CFStringRef)CFArrayGetValueAtIndex(langs, i); - if (CFStringCompare(lang, lang_code, kCFCompareAnchored) == kCFCompareEqualTo) { - /* Lang code is supported by font, get full font name. */ - CFStringRef font_name = CTFontCopyFullName(font_ref); - char name[128]; - CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8); - CFRelease(font_name); - /* Skip some inappropriate or ugly looking fonts that have better alternatives. */ - if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 || - strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 || - strncmp(name, "GB18030 Bitmap", 14) == 0) continue; - - /* Save result. */ - callback->SetFontNames(settings, name); - DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); - result = true; - break; - } - } - CFRelease(langs); + /* Skip some inappropriate or ugly looking fonts that have better alternatives. */ + if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 || + strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 || + strncmp(name, "GB18030 Bitmap", 14) == 0) continue; + + /* Save result. */ + callback->SetFontNames(settings, name); + if (!callback->FindMissingGlyphs(NULL)) { + DEBUG(freetype, 2, "CT-Font for %s: %s", language_isocode, name); + result = true; + break; } - CFRelease(font_ref); } - ATSFontIteratorRelease(&itr); - CFRelease(lang_code); + if (descs != NULL) CFRelease(descs); } else #endif { -#if (MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_5) && !__LP64__ - /* Determine fallback font using ATSUI. This uses a string sample with - * missing characters. This is not failure-proof, but a better way like - * using the isocode as in the CoreText code path is not available. - * ATSUI was deprecated with 10.6 and is only partially available in - * 64-bit mode. */ - - /* Remove all control characters in the range from SCC_CONTROL_START to - * SCC_CONTROL_END as well as all ASCII < 0x20 from the string as it will - * mess with the automatic font detection */ - char buff[256]; // This length is enough to find a suitable replacement font - strecpy(buff, str, lastof(buff)); - str_validate(buff, lastof(buff), SVS_ALLOW_NEWLINE); - - /* Extract a UniChar representation of the sample string. */ - CFStringRef cf_str = CFStringCreateWithCString(kCFAllocatorDefault, buff, kCFStringEncodingUTF8); - if (cf_str == NULL) { - /* Something went wrong. Corrupt/invalid sample string? */ - return false; - } - CFIndex str_len = CFStringGetLength(cf_str); - UniChar string[str_len]; - CFStringGetCharacters(cf_str, CFRangeMake(0, str_len), string); - - /* Create a default text style with the default font. */ - ATSUStyle style; - ATSUCreateStyle(&style); - - /* Create a text layout object from the sample string using the text style. */ - UniCharCount run_len = kATSUToTextEnd; - ATSUTextLayout text_layout; - ATSUCreateTextLayoutWithTextPtr(string, kATSUFromTextBeginning, kATSUToTextEnd, str_len, 1, &run_len, &style, &text_layout); - - /* Try to match a font for the sample text. ATSUMatchFontsToText stops after - * it finds the first continuous character run not renderable with the currently - * selected font starting at offset. The matching needs to be repeated until - * the end of the string is reached to make sure the fallback font matches for - * all characters in the string and not only the first run. */ - UniCharArrayOffset offset = kATSUFromTextBeginning; - OSStatus os_err; - do { - ATSUFontID font; - UniCharCount run_len; - os_err = ATSUMatchFontsToText(text_layout, offset, kATSUToTextEnd, &font, &offset, &run_len); - if (os_err == kATSUFontsMatched) { - /* Found a better fallback font. Update the text layout - * object with the new font. */ - ATSUAttributeTag tag = kATSUFontTag; - ByteCount size = sizeof(font); - ATSUAttributeValuePtr val = &font; - ATSUSetAttributes(style, 1, &tag, &size, &val); - offset += run_len; - } - /* Exit if the end of the string is reached or some other error occurred. */ - } while (os_err == kATSUFontsMatched && offset < (UniCharArrayOffset)str_len); - - if (os_err == noErr || os_err == kATSUFontsMatched) { - /* ATSUMatchFontsToText exited normally. Extract font - * out of the text layout object. */ - ATSUFontID font; - ByteCount act_len; - ATSUGetAttribute(style, kATSUFontTag, sizeof(font), &font, &act_len); - - /* Get unique font name. The result is not a c-string, we have - * to leave space for a \0 and terminate it ourselves. */ + /* Create a font iterator and iterate over all fonts that + * are available to the application. */ + ATSFontIterator itr; + ATSFontRef font; + ATSFontIteratorCreate(kATSFontContextLocal, NULL, NULL, kATSOptionFlagsDefaultScope, &itr); + while (!result && ATSFontIteratorNext(itr, &font) == noErr) { + /* Get font name. */ char name[128]; - ATSUFindFontName(font, kFontUniqueName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, 127, name, &act_len, NULL); - name[act_len > 127 ? 127 : act_len] = '\0'; + CFStringRef font_name; + ATSFontGetName(font, kATSOptionFlagsDefault, &font_name); + CFStringGetCString(font_name, name, lengthof(name), kCFStringEncodingUTF8); + CFRelease(font_name); + + /* We only want the base font and not bold or italic variants. */ + if (strstr(name, "Italic") != NULL || strstr(name, "Bold")) continue; + + /* Skip some inappropriate or ugly looking fonts that have better alternatives. */ + if (strncmp(name, "Courier", 7) == 0 || strncmp(name, "Apple Symbols", 13) == 0 || + strncmp(name, ".Aqua", 5) == 0 || strncmp(name, "LastResort", 10) == 0 || + strncmp(name, "GB18030 Bitmap", 14) == 0) continue; - /* Save Result. */ + /* Save result. */ callback->SetFontNames(settings, name); - DEBUG(freetype, 2, "ATSUI-Font for %s: %s", language_isocode, name); - result = true; + if (!callback->FindMissingGlyphs(NULL)) { + DEBUG(freetype, 2, "ATS-Font for %s: %s", language_isocode, name); + result = true; + break; + } } - - ATSUDisposeTextLayout(text_layout); - ATSUDisposeStyle(style); - CFRelease(cf_str); -#endif + ATSFontIteratorRelease(&itr); } - if (result && strncmp(settings->medium.font, "Geeza Pro", 9) == 0) { - /* The font 'Geeza Pro' is often found for arabic characters, but - * it has the 'tiny' problem of not having any latin characters. - * 'Arial Unicode MS' on the other hand has arabic and latin glyphs, - * but seems to 'forget' to inform the OS about this fact. Manually - * substitute the latter for the former if it is loadable. */ - bool ft_init = _library != NULL; - FT_Face face; - /* Init FreeType if needed. */ - if ((ft_init || FT_Init_FreeType(&_library) == FT_Err_Ok) && GetFontByFaceName("Arial Unicode MS", &face) == FT_Err_Ok) { - FT_Done_Face(face); - callback->SetFontNames(settings, "Arial Unicode MS"); - DEBUG(freetype, 1, "Replacing font 'Geeza Pro' with 'Arial Unicode MS'"); - } - if (!ft_init) { - /* Uninit FreeType if we did the init. */ - FT_Done_FreeType(_library); - _library = NULL; - } - } + if (!result) { + /* For some OS versions, the font 'Arial Unicode MS' does not report all languages it + * supports. If we didn't find any other font, just try it, maybe we get lucky. */ + callback->SetFontNames(settings, "Arial Unicode MS"); + result = !callback->FindMissingGlyphs(NULL); + } callback->FindMissingGlyphs(NULL); return result;