Use a PangoFontMap for pango_itemize that provides the same primary font from document language rather than adjacent characters b=416725, r+sr+a1.9=pavlov

This commit is contained in:
karlt+@karlt.net 2008-04-08 23:55:49 -07:00
parent 8dfec1d394
commit e5239b111e

View File

@ -98,6 +98,352 @@ static PangoLanguage *GetPangoLanguage(const nsACString& aLangGroup);
/* static */ gfxPangoFontCache* gfxPangoFontCache::sPangoFontCache = nsnull;
/**
* gfxPangoFontset: An implementation of a PangoFontset for gfxPangoFontMap
*/
#define GFX_TYPE_PANGO_FONTSET (gfx_pango_fontset_get_type())
#define GFX_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONTSET, gfxPangoFontset))
#define GFX_IS_PANGO_FONTSET(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONTSET))
/* static */
GType gfx_pango_fontset_get_type (void);
#define GFX_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
#define GFX_IS_PANGO_FONTSET_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONTSET))
#define GFX_PANGO_FONTSET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONTSET, gfxPangoFontsetClass))
// This struct is POD so that it can be used as a GObject.
struct gfxPangoFontset {
PangoFontset parent_instance;
PangoContext *mContext;
PangoFontDescription *mFontDesc;
PangoLanguage *mLanguage;
PangoFont *mBaseFont;
PangoFontMap *mFontMap;
PangoFontset *mChildFontset;
static PangoFontset *
NewFontset(PangoContext *aContext,
const PangoFontDescription *aFontDesc,
PangoLanguage *aLanguage,
PangoFont *aBaseFont, PangoFontMap *aFontMap)
{
gfxPangoFontset *fontset = static_cast<gfxPangoFontset *>
(g_object_new(GFX_TYPE_PANGO_FONTSET, NULL));
fontset->mContext = aContext;
g_object_ref(aContext);
fontset->mFontDesc = pango_font_description_copy(aFontDesc);
fontset->mLanguage = aLanguage;
fontset->mBaseFont = aBaseFont;
if(aBaseFont)
g_object_ref(aBaseFont);
fontset->mFontMap = aFontMap;
g_object_ref(aFontMap);
return PANGO_FONTSET(fontset);
}
};
struct gfxPangoFontsetClass {
PangoFontsetClass parent_class;
};
G_DEFINE_TYPE (gfxPangoFontset, gfx_pango_fontset, PANGO_TYPE_FONTSET)
static void
gfx_pango_fontset_init(gfxPangoFontset *fontset)
{
fontset->mContext = NULL;
fontset->mFontDesc = NULL;
fontset->mLanguage = NULL;
fontset->mBaseFont = NULL;
fontset->mFontMap = NULL;
fontset->mChildFontset = NULL;
}
static void
gfx_pango_fontset_finalize(GObject *object)
{
gfxPangoFontset *self = GFX_PANGO_FONTSET(object);
if (self->mContext)
g_object_unref(self->mContext);
if (self->mFontDesc)
pango_font_description_free(self->mFontDesc);
if (self->mBaseFont)
g_object_unref(self->mBaseFont);
if (self->mFontMap)
g_object_unref(self->mFontMap);
if (self->mChildFontset)
g_object_unref(self->mChildFontset);
G_OBJECT_CLASS(gfx_pango_fontset_parent_class)->finalize(object);
}
static PangoLanguage *
gfx_pango_fontset_get_language(PangoFontset *fontset)
{
gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
return self->mLanguage;
}
struct ForeachExceptBaseData {
PangoFont *mBaseFont;
PangoFontset *mFontset;
PangoFontsetForeachFunc mFunc;
gpointer mData;
};
static gboolean
foreach_except_base_cb(PangoFontset *fontset, PangoFont *font, gpointer data)
{
ForeachExceptBaseData *baseData =
static_cast<ForeachExceptBaseData *>(data);
// returning false means continue with the other fonts in the set
return font != baseData->mBaseFont &&
(*baseData->mFunc)(baseData->mFontset, font, baseData->mData);
}
static PangoFontset *
EnsureChildFontset(gfxPangoFontset *self)
{
if (!self->mChildFontset) {
// To consider:
//
// * If this is happening often (e.g. Chinese/Japanese pagess where a
// Latin font is specified first), and Pango's 64-entry pattern
// cache is not large enough, then a fontset cache here could be
// helpful. Ideally we'd only cache the fonts that are actually
// accessed rather than all the fonts from the FcFontSort.
//
// * Mozilla's langGroup font prefs could be used to specify preferred
// fallback fonts for the script of the characters (as indicated by
// Pango in mLanguage), by doing the conversion from gfxFontGroup
// "families" to PangoFcFontMap "family" here.
self->mChildFontset =
pango_font_map_load_fontset(self->mFontMap, self->mContext,
self->mFontDesc, self->mLanguage);
}
return self->mChildFontset;
}
static void
gfx_pango_fontset_foreach(PangoFontset *fontset, PangoFontsetForeachFunc func,
gpointer data)
{
gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
if (self->mBaseFont && (*func)(fontset, self->mBaseFont, data))
return;
// Falling back to secondary fonts
PangoFontset *childFontset = EnsureChildFontset(self);
ForeachExceptBaseData baseData = { self->mBaseFont, fontset, func, data };
pango_fontset_foreach(childFontset, foreach_except_base_cb, &baseData);
}
static PangoFont *
gfx_pango_fontset_get_font(PangoFontset *fontset, guint wc)
{
gfxPangoFontset *self = GFX_PANGO_FONTSET(fontset);
PangoCoverageLevel baseLevel = PANGO_COVERAGE_NONE;
if (self->mBaseFont) {
// PangoFcFontMap caches this:
PangoCoverage *coverage =
pango_font_get_coverage(self->mBaseFont, self->mLanguage);
if (coverage) {
baseLevel = pango_coverage_get(coverage, wc);
pango_coverage_unref(coverage);
}
}
if (baseLevel != PANGO_COVERAGE_EXACT) {
PangoFontset *childFontset = EnsureChildFontset(self);
PangoFont *childFont = pango_fontset_get_font(childFontset, wc);
if (!self->mBaseFont || childFont == self->mBaseFont)
return childFont;
if (childFont) {
PangoCoverage *coverage =
pango_font_get_coverage(childFont, self->mLanguage);
if (coverage) {
PangoCoverageLevel childLevel =
pango_coverage_get(coverage, wc);
pango_coverage_unref(coverage);
// Only use the child font if better than the base font.
if (childLevel > baseLevel)
return childFont;
}
g_object_unref(childFont);
}
}
g_object_ref(self->mBaseFont);
return self->mBaseFont;
}
static void
gfx_pango_fontset_class_init (gfxPangoFontsetClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PangoFontsetClass *fontset_class = PANGO_FONTSET_CLASS (klass);
object_class->finalize = gfx_pango_fontset_finalize;
fontset_class->get_font = gfx_pango_fontset_get_font;
// inherit fontset_class->get_metrics (which won't be used anyway)
fontset_class->get_language = gfx_pango_fontset_get_language;
fontset_class->foreach = gfx_pango_fontset_foreach;
}
/**
* gfxPangoFontMap: An implementation of a PangoFontMap.
*
* This allows the primary (base) font to be specified. There are two
* advantages to this:
*
* 1. Always using the same base font irrespective of the language that Pango
* chooses for the script means that PANGO_SCRIPT_COMMON characters are
* consistently rendered with the same font. (Bug 339513 and bug 416725)
*
* 2. We normally have the base font from the gfxFont cache so this saves a
* FcFontSort when the entry has expired from Pango's much smaller pattern
* cache.
*
* This object references a child font map rather than deriving so that
* the cache of the child font map is shared.
*/
#define GFX_TYPE_PANGO_FONT_MAP (gfx_pango_font_map_get_type())
#define GFX_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMap))
#define GFX_IS_PANGO_FONT_MAP(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GFX_TYPE_PANGO_FONT_MAP))
GType gfx_pango_font_map_get_type (void);
#define GFX_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
#define GFX_IS_PANGO_FONT_MAP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GFX_TYPE_PANGO_FONT_MAP))
#define GFX_PANGO_FONT_MAP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GFX_TYPE_PANGO_FONT_MAP, gfxPangoFontMapClass))
// Do not instantiate this class directly, but use NewFontMap.
// This struct is POD so that it can be used as a GObject.
struct gfxPangoFontMap {
PangoFontMap parent_instance;
PangoFontMap *mChildFontMap;
PangoFont *mBaseFont;
static PangoFontMap *
NewFontMap(PangoFontMap *aChildFontMap, PangoFont *aBaseFont)
{
NS_ASSERTION(strcmp(pango_font_map_get_shape_engine_type(aChildFontMap),
PANGO_RENDER_TYPE_FC) == 0,
"Unexpected child PangoFontMap shape engine type");
gfxPangoFontMap *fontmap = static_cast<gfxPangoFontMap *>
(g_object_new(GFX_TYPE_PANGO_FONT_MAP, NULL));
fontmap->mChildFontMap = aChildFontMap;
g_object_ref(aChildFontMap);
fontmap->mBaseFont = aBaseFont;
if(aBaseFont)
g_object_ref(aBaseFont);
return PANGO_FONT_MAP(fontmap);
}
void
SetBaseFont(PangoFont *aBaseFont)
{
if (mBaseFont)
g_object_unref(mBaseFont);
mBaseFont = aBaseFont;
if (aBaseFont)
g_object_ref(aBaseFont);
}
};
struct gfxPangoFontMapClass {
PangoFontMapClass parent_class;
};
G_DEFINE_TYPE (gfxPangoFontMap, gfx_pango_font_map, PANGO_TYPE_FONT_MAP)
static void
gfx_pango_font_map_init(gfxPangoFontMap *fontset)
{
fontset->mChildFontMap = NULL;
fontset->mBaseFont = NULL;
}
static void
gfx_pango_font_map_finalize(GObject *object)
{
gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(object);
if (self->mChildFontMap)
g_object_unref(self->mChildFontMap);
if (self->mBaseFont)
g_object_unref(self->mBaseFont);
G_OBJECT_CLASS(gfx_pango_font_map_parent_class)->finalize(object);
}
static PangoFont *
gfx_pango_font_map_load_font(PangoFontMap *fontmap, PangoContext *context,
const PangoFontDescription *description)
{
gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
if (self->mBaseFont) {
g_object_ref(self->mBaseFont);
return self->mBaseFont;
}
return pango_font_map_load_font(self->mChildFontMap, context, description);
}
static PangoFontset *
gfx_pango_font_map_load_fontset(PangoFontMap *fontmap, PangoContext *context,
const PangoFontDescription *desc,
PangoLanguage *language)
{
gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
return gfxPangoFontset::NewFontset(context, desc, language,
self->mBaseFont, self->mChildFontMap);
}
static void
gfx_pango_font_map_list_families(PangoFontMap *fontmap,
PangoFontFamily ***families, int *n_families)
{
gfxPangoFontMap *self = GFX_PANGO_FONT_MAP(fontmap);
pango_font_map_list_families(self->mChildFontMap, families, n_families);
}
static void
gfx_pango_font_map_class_init(gfxPangoFontMapClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
PangoFontMapClass *fontmap_class = PANGO_FONT_MAP_CLASS (klass);
object_class->finalize = gfx_pango_font_map_finalize;
fontmap_class->load_font = gfx_pango_font_map_load_font;
fontmap_class->load_fontset = gfx_pango_font_map_load_fontset;
fontmap_class->list_families = gfx_pango_font_map_list_families;
fontmap_class->shape_engine_type = PANGO_RENDER_TYPE_FC;
}
/**
** gfxPangoFontGroup
**/
@ -1228,6 +1574,22 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun,
}
#endif
static void
SetBaseFont(PangoContext *aContext, PangoFont *aBaseFont)
{
PangoFontMap *fontmap = pango_context_get_font_map(aContext);
if (GFX_IS_PANGO_FONT_MAP(fontmap)) {
// Update the base font in the gfxPangoFontMap
GFX_PANGO_FONT_MAP(fontmap)->SetBaseFont(aBaseFont);
}
else if (aBaseFont) {
// Change the font map to record and activate the base font
fontmap = gfxPangoFontMap::NewFontMap(fontmap, aBaseFont);
pango_context_set_font_map(aContext, fontmap);
g_object_unref(fontmap);
}
}
void
gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
const gchar *aUTF8, PRUint32 aUTF8Length,
@ -1253,6 +1615,18 @@ gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun,
// except that we almost always have something...
pango_context_set_language(context, lang);
// Set the primary font for consistent font selection for common
// characters, but use the default Pango behavior
// (selecting generic fonts from the script of the characters)
// in two situations:
// 1. When we don't have a language to make a good choice for the
// primary font.
// 2. For system fonts, use the default Pango behavior
// to give consistency with other apps.
if (lang && !GetStyle()->systemFont) {
SetBaseFont(context, GetFontAt(0)->GetPangoFont());
}
PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR;
GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull);