diff --git a/gfx/thebes/public/gfxFont.h b/gfx/thebes/public/gfxFont.h index c20f6f4d522..1ab28acf941 100644 --- a/gfx/thebes/public/gfxFont.h +++ b/gfx/thebes/public/gfxFont.h @@ -998,6 +998,21 @@ public: PRUint32 mEndOffset; }; + class GlyphRunOffsetComparator { + public: + PRBool Equals(const GlyphRun& a, + const GlyphRun& b) const + { + return a.mCharacterOffset == b.mCharacterOffset; + } + + PRBool LessThan(const GlyphRun& a, + const GlyphRun& b) const + { + return a.mCharacterOffset < b.mCharacterOffset; + } + }; + friend class GlyphRunIterator; friend class FontSelector; @@ -1013,9 +1028,18 @@ public: * only during initialization when font substitution has been computed. * Call it before setting up the glyphs for the characters in this run; * SetMissingGlyph requires that the correct glyphrun be installed. + * + * If aForceNewRun, a new glyph run will be added, even if the + * previously added run uses the same font. If glyph runs are + * added out of strictly increasing aStartCharIndex order (via + * force), then SortGlyphRuns must be called after all glyph runs + * are added before any further operations are performed with this + * TextRun. */ - nsresult AddGlyphRun(gfxFont *aFont, PRUint32 aStartCharIndex); + nsresult AddGlyphRun(gfxFont *aFont, PRUint32 aStartCharIndex, PRBool aForceNewRun = PR_FALSE); void ResetGlyphRuns() { mGlyphRuns.Clear(); } + void SortGlyphRuns(); + // Call the following glyph-setters during initialization or during reshaping // only. It is OK to overwrite existing data for a character. /** diff --git a/gfx/thebes/public/gfxPlatform.h b/gfx/thebes/public/gfxPlatform.h index eb863395868..00f7bd35734 100644 --- a/gfx/thebes/public/gfxPlatform.h +++ b/gfx/thebes/public/gfxPlatform.h @@ -133,7 +133,7 @@ public: PRUint32 height, PRUint32 stride); - void GetPrefFonts(const char *aLangGroup, nsString& array); + void GetPrefFonts(const char *aLangGroup, nsString& array, PRBool aAppendUnicode = PR_TRUE); protected: gfxPlatform() { } diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in index 6ce0093d9ef..a6ebaec72d7 100644 --- a/gfx/thebes/src/Makefile.in +++ b/gfx/thebes/src/Makefile.in @@ -107,6 +107,7 @@ endif ifneq (,$(filter $(MOZ_WIDGET_TOOLKIT),mac cocoa)) CPPSRCS += gfxQuartzSurface.cpp gfxQuartzPDFSurface.cpp gfxPlatformMac.cpp gfxAtsuiFonts.cpp #CPPSRCS += gfxPDFSurface.cpp +CPPSRCS += nsUnicodeRange.cpp CMMSRCS = gfxQuartzFontCache.mm diff --git a/gfx/thebes/src/gfxAtsuiFonts.cpp b/gfx/thebes/src/gfxAtsuiFonts.cpp index ee413c4fef5..878e5cf084d 100644 --- a/gfx/thebes/src/gfxAtsuiFonts.cpp +++ b/gfx/thebes/src/gfxAtsuiFonts.cpp @@ -46,6 +46,7 @@ #include "nsPromiseFlatString.h" #include "gfxContext.h" +#include "gfxPlatform.h" #include "gfxAtsuiFonts.h" #include "gfxFontTest.h" @@ -55,6 +56,12 @@ #include "gfxQuartzSurface.h" #include "gfxQuartzFontCache.h" +#include "nsIPrefBranch.h" +#include "nsIPrefService.h" +#include "nsServiceManagerUtils.h" +#include "nsUnicodeRange.h" +#include "nsCRT.h" + // Uncomment this to dump all text runs created to stdout // #define DUMP_TEXT_RUNS @@ -280,6 +287,26 @@ gfxAtsuiFont::GetMetrics() return mMetrics; } +static nsresult +CreateFontFallbacksFromFontList(nsTArray< nsRefPtr > *aFonts, + ATSUFontFallbacks *aFallbacks, + ATSUFontFallbackMethod aMethod) +{ + // Create the fallback structure + OSStatus status = ::ATSUCreateFontFallbacks(aFallbacks); + NS_ENSURE_TRUE(status == noErr, NS_ERROR_FAILURE); + + nsAutoTArray fids; + + for (unsigned int i = 0; i < aFonts->Length(); i++) { + gfxAtsuiFont* atsuiFont = NS_STATIC_CAST(gfxAtsuiFont*, aFonts->ElementAt(i).get()); + fids.AppendElement(atsuiFont->GetATSUFontID()); + } + status = ::ATSUSetObjFontFallbacks(*aFallbacks, fids.Length(), fids.Elements(), aMethod); + + return status == noErr ? NS_OK : NS_ERROR_FAILURE; +} + PRBool gfxAtsuiFont::HasMirroringInfo() { @@ -345,28 +372,7 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(const nsAString& families, GetOrMakeFont(fontID, aStyle, &mFonts); } - // Create the fallback structure - ATSUCreateFontFallbacks(&mFallbacks); - -#define NUM_STATIC_FIDS 16 - ATSUFontID static_fids[NUM_STATIC_FIDS]; - ATSUFontID *fids; - if (mFonts.Length() > NUM_STATIC_FIDS) - fids = (ATSUFontID *) PR_Malloc(sizeof(ATSUFontID) * mFonts.Length()); - else - fids = static_fids; - - for (unsigned int i = 0; i < mFonts.Length(); i++) { - gfxAtsuiFont* atsuiFont = static_cast(static_cast(mFonts[i])); - fids[i] = atsuiFont->GetATSUFontID(); - } - ATSUSetObjFontFallbacks(mFallbacks, - mFonts.Length(), - fids, - kATSUSequentialFallbacksPreferred /* kATSUSequentialFallbacksExclusive? */); - - if (fids != static_fids) - PR_Free(fids); + CreateFontFallbacksFromFontList(&mFonts, &mFallbacks, kATSULastResortOnlyFallback); } PRBool @@ -381,7 +387,7 @@ gfxAtsuiFontGroup::FindATSUFont(const nsAString& aName, ATSUFontID fontID = fc->FindATSUFontIDForFamilyAndStyle (aName, fontStyle); if (fontID != kATSUInvalidFontID) { - //printf ("FindATSUFont! %s %d -> %d\n", NS_ConvertUTF16toUTF8(aName).get(), fontStyle->weight, (int)fontID); + //fprintf (stderr, "..FindATSUFont: %s\n", NS_ConvertUTF16toUTF8(aName).get()); GetOrMakeFont(fontID, fontStyle, &fontGroup->mFonts); } @@ -920,10 +926,256 @@ PostLayoutOperationCallback(ATSULayoutOperationSelector iCurrentOperation, return noErr; } +// The lang IDs for font prefs +enum eFontPrefLang { + eFontPrefLang_Western = 0, + eFontPrefLang_CentEuro = 1, + eFontPrefLang_Japanese = 2, + eFontPrefLang_ChineseTW = 3, + eFontPrefLang_ChineseCN = 4, + eFontPrefLang_ChineseHK = 5, + eFontPrefLang_Korean = 6, + eFontPrefLang_Cyrillic = 7, + eFontPrefLang_Baltic = 8, + eFontPrefLang_Greek = 9, + eFontPrefLang_Turkish = 10, + eFontPrefLang_Thai = 11, + eFontPrefLang_Hebrew = 12, + eFontPrefLang_Arabic = 13, + eFontPrefLang_Devanagari = 14, + eFontPrefLang_Tamil = 15, + eFontPrefLang_Armenian = 16, + eFontPrefLang_Bengali = 17, + eFontPrefLang_Canadian = 18, + eFontPrefLang_Ethiopic = 19, + eFontPrefLang_Georgian = 20, + eFontPrefLang_Gujarati = 21, + eFontPrefLang_Gurmukhi = 22, + eFontPrefLang_Khmer = 23, + eFontPrefLang_Malayalam = 24, + + eFontPrefLang_LangCount = 25, // except Others and UserDefined. + + eFontPrefLang_Others = 25, // x-unicode + eFontPrefLang_UserDefined = 26, + + eFontPrefLang_CJKSet = 27, // special code for CJK set + eFontPrefLang_AllCount = 28 +}; + +// The lang names for eFontPrefLang +static const char *gPrefLangNames[] = { + "x-western", + "x-central-euro", + "ja", + "zh-TW", + "zh-CN", + "zh-HK", + "ko", + "x-cyrillic", + "x-baltic", + "el", + "tr", + "th", + "he", + "ar", + "x-devanagari", + "x-tamil", + "x-armn", + "x-beng", + "x-cans", + "x-ethi", + "x-geor", + "x-gujr", + "x-guru", + "x-khmr", + "x-mlym", + "x-unicode", + "x-user-def" +}; + +static eFontPrefLang +GetFontPrefLangFor(const char* aLang) +{ + if (!aLang || aLang[0]) + return eFontPrefLang_Others; + for (PRUint32 i = 0; i < PRUint32(eFontPrefLang_LangCount); ++i) { + if (!PL_strcasecmp(gPrefLangNames[i], aLang)) + return eFontPrefLang(i); + } + return eFontPrefLang_Others; +} + +eFontPrefLang +GetFontPrefLangFor(PRUint8 aUnicodeRange) +{ + switch (aUnicodeRange) { + case kRangeCyrillic: return eFontPrefLang_Cyrillic; + case kRangeGreek: return eFontPrefLang_Greek; + case kRangeTurkish: return eFontPrefLang_Turkish; + case kRangeHebrew: return eFontPrefLang_Hebrew; + case kRangeArabic: return eFontPrefLang_Arabic; + case kRangeBaltic: return eFontPrefLang_Baltic; + case kRangeThai: return eFontPrefLang_Thai; + case kRangeKorean: return eFontPrefLang_Korean; + case kRangeJapanese: return eFontPrefLang_Japanese; + case kRangeSChinese: return eFontPrefLang_ChineseCN; + case kRangeTChinese: return eFontPrefLang_ChineseTW; + case kRangeDevanagari: return eFontPrefLang_Devanagari; + case kRangeTamil: return eFontPrefLang_Tamil; + case kRangeArmenian: return eFontPrefLang_Armenian; + case kRangeBengali: return eFontPrefLang_Bengali; + case kRangeCanadian: return eFontPrefLang_Canadian; + case kRangeEthiopic: return eFontPrefLang_Ethiopic; + case kRangeGeorgian: return eFontPrefLang_Georgian; + case kRangeGujarati: return eFontPrefLang_Gujarati; + case kRangeGurmukhi: return eFontPrefLang_Gurmukhi; + case kRangeKhmer: return eFontPrefLang_Khmer; + case kRangeMalayalam: return eFontPrefLang_Malayalam; + case kRangeSetCJK: return eFontPrefLang_CJKSet; + default: return eFontPrefLang_Others; + } +} + +static const char* +GetPrefLangName(eFontPrefLang aLang) +{ + if (PRUint32(aLang) < PRUint32(eFontPrefLang_AllCount)) + return gPrefLangNames[PRUint32(aLang)]; + return nsnull; +} + +struct AFLClosure { + const gfxFontStyle *style; + nsTArray > *fontArray; +}; + +PRBool +AppendFontToList(const nsAString& aName, + const nsACString& aGenericName, + void *closure) +{ + struct AFLClosure *afl = (struct AFLClosure *) closure; + + gfxQuartzFontCache *fc = gfxQuartzFontCache::SharedFontCache(); + ATSUFontID fontID = fc->FindATSUFontIDForFamilyAndStyle (aName, afl->style); + + if (fontID != kATSUInvalidFontID) { + //fprintf (stderr, "..AppendFontToList: %s\n", NS_ConvertUTF16toUTF8(aName).get()); + GetOrMakeFont(fontID, afl->style, afl->fontArray); + } + + return PR_TRUE; +} + +static nsresult +AppendPrefFonts(nsTArray > *aFonts, + eFontPrefLang aLang, + PRUint32& didAppendBits, + const gfxFontStyle *aStyle) +{ + if (didAppendBits & (1 << aLang)) + return NS_OK; + + didAppendBits |= (1 << aLang); + + const char* langGroup = GetPrefLangName(aLang); + if (!langGroup || !langGroup[0]) { + NS_ERROR("The langGroup is null"); + return NS_ERROR_FAILURE; + } + gfxPlatform *platform = gfxPlatform::GetPlatform(); + NS_ENSURE_TRUE(platform, NS_ERROR_OUT_OF_MEMORY); + nsString fonts; + platform->GetPrefFonts(langGroup, fonts, PR_FALSE); + if (fonts.IsEmpty()) + return NS_OK; + + struct AFLClosure afl = { aStyle, aFonts }; + gfxFontGroup::ForEachFont(fonts, nsDependentCString(langGroup), + AppendFontToList, &afl); + return NS_OK; +} + +static nsresult +AppendCJKPrefFonts(nsTArray > *aFonts, + PRUint32& didAppendBits, + const gfxFontStyle *aStyle) +{ + nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); + NS_ENSURE_TRUE(prefs, NS_ERROR_OUT_OF_MEMORY); + + nsCOMPtr prefBranch; + prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); + NS_ENSURE_TRUE(prefBranch, NS_ERROR_OUT_OF_MEMORY); + + // Add the CJK pref fonts from accept languages, the order should be same order + nsXPIDLCString list; + nsresult rv = prefBranch->GetCharPref("intl.accept_languages", getter_Copies(list)); + if (NS_SUCCEEDED(rv) && !list.IsEmpty()) { + const char kComma = ','; + const char *p, *p_end; + list.BeginReading(p); + list.EndReading(p_end); + while (p < p_end) { + while (nsCRT::IsAsciiSpace(*p)) { + if (++p == p_end) + break; + } + if (p == p_end) + break; + const char *start = p; + while (++p != p_end && *p != kComma) + /* nothing */ ; + nsCAutoString lang(Substring(start, p)); + lang.CompressWhitespace(PR_FALSE, PR_TRUE); + eFontPrefLang fpl = GetFontPrefLangFor(lang.get()); + switch (fpl) { + case eFontPrefLang_Japanese: + case eFontPrefLang_Korean: + case eFontPrefLang_ChineseCN: + case eFontPrefLang_ChineseHK: + case eFontPrefLang_ChineseTW: + rv = AppendPrefFonts(aFonts, fpl, didAppendBits, aStyle); + NS_ENSURE_SUCCESS(rv, rv); + break; + default: + break; + } + p++; + } + } + + // Prefer the system locale if it is CJK. + ScriptCode sysScript = ::GetScriptManagerVariable(smSysScript); + // XXX Is not there the HK locale? + switch (sysScript) { + case smJapanese: rv = AppendPrefFonts(aFonts, eFontPrefLang_Japanese, didAppendBits, aStyle); break; + case smTradChinese: rv = AppendPrefFonts(aFonts, eFontPrefLang_ChineseTW, didAppendBits, aStyle); break; + case smKorean: rv = AppendPrefFonts(aFonts, eFontPrefLang_Korean, didAppendBits, aStyle); break; + case smSimpChinese: rv = AppendPrefFonts(aFonts, eFontPrefLang_ChineseCN, didAppendBits, aStyle); break; + default: rv = NS_OK; + } + NS_ENSURE_SUCCESS(rv, rv); + + // last resort... (the order is same as old gfx.) + rv = AppendPrefFonts(aFonts, eFontPrefLang_Japanese, didAppendBits, aStyle); + NS_ENSURE_SUCCESS(rv, rv); + rv = AppendPrefFonts(aFonts, eFontPrefLang_Korean, didAppendBits, aStyle); + NS_ENSURE_SUCCESS(rv, rv); + rv = AppendPrefFonts(aFonts, eFontPrefLang_ChineseCN, didAppendBits, aStyle); + NS_ENSURE_SUCCESS(rv, rv); + rv = AppendPrefFonts(aFonts, eFontPrefLang_ChineseHK, didAppendBits, aStyle); + NS_ENSURE_SUCCESS(rv, rv); + rv = AppendPrefFonts(aFonts, eFontPrefLang_ChineseTW, didAppendBits, aStyle); + return rv; +} + static void AddGlyphRun(gfxTextRun *aRun, gfxAtsuiFont *aFont, PRUint32 aOffset) { - aRun->AddGlyphRun(aFont, aOffset); + //fprintf (stderr, "+ AddGlyphRun: %d %s\n", aOffset, NS_ConvertUTF16toUTF8(aFont->GetUniqueName()).get()); + aRun->AddGlyphRun(aFont, aOffset, PR_TRUE); if (!aRun->IsClusterStart(aOffset)) { // Glyph runs must start at cluster boundaries. However, sometimes // ATSUI matches different fonts for characters in the same cluster. @@ -977,8 +1229,8 @@ DisableOptionalLigaturesInStyle(ATSUStyle aStyle) // @param runLength length of substring to be mirrored static void MirrorSubstring(ATSUTextLayout layout, nsAutoArrayPtr& mirroredStr, - const PRUnichar *aString, PRUint32 aLength, - UniCharArrayOffset runStart, UniCharCount runLength) + const PRUnichar *aString, PRUint32 aLength, + UniCharArrayOffset runStart, UniCharCount runLength) { UniCharArrayOffset off; @@ -1011,8 +1263,8 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, PRUint32 aSegmentLength) { OSStatus status; - gfxAtsuiFont *atsuiFont = GetFontAt(0); - ATSUStyle mainStyle = atsuiFont->GetATSUStyle(); + gfxAtsuiFont *firstFont = GetFontAt(0); + ATSUStyle mainStyle = firstFont->GetATSUStyle(); nsTArray stylesToDispose; PRUint32 headerChars = aWrapped ? 1 : 0; const PRUnichar *realString = aString + headerChars; @@ -1090,113 +1342,225 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, /* Now go through and update the styles for the text, based on font matching. */ + nsAutoArrayPtr mirroredStr; + nsTArray missingOffsetsAndLengths; + nsTArray fallbacksToDispose; + + PRBool firstTime = PR_TRUE; + UniCharArrayOffset runStart = headerChars; - UniCharCount totalLength = runStart + aSegmentLength; UniCharCount runLength = aSegmentLength; - nsAutoArrayPtr mirroredStr; + UniCharCount totalLength = headerChars + aSegmentLength; + + nsresult rv; - //fprintf (stderr, "==== Starting font maching [string length: %d]\n", totalLength); - while (runStart < totalLength) { - ATSUFontID substituteFontID; - UniCharArrayOffset changedOffset; - UniCharCount changedLength; + //fprintf (stderr, "==== Starting font maching [string length: %d (%s)]\n", totalLength, NS_ConvertUTF16toUTF8(aString, aLength).get()); + do { + PRUint32 missingRanges = missingOffsetsAndLengths.Length() / 2; + UniCharArrayOffset missingStart = 0; + UniCharCount missingLength = 0; - OSStatus status = ATSUMatchFontsToText(layout, runStart, runLength, - &substituteFontID, &changedOffset, &changedLength); - if (status == noErr) { - - // glyphs exist for all characters in the [runStart, runStart + runLength) substring - - // in the RTL case, handle fallback mirroring - if (aRun->IsRightToLeft() && !atsuiFont->HasMirroringInfo()) { - MirrorSubstring(layout, mirroredStr, aString, aLength, runStart, runLength); - } - - // add a glyph run for the entire substring - AddGlyphRun(aRun, atsuiFont, aSegmentStart + runStart - headerChars); - - break; + //fprintf (stderr, "(loop start, missing ranges: %d)\n", missingRanges); - } else if (status == kATSUFontsMatched) { - - // substitute font will be used in [changedOffset, changedOffset + changedLength) - - // create a new style for the substitute font - ATSUStyle subStyle; - ATSUCreateStyle (&subStyle); - ATSUCopyAttributes (mainStyle, subStyle); + if (missingRanges > 0) { + // we had some misses. Let's figure out what languages we need + // and pull in the fallback fonts from prefs. - ATSUAttributeTag fontTags[] = { kATSUFontTag }; - ByteCount fontArgSizes[] = { sizeof(ATSUFontID) }; - ATSUAttributeValuePtr fontArgs[] = { &substituteFontID }; + missingStart = missingOffsetsAndLengths[(missingRanges-1) * 2]; + missingLength = missingOffsetsAndLengths[(missingRanges-1) * 2 + 1]; + missingOffsetsAndLengths.RemoveElementsAt((missingRanges-1) * 2, 2); - ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs); + runStart = missingStart; + runLength = missingLength; - // apply the new style to the layout for the changed substring - ATSUSetRunStyle (layout, subStyle, changedOffset, changedLength); + //fprintf (stderr, ".. range: %d %d\n", runStart, runLength); - // if needed, add a glyph run for [runStart, changedOffset) with the original font - if (changedOffset > runStart) { - - // in the RTL case, handle fallback mirroring - if (aRun->IsRightToLeft() && !atsuiFont->HasMirroringInfo()) { - MirrorSubstring(layout, mirroredStr, aString, aLength, runStart, - changedOffset - runStart); - } - - AddGlyphRun(aRun, atsuiFont, aSegmentStart + runStart - headerChars); + totalLength = runStart + runLength; + + PRUint32 didAppendFonts = 0; + PRUint32 lastRange = kRangeTableBase; + + nsAutoTArray,3> mLangFonts; + + for (PRUint32 j = 0; j < runLength; j++) { + PRUint32 unicodeRange = FindCharUnicodeRange(aString[j+runStart]); + if (unicodeRange == lastRange) + continue; + + lastRange = unicodeRange; + eFontPrefLang prefLang = GetFontPrefLangFor(unicodeRange); + + if (prefLang == eFontPrefLang_CJKSet) + rv = AppendCJKPrefFonts(&mLangFonts, didAppendFonts, GetStyle()); + else + rv = AppendPrefFonts(&mLangFonts, prefLang, didAppendFonts, GetStyle()); + if (NS_FAILED(rv)) + break; } - // add a glyph run for [changedOffset, changedOffset + changedLength) with the - // substituted font - gfxAtsuiFont *font = FindFontFor(substituteFontID); - if (font) { + if (NS_FAILED(rv)) + break; - // in the RTL case, handle fallback mirroring - if (aRun->IsRightToLeft() && !font->HasMirroringInfo()) { - MirrorSubstring(layout, mirroredStr, aString, aLength, changedOffset, - changedLength); - } - - AddGlyphRun(aRun, font, aSegmentStart + changedOffset - headerChars); - } - - stylesToDispose.AppendElement(subStyle); - - } else if (status == kATSUFontsNotMatched) { - - //fprintf (stderr, "ATSUMatchFontsToText returned kATSUFontsNotMatched\n"); - /* I need to select the last resort font; how the heck do I do that? */ - // Record which font is associated with these glyphs, anyway + // always append Unicode + rv = AppendPrefFonts(&mLangFonts, eFontPrefLang_Others, didAppendFonts, GetStyle()); + if (NS_FAILED(rv)) + break; - // hmmm, so was changedOffset set? we appear to ignore it... - - // in the RTL case, handle fallback mirroring - if (aRun->IsRightToLeft() && !atsuiFont->HasMirroringInfo()) { - MirrorSubstring(layout, mirroredStr, aString, aLength, runStart, runLength); - } - - AddGlyphRun(aRun, atsuiFont, aSegmentStart + runStart - headerChars); - - if (!closure.mUnmatchedChars) { - closure.mUnmatchedChars = new PRPackedBool[aLength]; - if (closure.mUnmatchedChars) { - memset(closure.mUnmatchedChars.get(), PR_FALSE, aLength); - } - } - if (closure.mUnmatchedChars) { - memset(closure.mUnmatchedChars.get() + changedOffset - headerChars, - PR_TRUE, changedLength); - } - + // now we have a list of fonts in mLangFonts (potentially -- 0-length is ok) + ATSUFontFallbacks langFallbacks; + rv = CreateFontFallbacksFromFontList(&mLangFonts, &langFallbacks, kATSUSequentialFallbacksPreferred); + if (NS_FAILED(rv)) + break; + + fallbacksToDispose.AppendElement(langFallbacks); + + static ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag }; + static ByteCount fallbackArgSizes[] = { sizeof(ATSUFontFallbacks) }; + ATSUAttributeValuePtr fallbackArgs[] = { &langFallbacks }; + + ATSUSetLayoutControls(layout, + NS_ARRAY_LENGTH(fallbackTags), + fallbackTags, fallbackArgSizes, fallbackArgs); } - //fprintf (stderr, "total length: %d changedOffset: %d changedLength: %d\p=n", runLength, changedOffset, changedLength); + while (runStart < totalLength) { + ATSUFontID substituteFontID; + UniCharArrayOffset changedOffset; + UniCharCount changedLength; + UniCharCount foundCharacters; - runStart = changedOffset + changedLength; - runLength = totalLength - runStart; + OSStatus status = ATSUMatchFontsToText(layout, runStart, runLength, + &substituteFontID, &changedOffset, &changedLength); + + if (status == noErr) { + foundCharacters = runLength; + changedLength = 0; + } else { + foundCharacters = changedOffset - runStart; + } + + // first, handle any characters that were matched with firstFont + if (foundCharacters) { + //fprintf (stderr, "... glyphs found in first: %d %d\n", runStart, foundCharacters); + // glyphs exist for all characters in the [runStart, runStart + foundCharacters) substring + + // in the RTL case, handle fallback mirroring + if (aRun->IsRightToLeft() && !firstFont->HasMirroringInfo()) { + MirrorSubstring(layout, mirroredStr, aString, aLength, runStart, runLength); + } + + // add a glyph run for the entire substring + AddGlyphRun(aRun, firstFont, aSegmentStart + runStart - headerChars); + + // do we have any more work to do? + if (status == noErr) + break; + } + + // then, handle any chars that were found in the fallback list + if (status == kATSUFontsMatched) { + // substitute font will be used in [changedOffset, changedOffset + changedLength) + gfxAtsuiFont *font = FindFontFor(substituteFontID); + + //fprintf (stderr, "... glyphs matched in fallback: %d %d (%s)\n", changedOffset, changedLength, NS_ConvertUTF16toUTF8(font->GetUniqueName()).get()); + + if (font) { + // create a new style for the substitute font + ATSUStyle subStyle; + ATSUCreateStyle (&subStyle); + ATSUCopyAttributes (mainStyle, subStyle); + + ATSUAttributeTag fontTags[] = { kATSUFontTag }; + ByteCount fontArgSizes[] = { sizeof(ATSUFontID) }; + ATSUAttributeValuePtr fontArgs[] = { &substituteFontID }; + + ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs); + + // apply the new style to the layout for the changed substring + ATSUSetRunStyle (layout, subStyle, changedOffset, changedLength); + + stylesToDispose.AppendElement(subStyle); + + // add a glyph run for [changedOffset, changedOffset + changedLength) with the + // substituted font + + // in the RTL case, handle fallback mirroring + if (aRun->IsRightToLeft() && !font->HasMirroringInfo()) { + MirrorSubstring(layout, mirroredStr, aString, aLength, changedOffset, + changedLength); + } + + AddGlyphRun(aRun, font, aSegmentStart + changedOffset - headerChars); + } else { + // We could hit this case if we decided to ignore the + // font when enumerating at startup; pretend that these are + // missing glyphs. + // XXX - wait, why did it even end up in the fallback list, + // then? + + status = kATSUFontsNotMatched; + } + } + + if (status == kATSUFontsNotMatched) { + //fprintf (stderr, "... glyphs not found: %d %d\n", changedOffset, changedLength); + + // If this is our first time through (no fallback), + // or if we're missing a different range than before, + // add it to the list of ranges to examine + if (firstTime || + (changedOffset != missingStart && changedLength != missingLength)) + { + //fprintf (stderr, " (will look again at %d %d)\n", changedOffset, changedLength); + missingOffsetsAndLengths.AppendElement(changedOffset); + missingOffsetsAndLengths.AppendElement(changedLength); + } else { + AddGlyphRun(aRun, firstFont, aSegmentStart + changedOffset - headerChars); + + if (!closure.mUnmatchedChars) { + closure.mUnmatchedChars = new PRPackedBool[aLength]; + if (closure.mUnmatchedChars) { + memset(closure.mUnmatchedChars.get(), PR_FALSE, aLength); + } + } + + if (closure.mUnmatchedChars) { + memset(closure.mUnmatchedChars.get() + changedOffset - headerChars, + PR_TRUE, changedLength); + } + } + } + + //fprintf (stderr, "total length: %d changedOffset: %d changedLength: %d\n", runLength, changedOffset, changedLength); + + runStart = changedOffset + changedLength; + runLength = totalLength - runStart; + } + + firstTime = PR_FALSE; + } while (missingOffsetsAndLengths.Length() > 0); + + // put back the fallback object, because we'll be disposing + // of any of the ones we created while doing fallback + if (fallbacksToDispose.Length() > 0) { + static ATSUAttributeTag fallbackTags[] = { kATSULineFontFallbacksTag }; + static ByteCount fallbackArgSizes[] = { sizeof(ATSUFontFallbacks) }; + ATSUAttributeValuePtr fallbackArgs[] = { GetATSUFontFallbacksPtr() }; + + ATSUSetLayoutControls(layout, + NS_ARRAY_LENGTH(fallbackTags), + fallbackTags, fallbackArgSizes, fallbackArgs); } + for (PRUint32 i = 0; i < fallbacksToDispose.Length(); i++) + ATSUDisposeFontFallbacks(fallbacksToDispose[i]); + + // sort our glyph runs; we may have added + // some out of order while doing fallback font matching + aRun->SortGlyphRuns(); + + //fprintf (stderr, "==== End font matching\n"); + // Trigger layout so that our callback fires. We don't actually care about // the result of this call. ATSTrapezoid trap; @@ -1206,7 +1570,6 @@ gfxAtsuiFontGroup::InitTextRun(gfxTextRun *aRun, ATSUDisposeTextLayout(layout); - //fprintf (stderr, "==== End font matching\n"); PRUint32 i; for (i = 0; i < stylesToDispose.Length(); ++i) { ATSUDisposeStyle(stylesToDispose[i]); diff --git a/gfx/thebes/src/gfxFont.cpp b/gfx/thebes/src/gfxFont.cpp index de5ede5d9b8..8ee603caec9 100644 --- a/gfx/thebes/src/gfxFont.cpp +++ b/gfx/thebes/src/gfxFont.cpp @@ -1521,14 +1521,16 @@ gfxTextRun::FindFirstGlyphRunContaining(PRUint32 aOffset) } nsresult -gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset) +gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset, PRBool aForceNewRun) { PRUint32 numGlyphRuns = mGlyphRuns.Length(); - if (numGlyphRuns > 0) { + if (!aForceNewRun && + numGlyphRuns > 0) + { GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1]; NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset, - "Glyph runs out of order"); + "Glyph runs out of order (and run not forced)"); if (lastGlyphRun->mFont == aFont) return NS_OK; @@ -1538,8 +1540,8 @@ gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset) } } - NS_ASSERTION(numGlyphRuns > 0 || aUTF16Offset == 0, - "First run doesn't cover the first character?"); + NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0, + "First run doesn't cover the first character (and run not forced)?"); GlyphRun *glyphRun = mGlyphRuns.AppendElement(); if (!glyphRun) @@ -1549,6 +1551,13 @@ gfxTextRun::AddGlyphRun(gfxFont *aFont, PRUint32 aUTF16Offset) return NS_OK; } +void +gfxTextRun::SortGlyphRuns() +{ + GlyphRunOffsetComparator comp; + mGlyphRuns.Sort(comp); +} + PRUint32 gfxTextRun::CountMissingGlyphs() { diff --git a/gfx/thebes/src/gfxPlatform.cpp b/gfx/thebes/src/gfxPlatform.cpp index dcafbdb3266..ac046b01255 100644 --- a/gfx/thebes/src/gfxPlatform.cpp +++ b/gfx/thebes/src/gfxPlatform.cpp @@ -287,11 +287,12 @@ AppendGenericFontFromPref(nsString& aFonts, const char *aLangGroup, const char * } void -gfxPlatform::GetPrefFonts(const char *aLangGroup, nsString& aFonts) +gfxPlatform::GetPrefFonts(const char *aLangGroup, nsString& aFonts, PRBool aAppendUnicode) { aFonts.Truncate(); AppendGenericFontFromPref(aFonts, aLangGroup, nsnull); - AppendGenericFontFromPref(aFonts, "x-unicode", nsnull); + if (aAppendUnicode) + AppendGenericFontFromPref(aFonts, "x-unicode", nsnull); }