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;
}
Exemple #2
0
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;
}