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
#include "nsDataHashtable.h"
#include "nsClassHashtable.h"
class FontSelector;
@ -74,11 +75,14 @@ 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);
PRUint32 GetGlyph(const PRUint32 aChar);
virtual gfxTextRun::Metrics Measure(gfxTextRun *aTextRun,
PRUint32 aStart, PRUint32 aEnd,
PRBool aTightBoundingBox,
@ -91,6 +95,8 @@ protected:
PangoContext *mPangoCtx;
XftFont *mXftFont;
PangoFont *mPangoFont;
PangoFont *mGlyphTestingFont;
cairo_scaled_font_t *mCairoFont;
PRBool mHasMetrics;
@ -100,6 +106,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 +138,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,
nsresult SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont,
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,
const gchar *aUTF8, PRUint32 aUTF8Length,
const PRUnichar *aUTF16Text, PRUint32 aUTF16Length);
nsresult SetMissingGlyphs(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 *aUTF16Offset);
void CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 aUTF8HeaderLength);
@ -161,4 +165,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,44 @@ 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 *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 *
gfxPangoFont::GetPangoFont()
PRUint32
gfxPangoFont::GetGlyph(const PRUint32 aChar)
{
RealizeFont();
return pango_context_load_font(mPangoCtx, mPangoFontDesc);
// Ensure that null character should be missing.
if (aChar == 0)
return 0;
RealizePangoFont();
return pango_fc_font_get_glyph(PANGO_FC_FONT(mPangoFont), aChar);
}
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
// 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 +795,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 +813,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 +835,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);
}
CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength);
#endif
}
@ -891,7 +949,8 @@ gfxPangoFont::Measure(gfxTextRun *aTextRun,
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*
CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont)
@ -1005,7 +1064,7 @@ SetMissingGlyphForUCS4(gfxTextRun *aTextRun, PRUint32 aIndex, gunichar aCh)
}
nsresult
gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont,
const gchar *aUTF8, PRUint32 aUTF8Length,
PRUint32 *aUTF16Offset, PangoGlyphString *aGlyphs,
PangoGlyphUnit aOverrideSpaceWidth,
@ -1053,7 +1112,13 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun,
for (;;) {
++glyphCount;
if (IS_MISSING_GLYPH(aGlyphs->glyphs[glyphIndex].glyph)) {
haveMissingGlyph = PR_TRUE;
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;
}
glyphIndex += direction;
if (glyphCount == numGlyphs ||
@ -1123,6 +1188,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 +1284,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 +1292,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 +1300,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 +1337,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 +1347,6 @@ private:
const char *mString; // UTF-8
PRUint32 mFontIndex;
PRInt32 mLength;
PRUint32 mSegmentOffset;
PRUint32 mUTF16Offset;
PRUint32 mSpaceWidth;
@ -1353,108 +1354,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;
}
// 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;
}
// current font has 0 glyphs for current segment, try with next font
if (missingStart == start && next == last)
goto RetryNextFont;
// 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;
rv = InitSegments(missingStart, next - missingStart);
mFontIndex = fontIndex;
NS_ENSURE_SUCCESS(rv, rv);
start = c = next;
}
// Shaping
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 = font->GetPangoFont();
pango_shape(current, aLength, &mItem->analysis, glyphString);
mItem->analysis.font = pf;
pango_shape(aUTF8, 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;
break;
}
}
if (i != 0) {
// found glyphs
AppendNextSegment(font, offset - (aOffset + skipLength),
glyphString, PR_FALSE);
}
// missing glyphs
PRUint32 fontIndex = mFontIndex;
InitSegments(offset, missingLength, mFontIndex);
mFontIndex = fontIndex;
PRUint32 next = offset + missingLength;
if (next >= aLength) {
pango_glyph_string_free(glyphString);
return;
}
// 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;
nsresult rv = mTextRun->AddGlyphRun(aFont, mUTF16Offset);
if (NS_FAILED(rv)) {
NS_ERROR("AddGlyphRun Failed");
pango_glyph_string_free(glyphString);
return rv;
}
AppendNextSegment(font, aLength - skipLength, glyphString, skipLength == 0);
PRUint32 utf16Offset = mUTF16Offset;
rv = mGroup->SetGlyphs(mTextRun, aFont, 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 +1519,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));
@ -1887,3 +1882,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;
}