зеркало из https://github.com/mozilla/pjs.git
Bug 357637 Loading time (Tp) of pages with Chinese text is unbearable (caching the pango fonts) r=vlad
This commit is contained in:
Родитель
28966b3995
Коммит
449f849369
|
@ -54,6 +54,7 @@
|
|||
// #define ENABLE_XFT_FAST_PATH_ALWAYS
|
||||
|
||||
#include "nsDataHashtable.h"
|
||||
#include "nsClassHashtable.h"
|
||||
|
||||
class FontSelector;
|
||||
|
||||
|
@ -74,11 +75,13 @@ public:
|
|||
|
||||
void GetMozLang(nsACString &aMozLang);
|
||||
void GetActualFontFamily(nsACString &aFamily);
|
||||
PangoFont *GetPangoFont();
|
||||
|
||||
XftFont *GetXftFont () { RealizeXftFont (); return mXftFont; }
|
||||
PangoFont *GetPangoFont() { RealizePangoFont(); return mPangoFont; }
|
||||
gfxFloat GetAdjustedSize() { RealizeFont(); return mAdjustedSize; }
|
||||
|
||||
PRBool HasGlyph(const PRUint32 aChar);
|
||||
|
||||
virtual gfxTextRun::Metrics Measure(gfxTextRun *aTextRun,
|
||||
PRUint32 aStart, PRUint32 aEnd,
|
||||
PRBool aTightBoundingBox,
|
||||
|
@ -91,6 +94,8 @@ protected:
|
|||
PangoContext *mPangoCtx;
|
||||
|
||||
XftFont *mXftFont;
|
||||
PangoFont *mPangoFont;
|
||||
PangoFont *mGlyphTestingFont;
|
||||
cairo_scaled_font_t *mCairoFont;
|
||||
|
||||
PRBool mHasMetrics;
|
||||
|
@ -100,6 +105,8 @@ protected:
|
|||
void RealizeFont(PRBool force = PR_FALSE);
|
||||
void RealizeXftFont(PRBool force = PR_FALSE);
|
||||
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);
|
||||
};
|
||||
|
@ -130,21 +137,17 @@ protected:
|
|||
|
||||
// ****** Textrun glyph conversion helpers ******
|
||||
|
||||
// If aUTF16Text is null, then the string contains no characters >= 0x100
|
||||
void InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
|
||||
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
|
||||
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
|
||||
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength);
|
||||
// Returns NS_ERROR_FAILURE if there's a missing glyph
|
||||
nsresult SetGlyphs(gfxTextRun *aTextRun, const gchar *aUTF8,
|
||||
PRUint32 aUTF8Length,
|
||||
PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
|
||||
PangoGlyphUnit aOverrideSpaceWidth,
|
||||
PRBool aAbortOnMissingGlyph);
|
||||
// If aUTF16Text is null, then the string contains no characters >= 0x100.
|
||||
// Returns NS_ERROR_FAILURE if we require the itemizing path
|
||||
nsresult CreateGlyphRunsFast(gfxTextRun *aTextRun,
|
||||
nsresult SetMissingGlyphs(gfxTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
|
||||
PRUint32 *aUTF16Offset);
|
||||
void CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
|
||||
const gchar *aUTF8, PRUint32 aUTF8Length,
|
||||
PRUint32 aUTF8HeaderLength);
|
||||
|
@ -161,4 +164,69 @@ private:
|
|||
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 */
|
||||
|
|
|
@ -97,6 +97,9 @@
|
|||
static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup);
|
||||
static void GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang);
|
||||
|
||||
/* static */ gfxPangoFontCache* gfxPangoFontCache::sPangoFontCache = nsnull;
|
||||
/* static */ gfxPangoFontNameMap* gfxPangoFontNameMap::sPangoFontNameMap = nsnull;
|
||||
|
||||
/**
|
||||
** gfxPangoFontGroup
|
||||
**/
|
||||
|
@ -266,7 +269,8 @@ gfxPangoFont::gfxPangoFont(const nsAString &aName,
|
|||
const gfxFontStyle *aFontStyle)
|
||||
: gfxFont(aName, aFontStyle),
|
||||
mPangoFontDesc(nsnull), mPangoCtx(nsnull),
|
||||
mXftFont(nsnull), mCairoFont(nsnull), mHasMetrics(PR_FALSE),
|
||||
mXftFont(nsnull), mPangoFont(nsnull), mGlyphTestingFont(nsnull),
|
||||
mCairoFont(nsnull), mHasMetrics(PR_FALSE),
|
||||
mAdjustedSize(0)
|
||||
{
|
||||
InitPangoLib();
|
||||
|
@ -277,6 +281,12 @@ gfxPangoFont::~gfxPangoFont()
|
|||
if (mPangoCtx)
|
||||
g_object_unref(mPangoCtx);
|
||||
|
||||
if (mPangoFont)
|
||||
g_object_unref(mPangoFont);
|
||||
|
||||
if (mGlyphTestingFont)
|
||||
g_object_unref(mGlyphTestingFont);
|
||||
|
||||
if (mPangoFontDesc)
|
||||
pango_font_description_free(mPangoFontDesc);
|
||||
|
||||
|
@ -288,6 +298,8 @@ gfxPangoFont::~gfxPangoFont()
|
|||
gfxPangoFont::Shutdown()
|
||||
{
|
||||
ShutdownPangoLib();
|
||||
gfxPangoFontCache::Shutdown();
|
||||
gfxPangoFontNameMap::Shutdown();
|
||||
}
|
||||
|
||||
static PangoStyle
|
||||
|
@ -364,6 +376,12 @@ gfxPangoFont::RealizeFont(PRBool force)
|
|||
g_object_unref(mPangoCtx);
|
||||
if (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();
|
||||
|
||||
|
@ -396,7 +414,7 @@ gfxPangoFont::RealizeFont(PRBool force)
|
|||
return;
|
||||
|
||||
gfxSize isz, lsz;
|
||||
GetSize("x", 1, isz, lsz);
|
||||
GetCharSize('x', isz, lsz);
|
||||
gfxFloat aspect = isz.height / GetStyle()->size;
|
||||
mAdjustedSize =
|
||||
PR_MAX(NS_round(GetStyle()->size*(GetStyle()->sizeAdjust/aspect)), 1.0);
|
||||
|
@ -414,39 +432,69 @@ gfxPangoFont::RealizeXftFont(PRBool force)
|
|||
return;
|
||||
}
|
||||
|
||||
PangoFcFont *fcfont = PANGO_FC_FONT(GetPangoFont());
|
||||
mXftFont = pango_xft_font_get_font(PANGO_FONT(fcfont));
|
||||
mXftFont = pango_xft_font_get_font(GetPangoFont());
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
GList *items = pango_itemize(mPangoCtx, aCharString, 0, aLength, al, NULL);
|
||||
pango_attr_list_unref(al);
|
||||
|
||||
if (!items || g_list_length(items) != 1)
|
||||
if (mGlyphTestingFont)
|
||||
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();
|
||||
pango_shape (aCharString, aLength, &(item->analysis), glstr);
|
||||
pango_shape(&aChar, 1, &analysis, glstr);
|
||||
|
||||
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;
|
||||
inkSize.height = ink_rect.height / FLOAT_PANGO_SCALE;
|
||||
aInkSize.width = ink_rect.width / FLOAT_PANGO_SCALE;
|
||||
aInkSize.height = ink_rect.height / FLOAT_PANGO_SCALE;
|
||||
|
||||
logSize.width = log_rect.width / FLOAT_PANGO_SCALE;
|
||||
logSize.height = log_rect.height / FLOAT_PANGO_SCALE;
|
||||
aLogSize.width = log_rect.width / FLOAT_PANGO_SCALE;
|
||||
aLogSize.height = log_rect.height / FLOAT_PANGO_SCALE;
|
||||
|
||||
pango_glyph_string_free(glstr);
|
||||
pango_item_free(item);
|
||||
g_list_free(items);
|
||||
}
|
||||
|
||||
// rounding and truncation functions for a Freetype floating point number
|
||||
|
@ -463,22 +511,10 @@ gfxPangoFont::GetMetrics()
|
|||
if (mHasMetrics)
|
||||
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
|
||||
float val;
|
||||
|
||||
PangoItem *item = (PangoItem*)items->data;
|
||||
PangoFcFont *fcfont = PANGO_FC_FONT(item->analysis.font);
|
||||
|
||||
XftFont *xftFont = pango_xft_font_get_font(PANGO_FONT(fcfont));
|
||||
XftFont *xftFont = GetXftFont(); // RealizeFont is called here.
|
||||
if (!xftFont)
|
||||
return mMetrics; // XXX error
|
||||
|
||||
|
@ -487,6 +523,7 @@ gfxPangoFont::GetMetrics()
|
|||
return mMetrics; // XXX error
|
||||
|
||||
int size;
|
||||
PangoFcFont *fcfont = PANGO_FC_FONT(mPangoFont);
|
||||
if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) != FcResultMatch)
|
||||
size = 12;
|
||||
mMetrics.emHeight = PR_MAX(1, size);
|
||||
|
@ -508,12 +545,12 @@ gfxPangoFont::GetMetrics()
|
|||
mMetrics.maxAdvance = xftFont->max_advance_width;
|
||||
|
||||
gfxSize isz, lsz;
|
||||
GetSize(" ", 1, isz, lsz);
|
||||
GetCharSize(' ', isz, lsz);
|
||||
mMetrics.spaceWidth = lsz.width;
|
||||
|
||||
// XXX do some FcCharSetHasChar work here to make sure
|
||||
// we have an "x"
|
||||
GetSize("x", 1, isz, lsz);
|
||||
GetCharSize('x', isz, lsz);
|
||||
mMetrics.xHeight = isz.height;
|
||||
mMetrics.aveCharWidth = isz.width;
|
||||
|
||||
|
@ -558,8 +595,7 @@ gfxPangoFont::GetMetrics()
|
|||
XftUnlockFace(xftFont);
|
||||
#else
|
||||
/* pango_cairo case; try to get all the metrics from pango itself */
|
||||
PangoItem *item = (PangoItem*)items->data;
|
||||
PangoFont *font = item->analysis.font;
|
||||
PangoFont *font = GetPangoFont(); // RealizeFont is called here.
|
||||
|
||||
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
|
||||
|
||||
gfxSize isz, lsz;
|
||||
GetSize(" ", 1, isz, lsz);
|
||||
GetCharSize(' ', isz, lsz);
|
||||
mMetrics.spaceWidth = lsz.width;
|
||||
GetSize("x", 1, isz, lsz);
|
||||
GetCharSize('x', isz, lsz);
|
||||
mMetrics.xHeight = isz.height;
|
||||
|
||||
mMetrics.aveCharWidth = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE;
|
||||
|
@ -620,17 +656,34 @@ gfxPangoFont::GetMetrics()
|
|||
return mMetrics;
|
||||
}
|
||||
|
||||
void
|
||||
gfxPangoFont::GetMozLang(nsACString &aMozLang)
|
||||
PRBool
|
||||
gfxPangoFont::HasGlyph(PRUint32 aChar)
|
||||
{
|
||||
aMozLang.Assign(GetStyle()->langGroup);
|
||||
}
|
||||
// Ensure that null character should be missing.
|
||||
if (aChar == 0)
|
||||
return PR_FALSE;
|
||||
|
||||
PangoFont *
|
||||
gfxPangoFont::GetPangoFont()
|
||||
{
|
||||
RealizeFont();
|
||||
return pango_context_load_font(mPangoCtx, mPangoFontDesc);
|
||||
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;
|
||||
}
|
||||
|
||||
nsString
|
||||
|
@ -721,7 +774,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
// We don't need to send an override character here, the characters must be all
|
||||
// LTR
|
||||
const gchar *utf8Chars = NS_REINTERPRET_CAST(const gchar*, aString);
|
||||
InitTextRun(run, utf8Chars, aLength, 0, nsnull, 0);
|
||||
InitTextRun(run, utf8Chars, aLength, 0);
|
||||
} else {
|
||||
const char *chars = NS_REINTERPRET_CAST(const char*, aString);
|
||||
// XXX this could be more efficient.
|
||||
|
@ -732,7 +785,7 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength,
|
|||
nsCAutoString utf8;
|
||||
PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8);
|
||||
AppendUTF16toUTF8(unicodeString, utf8);
|
||||
InitTextRun(run, utf8.get(), utf8.Length(), headerLen, nsnull, 0);
|
||||
InitTextRun(run, utf8.get(), utf8.Length(), headerLen);
|
||||
}
|
||||
return run;
|
||||
}
|
||||
|
@ -750,14 +803,13 @@ gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength,
|
|||
nsCAutoString utf8;
|
||||
PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), 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;
|
||||
}
|
||||
|
||||
void
|
||||
gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
|
||||
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength,
|
||||
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length)
|
||||
PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength)
|
||||
{
|
||||
#if defined(ENABLE_XFT_FAST_PATH_ALWAYS)
|
||||
CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength);
|
||||
|
@ -773,11 +825,7 @@ gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text,
|
|||
(aTextRun->IsRightToLeft()
|
||||
? 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);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
@ -1123,6 +1171,33 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
|
|||
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)
|
||||
void
|
||||
gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun,
|
||||
|
@ -1192,72 +1267,6 @@ gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun,
|
|||
}
|
||||
#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
|
||||
{
|
||||
public:
|
||||
|
@ -1266,7 +1275,7 @@ public:
|
|||
PangoItem *aItem, PRUint32 aUTF16Offset, PRPackedBool aIsRTL) :
|
||||
mItem(aItem),
|
||||
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)
|
||||
{
|
||||
for (PRUint32 i = 0; i < mGroup->FontListLength(); ++i)
|
||||
|
@ -1274,9 +1283,9 @@ public:
|
|||
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; }
|
||||
|
@ -1311,30 +1320,6 @@ public:
|
|||
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:
|
||||
PangoItem *mItem;
|
||||
|
||||
|
@ -1345,7 +1330,6 @@ private:
|
|||
const char *mString; // UTF-8
|
||||
PRUint32 mFontIndex;
|
||||
PRInt32 mLength;
|
||||
PRUint32 mSegmentOffset;
|
||||
PRUint32 mUTF16Offset;
|
||||
PRUint32 mSpaceWidth;
|
||||
|
||||
|
@ -1353,108 +1337,102 @@ private:
|
|||
PRPackedBool mTriedOtherFonts;
|
||||
PRPackedBool mIsRTL;
|
||||
|
||||
void InitSegments(const PRUint32 aOffset,
|
||||
const PRUint32 aLength,
|
||||
const PRUint32 aFontIndex) {
|
||||
mFontIndex = aFontIndex;
|
||||
|
||||
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();
|
||||
nsresult InitSegments(const gchar *aUTF8, PRUint32 aLength) {
|
||||
if (aLength == 0)
|
||||
return NS_OK;
|
||||
const gchar *start = aUTF8;
|
||||
const gchar *last = start + aLength;
|
||||
|
||||
RetryNextFont:
|
||||
nsRefPtr<gfxPangoFont> font = GetNextFont();
|
||||
|
||||
// If we cannot found the font that has the current character glyph,
|
||||
// we should return default font's missing data.
|
||||
if (!font) {
|
||||
font = mFonts[betterFontIndex];
|
||||
checkMissingGlyph = PR_FALSE;
|
||||
if (!font)
|
||||
return AppendMissingSegment(start, last - start);
|
||||
|
||||
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
|
||||
PangoFont *tmpFont = mItem->analysis.font;
|
||||
mItem->analysis.font = font->GetPangoFont();
|
||||
pango_shape(current, aLength, &mItem->analysis, glyphString);
|
||||
mItem->analysis.font = tmpFont;
|
||||
|
||||
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;
|
||||
// find the next point that can be renderd with current font
|
||||
const gchar *missingStart = c;
|
||||
const gchar *next;
|
||||
for (next = g_utf8_next_char(missingStart); next < last; next = g_utf8_next_char(next)) {
|
||||
u = g_utf8_get_char(next);
|
||||
if (font->HasGlyph(PRUint32(u)))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i != 0) {
|
||||
// found glyphs
|
||||
AppendNextSegment(font, offset - (aOffset + skipLength),
|
||||
glyphString, PR_FALSE);
|
||||
}
|
||||
// current font has 0 glyphs for current segment, try with next font
|
||||
if (missingStart == start && next == last)
|
||||
goto RetryNextFont;
|
||||
|
||||
// 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;
|
||||
InitSegments(offset, missingLength, mFontIndex);
|
||||
rv = InitSegments(missingStart, next - missingStart);
|
||||
mFontIndex = fontIndex;
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
PRUint32 next = offset + missingLength;
|
||||
if (next >= aLength) {
|
||||
start = c = next;
|
||||
}
|
||||
|
||||
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);
|
||||
return;
|
||||
return rv;
|
||||
}
|
||||
|
||||
// remains, continue this loop
|
||||
i = j;
|
||||
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);
|
||||
PRUint32 utf16Offset = mUTF16Offset;
|
||||
rv = mGroup->SetGlyphs(mTextRun, aUTF8, aLength,
|
||||
&utf16Offset, glyphString, mSpaceWidth, PR_FALSE);
|
||||
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() {
|
||||
|
@ -1524,7 +1502,7 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
|
|||
if (!prefBranch)
|
||||
return;
|
||||
|
||||
// Add by the order of accept languages.
|
||||
// Add the accept languages.
|
||||
nsXPIDLCString list;
|
||||
nsresult rv = prefBranch->GetCharPref("intl.accept_languages",
|
||||
getter_Copies(list));
|
||||
|
@ -1883,3 +1861,72 @@ GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang)
|
|||
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;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче