Bug 357637 Loading time (Tp) of pages with Chinese text is unbearable r=vlad

This commit is contained in:
masayuki%d-toybox.com 2007-05-04 10:02:54 +00:00
Родитель fb15a2472d
Коммит 55c73aa92a
2 изменённых файлов: 386 добавлений и 253 удалений

Просмотреть файл

@ -54,6 +54,7 @@
// #define ENABLE_XFT_FAST_PATH_ALWAYS // #define ENABLE_XFT_FAST_PATH_ALWAYS
#include "nsDataHashtable.h" #include "nsDataHashtable.h"
#include "nsClassHashtable.h"
class FontSelector; class FontSelector;
@ -74,11 +75,14 @@ public:
void GetMozLang(nsACString &aMozLang); void GetMozLang(nsACString &aMozLang);
void GetActualFontFamily(nsACString &aFamily); void GetActualFontFamily(nsACString &aFamily);
PangoFont *GetPangoFont();
XftFont *GetXftFont () { RealizeXftFont (); return mXftFont; } XftFont *GetXftFont () { RealizeXftFont (); return mXftFont; }
PangoFont *GetPangoFont() { RealizePangoFont(); return mPangoFont; }
gfxFloat GetAdjustedSize() { RealizeFont(); return mAdjustedSize; } gfxFloat GetAdjustedSize() { RealizeFont(); return mAdjustedSize; }
PRBool HasGlyph(const PRUint32 aChar);
PRUint32 GetGlyph(const PRUint32 aChar);
virtual gfxTextRun::Metrics Measure(gfxTextRun *aTextRun, virtual gfxTextRun::Metrics Measure(gfxTextRun *aTextRun,
PRUint32 aStart, PRUint32 aEnd, PRUint32 aStart, PRUint32 aEnd,
PRBool aTightBoundingBox, PRBool aTightBoundingBox,
@ -91,6 +95,8 @@ protected:
PangoContext *mPangoCtx; PangoContext *mPangoCtx;
XftFont *mXftFont; XftFont *mXftFont;
PangoFont *mPangoFont;
PangoFont *mGlyphTestingFont;
cairo_scaled_font_t *mCairoFont; cairo_scaled_font_t *mCairoFont;
PRBool mHasMetrics; PRBool mHasMetrics;
@ -100,6 +106,8 @@ protected:
void RealizeFont(PRBool force = PR_FALSE); void RealizeFont(PRBool force = PR_FALSE);
void RealizeXftFont(PRBool force = PR_FALSE); void RealizeXftFont(PRBool force = PR_FALSE);
void GetSize(const char *aString, PRUint32 aLength, gfxSize& inkSize, gfxSize& logSize); void GetSize(const char *aString, PRUint32 aLength, gfxSize& inkSize, gfxSize& logSize);
void RealizePangoFont(PRBool aForce = PR_FALSE);
void GetCharSize(const char aChar, gfxSize& aInkSize, gfxSize& aLogSize);
virtual void SetupCairoFont(cairo_t *aCR); virtual void SetupCairoFont(cairo_t *aCR);
}; };
@ -130,21 +138,17 @@ protected:
// ****** Textrun glyph conversion helpers ****** // ****** Textrun glyph conversion helpers ******
// If aUTF16Text is null, then the string contains no characters >= 0x100
void InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text, void InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength);
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
// Returns NS_ERROR_FAILURE if there's a missing glyph // Returns NS_ERROR_FAILURE if there's a missing glyph
nsresult SetGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8, nsresult SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont,
PRUint32 aUTF8Length, const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs, PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
PangoGlyphUnit aOverrideSpaceWidth, PangoGlyphUnit aOverrideSpaceWidth,
PRBool aAbortOnMissingGlyph); PRBool aAbortOnMissingGlyph);
// If aUTF16Text is null, then the string contains no characters >= 0x100. nsresult SetMissingGlyphs(gfxTextRun *aTextRun,
// Returns NS_ERROR_FAILURE if we require the itemizing path
nsresult CreateGlyphRunsFast(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length, const gchar *aUTF8, PRUint32 aUTF8Length,
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length); PRUint32 *aUTF16Offset);
void CreateGlyphRunsItemizing(gfxTextRun *aTextRun, void CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length, const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 aUTF8HeaderLength); PRUint32 aUTF8HeaderLength);
@ -161,4 +165,69 @@ private:
nsTArray<gfxFontStyle> mAdditionalStyles; nsTArray<gfxFontStyle> mAdditionalStyles;
}; };
class gfxPangoFontWrapper {
public:
gfxPangoFontWrapper(PangoFont *aFont) {
mFont = aFont;
g_object_ref(mFont);
}
~gfxPangoFontWrapper() {
if (mFont)
g_object_unref(mFont);
}
PangoFont* Get() { return mFont; }
private:
PangoFont *mFont;
};
class gfxPangoFontCache
{
public:
gfxPangoFontCache();
~gfxPangoFontCache();
static gfxPangoFontCache* GetPangoFontCache() {
if (!sPangoFontCache)
sPangoFontCache = new gfxPangoFontCache();
return sPangoFontCache;
}
static void Shutdown() {
if (sPangoFontCache)
delete sPangoFontCache;
sPangoFontCache = nsnull;
}
void Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont);
PangoFont* Get(const PangoFontDescription *aFontDesc);
private:
static gfxPangoFontCache *sPangoFontCache;
nsClassHashtable<nsUint32HashKey, gfxPangoFontWrapper> mPangoFonts;
};
// XXX we should remove this class, because this class is used only in |HasGlyph| of gfxPangoFont.
// But it can use fontconfig directly after bug 366664.
class gfxPangoFontNameMap
{
public:
gfxPangoFontNameMap();
~gfxPangoFontNameMap();
static gfxPangoFontNameMap* GetPangoFontNameMap() {
if (!sPangoFontNameMap)
sPangoFontNameMap = new gfxPangoFontNameMap();
return sPangoFontNameMap;
}
static void Shutdown() {
if (sPangoFontNameMap)
delete sPangoFontNameMap;
sPangoFontNameMap = nsnull;
}
void Put(const nsACString &aName, PangoFont *aPangoFont);
PangoFont* Get(const nsACString &aName);
private:
static gfxPangoFontNameMap *sPangoFontNameMap;
nsClassHashtable<nsCStringHashKey, gfxPangoFontWrapper> mPangoFonts;
};
#endif /* GFX_PANGOFONTS_H */ #endif /* GFX_PANGOFONTS_H */

Просмотреть файл

@ -97,6 +97,9 @@
static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup); static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup);
static void GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang); static void GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang);
/* static */ gfxPangoFontCache* gfxPangoFontCache::sPangoFontCache = nsnull;
/* static */ gfxPangoFontNameMap* gfxPangoFontNameMap::sPangoFontNameMap = nsnull;
/** /**
** gfxPangoFontGroup ** gfxPangoFontGroup
**/ **/
@ -266,7 +269,8 @@ gfxPangoFont::gfxPangoFont(const nsAString &aName,
const gfxFontStyle *aFontStyle) const gfxFontStyle *aFontStyle)
: gfxFont(aName, aFontStyle), : gfxFont(aName, aFontStyle),
mPangoFontDesc(nsnull), mPangoCtx(nsnull), mPangoFontDesc(nsnull), mPangoCtx(nsnull),
mXftFont(nsnull), mCairoFont(nsnull), mHasMetrics(PR_FALSE), mXftFont(nsnull), mPangoFont(nsnull), mGlyphTestingFont(nsnull),
mCairoFont(nsnull), mHasMetrics(PR_FALSE),
mAdjustedSize(0) mAdjustedSize(0)
{ {
InitPangoLib(); InitPangoLib();
@ -277,6 +281,12 @@ gfxPangoFont::~gfxPangoFont()
if (mPangoCtx) if (mPangoCtx)
g_object_unref(mPangoCtx); g_object_unref(mPangoCtx);
if (mPangoFont)
g_object_unref(mPangoFont);
if (mGlyphTestingFont)
g_object_unref(mGlyphTestingFont);
if (mPangoFontDesc) if (mPangoFontDesc)
pango_font_description_free(mPangoFontDesc); pango_font_description_free(mPangoFontDesc);
@ -288,6 +298,8 @@ gfxPangoFont::~gfxPangoFont()
gfxPangoFont::Shutdown() gfxPangoFont::Shutdown()
{ {
ShutdownPangoLib(); ShutdownPangoLib();
gfxPangoFontCache::Shutdown();
gfxPangoFontNameMap::Shutdown();
} }
static PangoStyle static PangoStyle
@ -364,6 +376,12 @@ gfxPangoFont::RealizeFont(PRBool force)
g_object_unref(mPangoCtx); g_object_unref(mPangoCtx);
if (mPangoFontDesc) if (mPangoFontDesc)
pango_font_description_free(mPangoFontDesc); pango_font_description_free(mPangoFontDesc);
if (mPangoFont) {
g_object_unref(mPangoFont);
mPangoFont = nsnull;
mXftFont = nsnull;
// XXX we don't need to reset mGlyphTestingFont
}
mPangoFontDesc = pango_font_description_new(); mPangoFontDesc = pango_font_description_new();
@ -396,7 +414,7 @@ gfxPangoFont::RealizeFont(PRBool force)
return; return;
gfxSize isz, lsz; gfxSize isz, lsz;
GetSize("x", 1, isz, lsz); GetCharSize('x', isz, lsz);
gfxFloat aspect = isz.height / GetStyle()->size; gfxFloat aspect = isz.height / GetStyle()->size;
mAdjustedSize = mAdjustedSize =
PR_MAX(NS_round(GetStyle()->size*(GetStyle()->sizeAdjust/aspect)), 1.0); PR_MAX(NS_round(GetStyle()->size*(GetStyle()->sizeAdjust/aspect)), 1.0);
@ -414,39 +432,69 @@ gfxPangoFont::RealizeXftFont(PRBool force)
return; return;
} }
PangoFcFont *fcfont = PANGO_FC_FONT(GetPangoFont()); mXftFont = pango_xft_font_get_font(GetPangoFont());
mXftFont = pango_xft_font_get_font(PANGO_FONT(fcfont));
} }
void void
gfxPangoFont::GetSize(const char *aCharString, PRUint32 aLength, gfxSize& inkSize, gfxSize& logSize) gfxPangoFont::RealizePangoFont(PRBool aForce)
{ {
if (!aForce && mPangoFont)
return;
if (mPangoFont) {
g_object_unref(mPangoFont);
mPangoFont = nsnull;
mXftFont = nsnull;
}
RealizeFont(); RealizeFont();
gfxPangoFontCache *cache = gfxPangoFontCache::GetPangoFontCache();
if (!cache)
return; // Error
mPangoFont = cache->Get(mPangoFontDesc);
if (mPangoFont)
return;
mPangoFont = pango_context_load_font(mPangoCtx, mPangoFontDesc);
if (!mPangoFont)
return; // Error
cache->Put(mPangoFontDesc, mPangoFont);
PangoAttrList *al = pango_attr_list_new(); if (mGlyphTestingFont)
GList *items = pango_itemize(mPangoCtx, aCharString, 0, aLength, al, NULL);
pango_attr_list_unref(al);
if (!items || g_list_length(items) != 1)
return; return;
PangoItem *item = (PangoItem*) items->data; // Append this to font name map
gfxPangoFontNameMap *fontNameMap = gfxPangoFontNameMap::GetPangoFontNameMap();
if (!fontNameMap)
return; // Error
NS_ConvertUTF16toUTF8 name(mName);
mGlyphTestingFont = fontNameMap->Get(name);
if (mGlyphTestingFont)
return;
fontNameMap->Put(name, mPangoFont);
}
void
gfxPangoFont::GetCharSize(const char aChar, gfxSize& aInkSize, gfxSize& aLogSize)
{
PangoAnalysis analysis;
analysis.font = GetPangoFont();
analysis.level = 0;
analysis.lang_engine = nsnull;
analysis.extra_attrs = nsnull;
analysis.language = pango_language_from_string("en");
analysis.shape_engine = pango_font_find_shaper(analysis.font, analysis.language, aChar);
PangoGlyphString *glstr = pango_glyph_string_new(); PangoGlyphString *glstr = pango_glyph_string_new();
pango_shape (aCharString, aLength, &(item->analysis), glstr); pango_shape(&aChar, 1, &analysis, glstr);
PangoRectangle ink_rect, log_rect; PangoRectangle ink_rect, log_rect;
pango_glyph_string_extents (glstr, item->analysis.font, &ink_rect, &log_rect); pango_glyph_string_extents(glstr, analysis.font, &ink_rect, &log_rect);
inkSize.width = ink_rect.width / FLOAT_PANGO_SCALE; aInkSize.width = ink_rect.width / FLOAT_PANGO_SCALE;
inkSize.height = ink_rect.height / FLOAT_PANGO_SCALE; aInkSize.height = ink_rect.height / FLOAT_PANGO_SCALE;
logSize.width = log_rect.width / FLOAT_PANGO_SCALE; aLogSize.width = log_rect.width / FLOAT_PANGO_SCALE;
logSize.height = log_rect.height / FLOAT_PANGO_SCALE; aLogSize.height = log_rect.height / FLOAT_PANGO_SCALE;
pango_glyph_string_free(glstr); pango_glyph_string_free(glstr);
pango_item_free(item);
g_list_free(items);
} }
// rounding and truncation functions for a Freetype floating point number // rounding and truncation functions for a Freetype floating point number
@ -463,22 +511,10 @@ gfxPangoFont::GetMetrics()
if (mHasMetrics) if (mHasMetrics)
return mMetrics; return mMetrics;
RealizeFont();
PangoAttrList *al = pango_attr_list_new();
GList *items = pango_itemize(mPangoCtx, "a", 0, 1, al, NULL);
pango_attr_list_unref(al);
if (!items || g_list_length(items) != 1)
return mMetrics; // XXX error
#ifndef THEBES_USE_PANGO_CAIRO #ifndef THEBES_USE_PANGO_CAIRO
float val; float val;
PangoItem *item = (PangoItem*)items->data; XftFont *xftFont = GetXftFont(); // RealizeFont is called here.
PangoFcFont *fcfont = PANGO_FC_FONT(item->analysis.font);
XftFont *xftFont = pango_xft_font_get_font(PANGO_FONT(fcfont));
if (!xftFont) if (!xftFont)
return mMetrics; // XXX error return mMetrics; // XXX error
@ -487,6 +523,7 @@ gfxPangoFont::GetMetrics()
return mMetrics; // XXX error return mMetrics; // XXX error
int size; int size;
PangoFcFont *fcfont = PANGO_FC_FONT(mPangoFont);
if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch) if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
size = 12; size = 12;
mMetrics.emHeight = PR_MAX(1, size); mMetrics.emHeight = PR_MAX(1, size);
@ -508,12 +545,12 @@ gfxPangoFont::GetMetrics()
mMetrics.maxAdvance = xftFont->max_advance_width; mMetrics.maxAdvance = xftFont->max_advance_width;
gfxSize isz, lsz; gfxSize isz, lsz;
GetSize(" ", 1, isz, lsz); GetCharSize(' ', isz, lsz);
mMetrics.spaceWidth = lsz.width; mMetrics.spaceWidth = lsz.width;
// XXX do some FcCharSetHasChar work here to make sure // XXX do some FcCharSetHasChar work here to make sure
// we have an "x" // we have an "x"
GetSize("x", 1, isz, lsz); GetCharSize('x', isz, lsz);
mMetrics.xHeight = isz.height; mMetrics.xHeight = isz.height;
mMetrics.aveCharWidth = isz.width; mMetrics.aveCharWidth = isz.width;
@ -558,8 +595,7 @@ gfxPangoFont::GetMetrics()
XftUnlockFace(xftFont); XftUnlockFace(xftFont);
#else #else
/* pango_cairo case; try to get all the metrics from pango itself */ /* pango_cairo case; try to get all the metrics from pango itself */
PangoItem *item = (PangoItem*)items->data; PangoFont *font = GetPangoFont(); // RealizeFont is called here.
PangoFont *font = item->analysis.font;
PangoFontMetrics *pfm = pango_font_get_metrics (font, NULL); PangoFontMetrics *pfm = pango_font_get_metrics (font, NULL);
@ -585,9 +621,9 @@ gfxPangoFont::GetMetrics()
mMetrics.maxAdvance = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE; // XXX mMetrics.maxAdvance = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE; // XXX
gfxSize isz, lsz; gfxSize isz, lsz;
GetSize(" ", 1, isz, lsz); GetCharSize(' ', isz, lsz);
mMetrics.spaceWidth = lsz.width; mMetrics.spaceWidth = lsz.width;
GetSize("x", 1, isz, lsz); GetCharSize('x', isz, lsz);
mMetrics.xHeight = isz.height; mMetrics.xHeight = isz.height;
mMetrics.aveCharWidth = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE; mMetrics.aveCharWidth = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE;
@ -620,17 +656,44 @@ gfxPangoFont::GetMetrics()
return mMetrics; return mMetrics;
} }
void PRBool
gfxPangoFont::GetMozLang(nsACString &aMozLang) gfxPangoFont::HasGlyph(PRUint32 aChar)
{ {
aMozLang.Assign(GetStyle()->langGroup); // Ensure that null character should be missing.
if (aChar == 0)
return PR_FALSE;
PangoFont *font = nsnull;
if (mPangoFont)
font = mPangoFont;
else if (mGlyphTestingFont)
font = mGlyphTestingFont;
else {
gfxPangoFontNameMap *fontNameMap = gfxPangoFontNameMap::GetPangoFontNameMap();
NS_ENSURE_TRUE(fontNameMap, PR_FALSE);
// XXX in a prinsiple, we need to add weight and style for the key.
// But this method should be independent from pango for the performance.
// For the temporary, the name is enough for the key. The members of
// a font-family should have same glyphs.
NS_ConvertUTF16toUTF8 name(mName);
mGlyphTestingFont = fontNameMap->Get(name);
if (!mGlyphTestingFont) {
font = GetPangoFont();
NS_ENSURE_TRUE(font, PR_FALSE);
} else
font = mGlyphTestingFont;
}
return pango_fc_font_has_char(PANGO_FC_FONT(font), aChar) ? PR_TRUE : PR_FALSE;
} }
PangoFont * PRUint32
gfxPangoFont::GetPangoFont() gfxPangoFont::GetGlyph(const PRUint32 aChar)
{ {
RealizeFont(); // Ensure that null character should be missing.
return pango_context_load_font(mPangoCtx, mPangoFontDesc); if (aChar == 0)
return 0;
RealizePangoFont();
return pango_fc_font_get_glyph(PANGO_FC_FONT(mPangoFont), aChar);
} }
nsString nsString
@ -721,7 +784,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
// We don't need to send an override character here, the characters must be all // We don't need to send an override character here, the characters must be all
// LTR // LTR
const gchar *utf8Chars = NS_REINTERPRET_CAST(const gchar*, aString); const gchar *utf8Chars = NS_REINTERPRET_CAST(const gchar*, aString);
InitTextRun(run, utf8Chars, aLength, 0, nsnull, 0); InitTextRun(run, utf8Chars, aLength, 0);
} else { } else {
const char *chars = NS_REINTERPRET_CAST(const char*, aString); const char *chars = NS_REINTERPRET_CAST(const char*, aString);
// XXX this could be more efficient. // XXX this could be more efficient.
@ -732,7 +795,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
nsCAutoString utf8; nsCAutoString utf8;
PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8); PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
AppendUTF16toUTF8(unicodeString, utf8); AppendUTF16toUTF8(unicodeString, utf8);
InitTextRun(run, utf8.get(), utf8.Length(), headerLen, nsnull, 0); InitTextRun(run, utf8.get(), utf8.Length(), headerLen);
} }
return run; return run;
} }
@ -750,14 +813,13 @@ gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
nsCAutoString utf8; nsCAutoString utf8;
PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8); PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8);
AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8); AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8);
InitTextRun(run, utf8.get(), utf8.Length(), headerLen, aString, aLength); InitTextRun(run, utf8.get(), utf8.Length(), headerLen);
return run; return run;
} }
void void
gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text, gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength)
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length)
{ {
#if defined(ENABLE_XFT_FAST_PATH_ALWAYS) #if defined(ENABLE_XFT_FAST_PATH_ALWAYS)
CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
@ -773,11 +835,7 @@ gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
(aTextRun->IsRightToLeft() (aTextRun->IsRightToLeft()
? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR)); ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR));
nsresult rv = CreateGlyphRunsFast(aTextRun, aUTF8Text + aUTF8HeaderLength,
aUTF8Length - aUTF8HeaderLength, aUTF16Text, aUTF16Length);
if (rv == NS_ERROR_FAILURE) {
CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength); CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength);
}
#endif #endif
} }
@ -891,7 +949,8 @@ gfxPangoFont::Measure(gfxTextRun *aTextRun,
return GetPangoMetrics(&glyphs, GetPangoFont(), aTextRun->GetAppUnitsPerDevUnit(), clusterCount); return GetPangoMetrics(&glyphs, GetPangoFont(), aTextRun->GetAppUnitsPerDevUnit(), clusterCount);
} }
#define IS_MISSING_GLYPH(g) (((g) & 0x10000000) || (g) == 0x0FFFFFFF) #define EMPTY_GLYPH 0x0FFFFFFF
#define IS_MISSING_GLYPH(g) (((g) & 0x10000000) || (g) == EMPTY_GLYPH)
static cairo_scaled_font_t* static cairo_scaled_font_t*
CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont) CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont)
@ -1005,7 +1064,7 @@ SetMissingGlyphForUCS4(gfxTextRun *aTextRun, PRUint32 aIndex, gunichar aCh)
} }
nsresult nsresult
gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun, gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont,
const gchar *aUTF8, PRUint32 aUTF8Length, const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs, PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
PangoGlyphUnit aOverrideSpaceWidth, PangoGlyphUnit aOverrideSpaceWidth,
@ -1053,6 +1112,12 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
for (;;) { for (;;) {
++glyphCount; ++glyphCount;
if (IS_MISSING_GLYPH(aGlyphs->glyphs[glyphIndex].glyph)) { if (IS_MISSING_GLYPH(aGlyphs->glyphs[glyphIndex].glyph)) {
if (aGlyphs->glyphs[glyphIndex].glyph == EMPTY_GLYPH &&
aGlyphs->glyphs[glyphIndex].geometry.width == 0) {
// the zero width characters returns empty glyph ID at shaping,
// we should override it if the font has the character.
aGlyphs->glyphs[glyphIndex].glyph = aFont->GetGlyph(ch);
} else
haveMissingGlyph = PR_TRUE; haveMissingGlyph = PR_TRUE;
} }
glyphIndex += direction; glyphIndex += direction;
@ -1123,6 +1188,33 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
return NS_OK; return NS_OK;
} }
nsresult
gfxPangoFontGroup::SetMissingGlyphs(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 *aUTF16Offset)
{
PRUint32 utf16Offset = *aUTF16Offset;
PRUint32 textRunLength = aTextRun->GetLength();
for (PRUint32 index = 0; index < aUTF8Length;) {
if (utf16Offset >= textRunLength) {
NS_ERROR("Someone has added too many glyphs!");
break;
}
gunichar ch = g_utf8_get_char(aUTF8 + index);
SetMissingGlyphForUCS4(aTextRun, utf16Offset, ch);
++utf16Offset;
NS_ASSERTION(!IS_SURROGATE(ch), "surrogates should not appear in UTF8");
if (ch >= 0x10000)
++utf16Offset;
// We produced this UTF8 so we don't need to worry about malformed stuff
index = g_utf8_next_char(aUTF8 + index) - aUTF8;
}
*aUTF16Offset = utf16Offset;
return NS_OK;
}
#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS) #if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS)
void void
gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun, gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun,
@ -1192,72 +1284,6 @@ gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun,
} }
#endif #endif
nsresult
gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length,
const PRUnichar *aUTF16, PRUint32 aUTF16Length)
{
gfxPangoFont *font = GetFontAt(0);
PangoAnalysis analysis;
analysis.font = font->GetPangoFont();
analysis.level = aTextRun->IsRightToLeft() ? 1 : 0;
analysis.lang_engine = nsnull;
analysis.extra_attrs = nsnull;
// Find non-ASCII character for finding the language of the script.
guint32 ch = 'a';
PRUint8 unicharRange = kRangeSetLatin;
if (aUTF16) {
PRUint32 i;
for (i = 0; i < aUTF16Length; ++i) {
PRUnichar utf16Char = aUTF16[i];
if (utf16Char > 0x100) {
ch = utf16Char;
unicharRange = FindCharUnicodeRange(utf16Char);
break;
}
}
}
// Determin the language for finding the shaper.
nsCAutoString lang;
font->GetMozLang(lang);
switch (unicharRange) {
case kRangeSetLatin:
lang.Assign("x-western");
break;
case kRangeSetCJK:
if (GetCJKLangGroupIndex(lang.get()) < 0)
return NS_ERROR_FAILURE; // try with itemizing
break;
default:
lang.Assign(LangGroupFromUnicodeRange(unicharRange));
break;
}
if (lang.IsEmpty() || lang.Equals("x-unicode") || lang.Equals("x-user-def"))
return NS_ERROR_FAILURE; // try with itemizing
analysis.language = GetPangoLanguage(lang);
analysis.shape_engine = pango_font_find_shaper(analysis.font,
analysis.language,
ch);
SetupClusterBoundaries(aTextRun, aUTF8, aUTF8Length, 0, &analysis);
PangoGlyphString *glyphString = pango_glyph_string_new();
pango_shape(aUTF8, aUTF8Length, &analysis, glyphString);
PRUint32 utf16Offset = 0;
nsresult rv = aTextRun->AddGlyphRun(font, 0);
if (NS_FAILED(rv))
return rv;
rv = SetGlyphs(aTextRun, aUTF8, aUTF8Length, &utf16Offset, glyphString, 0, PR_TRUE);
pango_glyph_string_free(glyphString);
return rv;
}
class FontSelector class FontSelector
{ {
public: public:
@ -1266,7 +1292,7 @@ public:
PangoItem *aItem, PRUint32 aUTF16Offset, PRPackedBool aIsRTL) : PangoItem *aItem, PRUint32 aUTF16Offset, PRPackedBool aIsRTL) :
mItem(aItem), mItem(aItem),
mGroup(aGroup), mTextRun(aTextRun), mString(aString), mGroup(aGroup), mTextRun(aTextRun), mString(aString),
mFontIndex(0), mLength(aLength), mSegmentOffset(0), mUTF16Offset(aUTF16Offset), mFontIndex(0), mLength(aLength), mUTF16Offset(aUTF16Offset),
mTriedPrefFonts(0), mTriedOtherFonts(0), mIsRTL(aIsRTL) mTriedPrefFonts(0), mTriedOtherFonts(0), mIsRTL(aIsRTL)
{ {
for (PRUint32 i = 0; i < mGroup->FontListLength(); ++i) for (PRUint32 i = 0; i < mGroup->FontListLength(); ++i)
@ -1274,9 +1300,9 @@ public:
mSpaceWidth = NS_lround(mGroup->GetFontAt(0)->GetMetrics().spaceWidth * FLOAT_PANGO_SCALE); mSpaceWidth = NS_lround(mGroup->GetFontAt(0)->GetMetrics().spaceWidth * FLOAT_PANGO_SCALE);
} }
void Run() nsresult Run()
{ {
InitSegments(0, mLength, mFontIndex); return InitSegments(mString, mLength);
} }
PRUint32 GetUTF16Offset() { return mUTF16Offset; } PRUint32 GetUTF16Offset() { return mUTF16Offset; }
@ -1311,30 +1337,6 @@ public:
return PR_TRUE; return PR_TRUE;
} }
PRBool AppendNextSegment(gfxPangoFont *aFont, PRUint32 aUTF8Length,
PangoGlyphString *aGlyphs, PRBool aGotGlyphs)
{
PangoFont *pf = aFont->GetPangoFont();
PRUint32 incomingUTF16Offset = mUTF16Offset;
if (!aGotGlyphs) {
// we can't use the existing glyphstring.
PangoFont *tmpFont = mItem->analysis.font;
mItem->analysis.font = pf;
pango_shape(mString + mSegmentOffset, aUTF8Length, &mItem->analysis, aGlyphs);
mItem->analysis.font = tmpFont;
}
nsresult rv = mTextRun->AddGlyphRun(aFont, incomingUTF16Offset);
if (NS_FAILED(rv))
return rv;
mGroup->SetGlyphs(mTextRun, mString + mSegmentOffset, aUTF8Length, &mUTF16Offset,
aGlyphs, mSpaceWidth, PR_FALSE);
mSegmentOffset += aUTF8Length;
return NS_OK;
}
private: private:
PangoItem *mItem; PangoItem *mItem;
@ -1345,7 +1347,6 @@ private:
const char *mString; // UTF-8 const char *mString; // UTF-8
PRUint32 mFontIndex; PRUint32 mFontIndex;
PRInt32 mLength; PRInt32 mLength;
PRUint32 mSegmentOffset;
PRUint32 mUTF16Offset; PRUint32 mUTF16Offset;
PRUint32 mSpaceWidth; PRUint32 mSpaceWidth;
@ -1353,108 +1354,102 @@ private:
PRPackedBool mTriedOtherFonts; PRPackedBool mTriedOtherFonts;
PRPackedBool mIsRTL; PRPackedBool mIsRTL;
void InitSegments(const PRUint32 aOffset, nsresult InitSegments(const gchar *aUTF8, PRUint32 aLength) {
const PRUint32 aLength, if (aLength == 0)
const PRUint32 aFontIndex) { return NS_OK;
mFontIndex = aFontIndex; const gchar *start = aUTF8;
const gchar *last = start + aLength;
const char *current = mString + aOffset;
PRBool checkMissingGlyph = PR_TRUE;
// for RTL, if we cannot find the font that has all glyphs,
// we should use better font.
PRUint32 betterFontIndex = 0;
PRUint32 foundGlyphs = 0;
PangoGlyphString *glyphString = pango_glyph_string_new();
RetryNextFont: RetryNextFont:
nsRefPtr<gfxPangoFont> font = GetNextFont(); nsRefPtr<gfxPangoFont> font = GetNextFont();
// If we cannot found the font that has the current character glyph, // If we cannot found the font that has the current character glyph,
// we should return default font's missing data. // we should return default font's missing data.
if (!font) { if (!font)
font = mFonts[betterFontIndex]; return AppendMissingSegment(start, last - start);
checkMissingGlyph = PR_FALSE;
nsresult rv;
for (const gchar *c = start; c < last;) {
// find the first missing glyph
gunichar u = g_utf8_get_char(c);
if (font->HasGlyph(PRUint32(u))) {
c = g_utf8_next_char(c);
continue;
} }
// Shaping // find the next point that can be renderd with current font
PangoFont *tmpFont = mItem->analysis.font; const gchar *missingStart = c;
mItem->analysis.font = font->GetPangoFont(); const gchar *next;
pango_shape(current, aLength, &mItem->analysis, glyphString); for (next = g_utf8_next_char(missingStart); next < last; next = g_utf8_next_char(next)) {
mItem->analysis.font = tmpFont; u = g_utf8_get_char(next);
if (font->HasGlyph(PRUint32(u)))
gint num_glyphs = glyphString->num_glyphs;
gint *clusters = glyphString->log_clusters;
PRUint32 offset = aOffset;
PRUint32 skipLength = 0;
if (checkMissingGlyph) {
for (PRInt32 i = 0; i < num_glyphs; ++i) {
PangoGlyphInfo *info = &glyphString->glyphs[i];
if (IS_MISSING_GLYPH(info->glyph)) {
// XXX Note that we don't support the segment separation
// in RTL text. Because the Arabic characters changes the
// glyphs by the position of the context. I think that the
// languages of RTL doesn't have *very *many characters, so,
// the Arabic/Hebrew font may have all glyphs in a font.
if (mIsRTL) {
PRUint32 found = i;
for (PRInt32 j = i; j < num_glyphs; ++j) {
info = &glyphString->glyphs[j];
if (!IS_MISSING_GLYPH(info->glyph))
found++;
}
if (found > foundGlyphs) {
// we find better font!
foundGlyphs = found;
betterFontIndex = mFontIndex - 1;
}
goto RetryNextFont;
}
// The glyph is missing, separate segment here.
PRUint32 missingLength = aLength - clusters[i];
PRInt32 j;
for (j = i + 1; j < num_glyphs; ++j) {
info = &glyphString->glyphs[j];
if (!IS_MISSING_GLYPH(info->glyph)) {
missingLength = aOffset + clusters[j] - offset;
break; break;
} }
}
if (i != 0) { // current font has 0 glyphs for current segment, try with next font
// found glyphs if (missingStart == start && next == last)
AppendNextSegment(font, offset - (aOffset + skipLength), goto RetryNextFont;
glyphString, PR_FALSE);
}
// missing glyphs // create the segment for found glyphs
rv = AppendSegment(font, start, missingStart - start);
NS_ENSURE_SUCCESS(rv, rv);
// init the missing glyphs with remains fonts.
PRUint32 fontIndex = mFontIndex; PRUint32 fontIndex = mFontIndex;
InitSegments(offset, missingLength, mFontIndex); rv = InitSegments(missingStart, next - missingStart);
mFontIndex = fontIndex; mFontIndex = fontIndex;
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 next = offset + missingLength; start = c = next;
if (next >= aLength) { }
rv = AppendSegment(font, start, last - start);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult AppendSegment(gfxPangoFont* aFont, const gchar *aUTF8, PRUint32 aLength) {
if (aLength == 0)
return NS_OK;
PangoFont* pf = aFont->GetPangoFont();
PangoGlyphString *glyphString = pango_glyph_string_new();
if (!glyphString)
return NS_ERROR_OUT_OF_MEMORY;
PangoFont *tmpFont = mItem->analysis.font;
mItem->analysis.font = pf;
pango_shape(aUTF8, aLength, &mItem->analysis, glyphString);
mItem->analysis.font = tmpFont;
nsresult rv = mTextRun->AddGlyphRun(aFont, mUTF16Offset);
if (NS_FAILED(rv)) {
NS_ERROR("AddGlyphRun Failed");
pango_glyph_string_free(glyphString); pango_glyph_string_free(glyphString);
return; return rv;
} }
PRUint32 utf16Offset = mUTF16Offset;
// remains, continue this loop rv = mGroup->SetGlyphs(mTextRun, aFont, aUTF8, aLength,
i = j; &utf16Offset, glyphString, mSpaceWidth, PR_FALSE);
skipLength = next - aOffset;
}
if (i + 1 < num_glyphs)
offset = aOffset + clusters[i + 1];
else
offset = aOffset + aLength;
}
} else {
offset = aOffset + aLength;
}
AppendNextSegment(font, aLength - skipLength, glyphString, skipLength == 0);
pango_glyph_string_free(glyphString); pango_glyph_string_free(glyphString);
NS_ENSURE_SUCCESS(rv, rv);
mUTF16Offset = utf16Offset;
return NS_OK;
}
nsresult AppendMissingSegment(const gchar *aUTF8, PRUint32 aLength) {
if (aLength == 0)
return NS_OK;
nsresult rv = mTextRun->AddGlyphRun(mFonts[0], mUTF16Offset);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 utf16Offset = mUTF16Offset;
rv = mGroup->SetMissingGlyphs(mTextRun, aUTF8, aLength, &utf16Offset);
NS_ENSURE_SUCCESS(rv, rv);
mUTF16Offset = utf16Offset;
return NS_OK;
} }
gfxPangoFont *GetNextFont() { gfxPangoFont *GetNextFont() {
@ -1524,7 +1519,7 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
if (!prefBranch) if (!prefBranch)
return; return;
// Add by the order of accept languages. // Add the accept languages.
nsXPIDLCString list; nsXPIDLCString list;
nsresult rv = prefBranch->GetCharPref("intl.accept_languages", nsresult rv = prefBranch->GetCharPref("intl.accept_languages",
getter_Copies(list)); getter_Copies(list));
@ -1887,3 +1882,72 @@ GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang)
break; break;
} }
} }
gfxPangoFontCache::gfxPangoFontCache()
{
mPangoFonts.Init(500);
}
gfxPangoFontCache::~gfxPangoFontCache()
{
}
void
gfxPangoFontCache::Put(const PangoFontDescription *aFontDesc, PangoFont *aPangoFont)
{
if (mPangoFonts.Count() > 5000)
mPangoFonts.Clear();
PRUint32 key = pango_font_description_hash(aFontDesc);
gfxPangoFontWrapper *value = new gfxPangoFontWrapper(aPangoFont);
if (!value)
return;
mPangoFonts.Put(key, value);
}
PangoFont*
gfxPangoFontCache::Get(const PangoFontDescription *aFontDesc)
{
PRUint32 key = pango_font_description_hash(aFontDesc);
gfxPangoFontWrapper *value;
if (!mPangoFonts.Get(key, &value))
return nsnull;
PangoFont *font = value->Get();
g_object_ref(font);
return font;
}
gfxPangoFontNameMap::gfxPangoFontNameMap()
{
mPangoFonts.Init(100);
}
gfxPangoFontNameMap::~gfxPangoFontNameMap()
{
}
void
gfxPangoFontNameMap::Put(const nsACString &aName, PangoFont *aPangoFont)
{
nsCAutoString key(aName);
ToLowerCase(key);
gfxPangoFontWrapper *value;
if (!mPangoFonts.Get(key, &value)) {
value = new gfxPangoFontWrapper(aPangoFont);
if (!value)
return;
mPangoFonts.Put(key, value);
}
}
PangoFont*
gfxPangoFontNameMap::Get(const nsACString &aName)
{
nsCAutoString key(aName);
ToLowerCase(key);
gfxPangoFontWrapper *value;
if (!mPangoFonts.Get(key, &value))
return nsnull;
PangoFont *font = value->Get();
g_object_ref(font);
return font;
}