diff --git a/gfx/thebes/public/gfxQtFonts.h b/gfx/thebes/public/gfxQtFonts.h index 36f390e3899..a197394497a 100644 --- a/gfx/thebes/public/gfxQtFonts.h +++ b/gfx/thebes/public/gfxQtFonts.h @@ -42,9 +42,11 @@ #include "cairo.h" #include "gfxTypes.h" #include "gfxFont.h" +#include "gfxContext.h" #include "nsDataHashtable.h" #include "nsClassHashtable.h" +//#include class gfxQtFont : public gfxFont { @@ -53,7 +55,31 @@ public: const gfxFontStyle *aFontStyle); virtual ~gfxQtFont (); + virtual nsString GetUniqueName (); + + virtual PRUint32 GetSpaceGlyph () + { + NS_ASSERTION (GetStyle ()->size != 0, + "forgot to short-circuit a text run with zero-sized font?"); + GetMetrics (); + return mSpaceGlyph; + } + static void Shutdown(); + + virtual const gfxFont::Metrics& GetMetrics(); + +protected: + void *mQFont; + cairo_scaled_font_t *mCairoFont; + + PRBool mHasMetrics; + PRUint32 mSpaceGlyph; + Metrics mMetrics; + gfxFloat mAdjustedSize; + + virtual PRBool SetupCairoFont(gfxContext *aContext); + }; class THEBES_API gfxQtFontGroup : public gfxFontGroup { @@ -69,6 +95,19 @@ public: const Parameters *aParams, PRUint32 aFlags); virtual gfxTextRun *MakeTextRun(const PRUint8 *aString, PRUint32 aLength, const Parameters *aParams, PRUint32 aFlags); + + gfxQtFont * GetFontAt (PRInt32 i) { + return static_cast < gfxQtFont * >(static_cast < gfxFont * >(mFonts[i])); + } + +protected: + void InitTextRun (gfxTextRun * aTextRun, const char * aUTF8Text, + PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, + PRBool aTake8BitPath); + + + static PRBool FontCallback (const nsAString & fontName, const nsACString & genericName, void *closure); + }; #endif /* GFX_QTFONTS_H */ diff --git a/gfx/thebes/src/gfxQtFonts.cpp b/gfx/thebes/src/gfxQtFonts.cpp index c170437aacc..d22c70e6b2e 100644 --- a/gfx/thebes/src/gfxQtFonts.cpp +++ b/gfx/thebes/src/gfxQtFonts.cpp @@ -43,6 +43,7 @@ * ***** END LICENSE BLOCK ***** */ #include "gfxPlatformQt.h" +#include "gfxTypes.h" #include "gfxQtFonts.h" #include "qdebug.h" @@ -50,35 +51,217 @@ * gfxQtFontGroup */ +static int +FFRECountHyphens (const nsAString &aFFREName) +{ + int h = 0; + PRInt32 hyphen = 0; + while ((hyphen = aFFREName.FindChar('-', hyphen)) >= 0) { + ++h; + ++hyphen; + } + return h; +} + +PRBool +gfxQtFontGroup::FontCallback (const nsAString& fontName, + const nsACString& genericName, + void *closure) +{ + qDebug(">>>>>>Func:%s::%d, fontname:%s, genName:%s\n", __PRETTY_FUNCTION__, __LINE__, NS_ConvertUTF16toUTF8(fontName).get(), nsCString(genericName).get()); + nsStringArray *sa = static_cast(closure); + + // We ignore prefs that have three hypens since they are X style prefs. + if (genericName.Length() && FFRECountHyphens(fontName) >= 3) + return PR_TRUE; + + if (sa->IndexOf(fontName) < 0) { + sa->AppendString(fontName); + } + + return PR_TRUE; +} + +/** + * Look up the font in the gfxFont cache. If we don't find it, create one. + * In either case, add a ref, append it to the aFonts array, and return it --- + * except for OOM in which case we do nothing and return null. + */ +static already_AddRefed +GetOrMakeFont(const nsAString& aName, const gfxFontStyle *aStyle) +{ + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + nsRefPtr font = gfxFontCache::GetCache()->Lookup(aName, aStyle); + if (!font) { + font = new gfxQtFont(aName, aStyle); + if (!font) + return nsnull; + gfxFontCache::GetCache()->AddNew(font); + } + gfxFont *f = nsnull; + font.swap(f); + return static_cast(f); +} + + gfxQtFontGroup::gfxQtFontGroup (const nsAString& families, const gfxFontStyle *aStyle) : gfxFontGroup(families, aStyle) { qDebug("Func:%s::%d, fam:%s\n\tNeed to create font metrics, otherwise - CRASH\n\n", __PRETTY_FUNCTION__, __LINE__, NS_ConvertUTF16toUTF8(families).get()); + nsStringArray familyArray; + + // Leave non-existing fonts in the list so that fontconfig can get the + // best match. + ForEachFontInternal(families, aStyle->langGroup, PR_TRUE, PR_FALSE, + FontCallback, &familyArray); + + // Construct a string suitable for fontconfig + nsAutoString fcFamilies; + if (familyArray.Count()) { + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + int i = 0; + while (1) { + fcFamilies.Append(*familyArray[i]); + ++i; + if (i >= familyArray.Count()) + break; + fcFamilies.Append(NS_LITERAL_STRING(",")); + } + } + else { + // XXX If there are no fonts, we should use dummy family. + // Pango will resolve from this. + // behdad: yep, looks good. + // printf("%s(%s)\n", NS_ConvertUTF16toUTF8(families).get(), + // aStyle->langGroup.get()); + fcFamilies.Append(NS_LITERAL_STRING("sans-serif")); + } + + nsRefPtr font = GetOrMakeFont(fcFamilies, &mStyle); + if (font) { + mFonts.AppendElement(font); + } } gfxQtFontGroup::~gfxQtFontGroup() { + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); } gfxFontGroup * gfxQtFontGroup::Copy(const gfxFontStyle *aStyle) { + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); return new gfxQtFontGroup(mFamilies, aStyle); } +#if defined(ENABLE_FAST_PATH_8BIT) +PRBool +gfxQtFontGroup::CanTakeFastPath(PRUint32 aFlags) +{ + // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't. + // We need to always use Pango for RTL text, in case glyph mirroring is + // required. + PRBool speed = aFlags & gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; + PRBool isRTL = aFlags & gfxTextRunFactory::TEXT_IS_RTL; + return speed && !isRTL + //&& PANGO_IS_FC_FONT(GetFontAt(0)->GetPangoFont()) + ; +} +#endif + +void +gfxQtFontGroup::InitTextRun(gfxTextRun *aTextRun, const char *aUTF8Text, + PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, + PRBool aTake8BitPath) +{ + qDebug(">>>>>>Func:%s::%d, Text:'%s'\n", __PRETTY_FUNCTION__, __LINE__, aUTF8Text); +#if defined(ENABLE_FAST_PATH_ALWAYS) +// CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); +#else +#if defined(ENABLE_FAST_PATH_8BIT) + if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) { +// nsresult rv = CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); + if (NS_SUCCEEDED(rv)) + return; + } +#endif + +// CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength); +#endif +} + +/** + * We use this to append an LTR or RTL Override character to the start of the + * string. This forces Pango to honour our direction even if there are neutral characters + * in the string. + */ +static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString) +{ + static const PRUnichar overrides[2][2] = + { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO + AppendUTF16toUTF8(overrides[aIsRTL], aString); + return 3; // both overrides map to 3 bytes in UTF8 +} + gfxTextRun * gfxQtFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength, const Parameters *aParams, PRUint32 aFlags) { - return nsnull; + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + NS_ASSERTION(aFlags & TEXT_IS_8BIT, "8bit should have been set"); + gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags); + if (!run) + return nsnull; + + PRBool isRTL = run->IsRightToLeft(); + if ((aFlags & TEXT_IS_ASCII) && !isRTL) { + // We don't need to send an override character here, the characters must be all LTR + const char *utf8Chars = reinterpret_cast(aString); + InitTextRun(run, utf8Chars, aLength, 0, PR_TRUE); + } else { + // this is really gross... + const char *chars = reinterpret_cast(aString); + NS_ConvertASCIItoUTF16 unicodeString(chars, aLength); + nsCAutoString utf8; + PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8); + AppendUTF16toUTF8(unicodeString, utf8); + InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE); + } + run->FetchGlyphExtents(aParams->mContext); + return run; } gfxTextRun * gfxQtFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength, const Parameters *aParams, PRUint32 aFlags) { - return nsnull; + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + gfxTextRun *run = gfxTextRun::Create(aParams, aString, aLength, this, aFlags); + if (!run) + return nsnull; + + run->RecordSurrogates(aString); + + nsCAutoString utf8; + PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8); + AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8); + PRBool is8Bit = PR_FALSE; + +#if defined(ENABLE_FAST_PATH_8BIT) + if (CanTakeFastPath(aFlags)) { + PRUint32 allBits = 0; + PRUint32 i; + for (i = 0; i < aLength; ++i) { + allBits |= aString[i]; + } + is8Bit = (allBits & 0xFF00) == 0; + } +#endif + InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit); + run->FetchGlyphExtents(aParams->mContext); + return run; } /** @@ -86,19 +269,93 @@ gfxQtFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength, */ gfxQtFont::gfxQtFont(const nsAString &aName, - const gfxFontStyle *aFontStyle) - : gfxFont(aName, aFontStyle) + const gfxFontStyle *aFontStyle) + : gfxFont(aName, aFontStyle), + mQFont(nsnull), mCairoFont(nsnull), + mHasMetrics(PR_FALSE), mAdjustedSize(0) { - + qDebug(">>>>>>Func:%s::%d, name:%s\n", __PRETTY_FUNCTION__, __LINE__, NS_ConvertUTF16toUTF8(aName).get()); } gfxQtFont::~gfxQtFont() { + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); +} +const gfxFont::Metrics& +gfxQtFont::GetMetrics() +{ + if (mHasMetrics) + return mMetrics; + + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); +#if 0 + // printf("font name: %s %f %f\n", NS_ConvertUTF16toUTF8(mName).get(), GetStyle()->size, mAdjustedSize); + // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font))); + + fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(mName).get()); + fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent); + fprintf (stderr, " maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent); + fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading); + fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight); + fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f suOff: %f suSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize, mMetrics.superscriptOffset, mMetrics.subscriptOffset); +#endif + + mHasMetrics = PR_TRUE; + return mMetrics; +} + + +nsString +gfxQtFont::GetUniqueName() +{ + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + nsString result; +/* + PangoFont *font = GetPangoFont(); + PangoFontDescription *desc = pango_font_describe(font); + pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE); + char *str = pango_font_description_to_string(desc); + pango_font_description_free (desc); + + CopyUTF8toUTF16(str, result); + g_free(str);*/ + return result; } /* static */ void gfxQtFont::Shutdown() { - + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); +} + +PRBool +gfxQtFont::SetupCairoFont(gfxContext *aContext) +{ + qDebug(">>>>>>Func:%s::%d\n", __PRETTY_FUNCTION__, __LINE__); + cairo_t *cr = aContext->GetCairo(); + cairo_matrix_t currentCTM; + cairo_get_matrix(cr, ¤tCTM); + + if (mCairoFont) { + // Need to validate that its CTM is OK + cairo_matrix_t fontCTM; + cairo_scaled_font_get_ctm(mCairoFont, &fontCTM); + if (fontCTM.xx != currentCTM.xx || fontCTM.yy != currentCTM.yy || + fontCTM.xy != currentCTM.xy || fontCTM.yx != currentCTM.yx) { + // Just recreate it from scratch, simplest way + cairo_scaled_font_destroy(mCairoFont); + mCairoFont = nsnull; + } + } + if (!mCairoFont) { + //mCairoFont = CreateScaledFont(cr, ¤tCTM, GetPangoFont()); + } + if (cairo_scaled_font_status(mCairoFont) != CAIRO_STATUS_SUCCESS) { + // Don't cairo_set_scaled_font as that would propagate the error to + // the cairo_t, precluding any further drawing. + return PR_FALSE; + } + cairo_set_scaled_font(cr, mCairoFont); + return PR_TRUE; }