diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp index 06ac171bcce..f0353c763e8 100644 --- a/gfx/thebes/src/gfxPangoFonts.cpp +++ b/gfx/thebes/src/gfxPangoFonts.cpp @@ -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 + (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(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 + (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);