bool QFontEngineMacMulti::canRender(const QChar *string, int len) { ATSUSetTextPointerLocation(textLayout, reinterpret_cast<const UniChar *>(string), 0, len, len); ATSUSetRunStyle(textLayout, style, 0, len); OSStatus e = noErr; int pos = 0; do { FMFont substFont = 0; UniCharArrayOffset changedOffset = 0; UniCharCount changeCount = 0; e = ATSUMatchFontsToText(textLayout, pos, len - pos, &substFont, &changedOffset, &changeCount); if (e == kATSUFontsMatched) { pos = changedOffset + changeCount; } else if (e == kATSUFontsNotMatched) { break; } } while (pos < len && e != noErr); return e == noErr || e == kATSUFontsMatched; }
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 * to find a suitable font. CoreText is available from 10.5 onwards. */ char lang[16]; if (strcmp(language_isocode, "zh_TW") == 0) { /* Traditional Chinese */ strecpy(lang, "zh-Hant", lastof(lang)); } 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)); char *sep = strchr(lang, '_'); if (sep != NULL) *sep = '\0'; } CFStringRef lang_code; lang_code = CFStringCreateWithCString(kCFAllocatorDefault, lang, kCFStringEncodingUTF8); /* 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); } CFRelease(font_ref); } ATSFontIteratorRelease(&itr); CFRelease(lang_code); } 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. */ char name[128]; ATSUFindFontName(font, kFontUniqueName, kFontNoPlatformCode, kFontNoScriptCode, kFontNoLanguageCode, 127, name, &act_len, NULL); name[act_len > 127 ? 127 : act_len] = '\0'; /* Save Result. */ callback->SetFontNames(settings, name); DEBUG(freetype, 2, "ATSUI-Font for %s: %s", language_isocode, name); result = true; } ATSUDisposeTextLayout(text_layout); ATSUDisposeStyle(style); CFRelease(cf_str); #endif } 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; } } callback->FindMissingGlyphs(NULL); return result; }
bool QFontEngineMacMulti::stringToCMapInternal(const QChar *str, int len, QGlyphLayout *glyphs, int *nglyphs, QTextEngine::ShaperFlags flags, QShaperItem *shaperItem) const { //qDebug() << "stringToCMap" << QString(str, len); OSStatus e = noErr; e = ATSUSetTextPointerLocation(textLayout, (UniChar *)(str), 0, len, len); if (e != noErr) { qWarning("Qt: internal: %ld: Error ATSUSetTextPointerLocation %s: %d", long(e), __FILE__, __LINE__); return false; } QGlyphLayoutInfo nfo; nfo.glyphs = glyphs; nfo.numGlyphs = nglyphs; nfo.callbackCalled = false; nfo.flags = flags; nfo.shaperItem = shaperItem; QVarLengthArray<int> mappedFonts(len); for (int i = 0; i < len; ++i) mappedFonts[i] = 0; nfo.mappedFonts = mappedFonts.data(); Q_ASSERT(sizeof(void *) <= sizeof(URefCon)); e = ATSUSetTextLayoutRefCon(textLayout, (URefCon)&nfo); if (e != noErr) { qWarning("Qt: internal: %ld: Error ATSUSetTextLayoutRefCon %s: %d", long(e), __FILE__, __LINE__); return false; } { const int maxAttributeCount = 3; ATSUAttributeTag tags[maxAttributeCount + 1]; ByteCount sizes[maxAttributeCount + 1]; ATSUAttributeValuePtr values[maxAttributeCount + 1]; int attributeCount = 0; tags[attributeCount] = kATSULineLayoutOptionsTag; ATSLineLayoutOptions layopts = kATSLineHasNoOpticalAlignment | kATSLineIgnoreFontLeading | kATSLineNoSpecialJustification // we do kashidas ourselves | kATSLineDisableAllJustification ; if (!(flags & QTextEngine::DesignMetrics)) { layopts |= kATSLineFractDisable | kATSLineUseDeviceMetrics | kATSLineDisableAutoAdjustDisplayPos; } if (fontDef.styleStrategy & QFont::NoAntialias) layopts |= kATSLineNoAntiAliasing; if (!kerning) layopts |= kATSLineDisableAllKerningAdjustments; values[attributeCount] = &layopts; sizes[attributeCount] = sizeof(layopts); ++attributeCount; tags[attributeCount] = kATSULayoutOperationOverrideTag; ATSULayoutOperationOverrideSpecifier spec; spec.operationSelector = kATSULayoutOperationPostLayoutAdjustment; spec.overrideUPP = atsuPostLayoutCallback; values[attributeCount] = &spec; sizes[attributeCount] = sizeof(spec); ++attributeCount; Boolean direction; if (flags & QTextEngine::RightToLeft) direction = kATSURightToLeftBaseDirection; else direction = kATSULeftToRightBaseDirection; tags[attributeCount] = kATSULineDirectionTag; values[attributeCount] = &direction; sizes[attributeCount] = sizeof(direction); ++attributeCount; Q_ASSERT(attributeCount < maxAttributeCount + 1); e = ATSUSetLayoutControls(textLayout, attributeCount, tags, sizes, values); if (e != noErr) { qWarning("Qt: internal: %ld: Error ATSUSetLayoutControls %s: %d", long(e), __FILE__, __LINE__); return false; } } e = ATSUSetRunStyle(textLayout, style, 0, len); if (e != noErr) { qWarning("Qt: internal: %ld: Error ATSUSetRunStyle %s: %d", long(e), __FILE__, __LINE__); return false; } if (!(fontDef.styleStrategy & QFont::NoFontMerging)) { int pos = 0; do { ATSUFontID substFont = 0; UniCharArrayOffset changedOffset = 0; UniCharCount changeCount = 0; e = ATSUMatchFontsToText(textLayout, pos, len - pos, &substFont, &changedOffset, &changeCount); if (e == kATSUFontsMatched) { int fontIdx = fontIndexForFontID(substFont); for (uint i = 0; i < changeCount; ++i) mappedFonts[changedOffset + i] = fontIdx; pos = changedOffset + changeCount; ATSUSetRunStyle(textLayout, engineAt(fontIdx)->style, changedOffset, changeCount); } else if (e == kATSUFontsNotMatched) { pos = changedOffset + changeCount; } } while (pos < len && e != noErr); } { // trigger the a layout Rect rect; e = ATSUMeasureTextImage(textLayout, kATSUFromTextBeginning, kATSUToTextEnd, /*iLocationX =*/ 0, /*iLocationY =*/ 0, &rect); if (e != noErr) { qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage %s: %d", long(e), __FILE__, __LINE__); return false; } } if (!nfo.callbackCalled) { qWarning("Qt: internal: %ld: Error ATSUMeasureTextImage did not trigger callback %s: %d", long(e), __FILE__, __LINE__); return false; } ATSUClearLayoutCache(textLayout, kATSUFromTextBeginning); return true; }