From 672cdf445be79315c2971cfc494ca7522b5bfe1a Mon Sep 17 00:00:00 2001 From: "pavlov%pavlov.net" Date: Wed, 19 Sep 2007 23:53:10 +0000 Subject: [PATCH] backing out 362682 due to linux tinderboxen not being upgraded... (396805, 389426) --- config/system-headers | 3 - configure.in | 22 +- gfx/cairo/cairo/src/cairo-ft-font.c | 14 +- gfx/thebes/public/gfxPangoFonts.h | 68 ++- gfx/thebes/public/gfxPlatformGtk.h | 2 +- gfx/thebes/src/cairo-xlib-utils.c | 1 - gfx/thebes/src/gfxPangoFonts.cpp | 915 ++++++++++++++++++++++------ gfx/thebes/src/gfxPlatformGtk.cpp | 100 ++- 8 files changed, 897 insertions(+), 228 deletions(-) diff --git a/config/system-headers b/config/system-headers index b19546916c7..c40e1dcae15 100644 --- a/config/system-headers +++ b/config/system-headers @@ -213,7 +213,6 @@ Gdiplus.h gdk/gdkevents.h gdk/gdk.h gdk/gdkkeysyms.h -gdk/gdkpango.h gdk/gdkprivate.h gdk/gdkregion.h gdk/gdkwindow.h @@ -506,7 +505,6 @@ PALM_CMN.H pango-engine.h pango-glyph.h pango-modules.h -pango/pangocairo.h pango/pangofc-decoder.h pango/pangofc-font.h pango/pangofc-fontmap.h @@ -515,7 +513,6 @@ pango/pango-fontmap.h pango/pango.h pango/pangoxft.h pango/pangox.h -pango/pango-utils.h pango-types.h pascal.h Patches.h diff --git a/configure.in b/configure.in index e3bc12ef67b..32785ef6974 100644 --- a/configure.in +++ b/configure.in @@ -116,8 +116,7 @@ PERL_VERSION=5.006 LIBART_VERSION=2.3.4 CAIRO_VERSION=1.4.2 GLITZ_VERSION=0.4.0 -PANGO_VERSION=1.10.0 -GTK2_VERSION=1.8.0 +GTK2_VERSION=1.3.7 MAKE_VERSION=3.78 WINDRES_VERSION=2.14.90 W32API_VERSION=3.8 @@ -4568,7 +4567,7 @@ fi if test "$COMPILE_ENVIRONMENT"; then if test "$MOZ_ENABLE_GTK2" then - PKG_CHECK_MODULES(MOZ_GTK2, gtk+-2.0 >= $GTK2_VERSION gdk-x11-2.0 glib-2.0 gobject-2.0) + PKG_CHECK_MODULES(MOZ_GTK2, gtk+-2.0 >= 1.3.7 gdk-x11-2.0 glib-2.0 gobject-2.0) fi fi # COMPILE_ENVIRONMENT @@ -4807,7 +4806,7 @@ if test "$MOZ_ENABLE_XFT" then AC_DEFINE(MOZ_ENABLE_XFT) PKG_CHECK_MODULES(MOZ_XFT, xft) - PKG_CHECK_MODULES(_PANGOCHK, pango >= $PANGO_VERSION) + PKG_CHECK_MODULES(_PANGOCHK, pango >= 1.1.0) fi AC_SUBST(MOZ_ENABLE_XFT) @@ -4824,7 +4823,12 @@ MOZ_ARG_ENABLE_BOOL(pango, if test "$MOZ_ENABLE_PANGO" && test -z "$MOZ_ENABLE_CAIRO_GFX" then - AC_MSG_ERROR([Cairo gfx is required for Pango font rendering]) + AC_DEFINE(MOZ_ENABLE_PANGO) + PKG_CHECK_MODULES(MOZ_PANGO, pangoxft >= 1.6.0) + + AC_SUBST(MOZ_ENABLE_PANGO) + AC_SUBST(MOZ_PANGO_CFLAGS) + AC_SUBST(MOZ_PANGO_LIBS) fi if test "$MOZ_ENABLE_GTK2" && test "$MOZ_ENABLE_CAIRO_GFX" @@ -4840,7 +4844,13 @@ fi if test "$MOZ_ENABLE_PANGO" && test "$MOZ_ENABLE_CAIRO_GFX" then AC_DEFINE(MOZ_ENABLE_PANGO) - PKG_CHECK_MODULES(MOZ_PANGO, pango >= $PANGO_VERSION pangocairo >= $PANGO_VERSION pangoft2 >= $PANGO_VERSION) + dnl PKG_CHECK_MODULES(MOZ_PANGO, pango >= 1.10.0 pangocairo >= 1.10.0) + if test "$MOZ_X11"; then + PKG_CHECK_MODULES(MOZ_PANGO, pango >= 1.6.0 pangoft2 >= 1.6.0 pangoxft >= 1.6.0) + else + PKG_CHECK_MODULES(MOZ_PANGO, pango >= 1.6.0 pangoft2 >= 1.6.0) + fi + AC_SUBST(MOZ_ENABLE_PANGO) AC_SUBST(MOZ_PANGO_CFLAGS) AC_SUBST(MOZ_PANGO_LIBS) diff --git a/gfx/cairo/cairo/src/cairo-ft-font.c b/gfx/cairo/cairo/src/cairo-ft-font.c index 8695ea119ed..8fb413743d1 100644 --- a/gfx/cairo/cairo/src/cairo-ft-font.c +++ b/gfx/cairo/cairo/src/cairo-ft-font.c @@ -1097,9 +1097,7 @@ _render_glyph_bitmap (FT_Face face, * we avoid the FT_LOAD_NO_RECURSE flag. */ error = FT_Render_Glyph (glyphslot, FT_RENDER_MODE_NORMAL); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { + if (error) { _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_NO_MEMORY; } @@ -1893,9 +1891,8 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, error = FT_Load_Glyph (scaled_font->unscaled->face, _cairo_scaled_glyph_index(scaled_glyph), load_flags); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { + + if (error) { status = CAIRO_STATUS_NO_MEMORY; goto FAIL; } @@ -2045,9 +2042,8 @@ _cairo_ft_scaled_glyph_init (void *abstract_font, error = FT_Load_Glyph (face, _cairo_scaled_glyph_index(scaled_glyph), load_flags | FT_LOAD_NO_BITMAP); - /* XXX ignoring all other errors for now. They are not fatal, typically - * just a glyph-not-found. */ - if (error == FT_Err_Out_Of_Memory) { + + if (error) { _cairo_ft_unscaled_font_unlock_face (unscaled); _cairo_error (CAIRO_STATUS_NO_MEMORY); return CAIRO_STATUS_NO_MEMORY; diff --git a/gfx/thebes/public/gfxPangoFonts.h b/gfx/thebes/public/gfxPangoFonts.h index 2e6dbb7ebb6..54dc20b3a88 100644 --- a/gfx/thebes/public/gfxPangoFonts.h +++ b/gfx/thebes/public/gfxPangoFonts.h @@ -44,15 +44,14 @@ #include "gfxFont.h" #include +#include -// Control when we bypass Pango -// Enable this to use FreeType to glyph-convert 8bit-only textruns, but use Pango +// Control when we use Xft directly, bypassing Pango +// Enable this to use Xft to glyph-convert 8bit-only textruns, but use Pango // to shape any textruns with non-8bit characters -// XXX -#define ENABLE_FAST_PATH_8BIT -// Enable this to bypass Pango shaping for all textruns. Don't expect -// anything other than simple Latin work though! -//#define ENABLE_FAST_PATH_ALWAYS +#define ENABLE_XFT_FAST_PATH_8BIT +// Enable this to use Xft to glyph-convert all textruns +// #define ENABLE_XFT_FAST_PATH_ALWAYS #include "nsDataHashtable.h" #include "nsClassHashtable.h" @@ -71,15 +70,17 @@ public: virtual const gfxFont::Metrics& GetMetrics(); - PangoFontDescription *GetPangoFontDescription() { if (!mPangoFontDesc) RealizeFont(); return mPangoFontDesc; } - PangoContext *GetPangoContext() { if (!mPangoFontDesc) RealizeFont(); return mPangoCtx; } + PangoFontDescription *GetPangoFontDescription() { RealizeFont(); return mPangoFontDesc; } + PangoContext *GetPangoContext() { RealizeFont(); return mPangoCtx; } void GetMozLang(nsACString &aMozLang); void GetActualFontFamily(nsACString &aFamily); - PangoFont *GetPangoFont() { if (!mPangoFont) RealizePangoFont(); return mPangoFont; } - gfxFloat GetAdjustedSize() { if (!mPangoFontDesc) RealizeFont(); return mAdjustedSize; } + 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 nsString GetUniqueName(); @@ -94,7 +95,9 @@ protected: PangoFontDescription *mPangoFontDesc; PangoContext *mPangoCtx; + XftFont *mXftFont; PangoFont *mPangoFont; + PangoFont *mGlyphTestingFont; cairo_scaled_font_t *mCairoFont; PRBool mHasMetrics; @@ -103,6 +106,7 @@ protected: gfxFloat mAdjustedSize; void RealizeFont(PRBool force = PR_FALSE); + void RealizeXftFont(PRBool force = PR_FALSE); void RealizePangoFont(PRBool aForce = PR_FALSE); void GetCharSize(const char aChar, gfxSize& aInkSize, gfxSize& aLogSize, PRUint32 *aGlyphID = nsnull); @@ -120,7 +124,7 @@ public: virtual gfxFontGroup *Copy(const gfxFontStyle *aStyle); - // Create and initialize a textrun using Pango + // Create and initialize a textrun using Pango (or Xft) virtual gfxTextRun *MakeTextRun(const PRUnichar *aString, PRUint32 aLength, const Parameters *aParams, PRUint32 aFlags); virtual gfxTextRun *MakeTextRun(const PRUint8 *aString, PRUint32 aLength, @@ -142,8 +146,8 @@ protected: * but stored in UTF16 format) */ void InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text, - PRUint32 aUTF8Length, PRBool aTake8BitPath); - + PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, + PRBool aTake8BitPath); // Returns NS_ERROR_FAILURE if there's a missing glyph nsresult SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont, const gchar *aUTF8, PRUint32 aUTF8Length, @@ -154,11 +158,11 @@ protected: const gchar *aUTF8, PRUint32 aUTF8Length, PRUint32 *aUTF16Offset); void CreateGlyphRunsItemizing(gfxTextRun *aTextRun, - const gchar *aUTF8, PRUint32 aUTF8Length); -#if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS) - PRBool CanTakeFastPath(PRUint32 aFlags); - void CreateGlyphRunsFast(gfxTextRun *aTextRun, - const gchar *aUTF8, PRUint32 aUTF8Length); + const gchar *aUTF8, PRUint32 aUTF8Length, + PRUint32 aUTF8HeaderLength); +#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS) + void CreateGlyphRunsXft(gfxTextRun *aTextRun, + const gchar *aUTF8, PRUint32 aUTF8Length); #endif static PRBool FontCallback (const nsAString& fontName, @@ -208,4 +212,30 @@ private: nsClassHashtable 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 mPangoFonts; +}; #endif /* GFX_PANGOFONTS_H */ diff --git a/gfx/thebes/public/gfxPlatformGtk.h b/gfx/thebes/public/gfxPlatformGtk.h index 4c7eafd0fbb..01773046f86 100644 --- a/gfx/thebes/public/gfxPlatformGtk.h +++ b/gfx/thebes/public/gfxPlatformGtk.h @@ -79,7 +79,7 @@ public: if (sDPI == -1) { InitDPI(); } - NS_ASSERTION(sDPI > 0, "Something is wrong"); + NS_ASSERTION(sDPI != 0, "Something is wrong"); return sDPI; } diff --git a/gfx/thebes/src/cairo-xlib-utils.c b/gfx/thebes/src/cairo-xlib-utils.c index a7d9333ba1c..69cccf2f483 100644 --- a/gfx/thebes/src/cairo-xlib-utils.c +++ b/gfx/thebes/src/cairo-xlib-utils.c @@ -40,7 +40,6 @@ #include "cairo-xlib.h" #include -#include #if HAVE_STDINT_H #include #elif HAVE_INTTYPES_H diff --git a/gfx/thebes/src/gfxPangoFonts.cpp b/gfx/thebes/src/gfxPangoFonts.cpp index db95d9fb12d..89e886ddbcb 100644 --- a/gfx/thebes/src/gfxPangoFonts.cpp +++ b/gfx/thebes/src/gfxPangoFonts.cpp @@ -21,7 +21,6 @@ * Contributor(s): * Vladimir Vukicevic * Masayuki Nakano - * Behdad Esfahbod * * based on nsFontMetricsPango.cpp by * Christopher Blizzard @@ -40,6 +39,11 @@ * * ***** END LICENSE BLOCK ***** */ +#ifdef XP_BEOS +#define THEBES_USE_PANGO_CAIRO +#endif + +#define PANGO_ENABLE_ENGINE #define PANGO_ENABLE_BACKEND #include "prtypes.h" @@ -58,38 +62,45 @@ #include "nsPromiseFlatString.h" #include "gfxContext.h" -#include "gfxPlatformGtk.h" #include "gfxPangoFonts.h" #include "nsCRT.h" -#include - -#include -#include - -#include -#include -#include -#include +#include "cairo.h" +#ifndef THEBES_USE_PANGO_CAIRO +#include +#include #include + +#include +#include + +#include +#include + +#include "cairo-ft.h" + +#include "gfxPlatformGtk.h" + +#else // THEBES_USE_PANGO_CAIRO + +#include + +#endif // THEBES_USE_PANGO_CAIRO + #include #define FLOAT_PANGO_SCALE ((gfxFloat)PANGO_SCALE) -#ifndef PANGO_GLYPH_UNKNOWN_FLAG -#define PANGO_GLYPH_UNKNOWN_FLAG ((PangoGlyph)0x10000000) -#endif -#ifndef PANGO_GLYPH_EMPTY -#define PANGO_GLYPH_EMPTY ((PangoGlyph)0) -#endif -#define IS_MISSING_GLYPH(g) (((g) & PANGO_GLYPH_UNKNOWN_FLAG) || (g) == PANGO_GLYPH_EMPTY) +#define IS_MISSING_GLYPH(g) (((g) & 0x10000000) || (g) == 0x0FFFFFFF) 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 @@ -155,7 +166,6 @@ gfxPangoFontGroup::gfxPangoFontGroup (const nsAString& families, // XXX If there are no actual fonts, we should use dummy family. // Pango will resolve from this. - // behdad: yep, looks good. if (familyArray.Count() == 0) { // printf("%s(%s)\n", NS_ConvertUTF16toUTF8(families).get(), // aStyle->langGroup.get()); @@ -184,12 +194,82 @@ gfxPangoFontGroup::Copy(const gfxFontStyle *aStyle) ** gfxPangoFont **/ +// Glue to avoid build/runtime dependencies on Pango > 1.6, +// because we like living in 1999 + +#ifndef THEBES_USE_PANGO_CAIRO +static void +(* PTR_pango_font_description_set_absolute_size)(PangoFontDescription*, double) + = nsnull; + +static void InitPangoLib() +{ + static PRBool initialized = PR_FALSE; + if (initialized) + return; + initialized = PR_TRUE; + + g_type_init(); + + PRLibrary *pangoLib = nsnull; + PTR_pango_font_description_set_absolute_size = + (void (*)(PangoFontDescription*, double)) + PR_FindFunctionSymbolAndLibrary("pango_font_description_set_absolute_size", + &pangoLib); + if (pangoLib) + PR_UnloadLibrary(pangoLib); + + PRLibrary *xftLib = nsnull; + int *xft_max_freetype_files_ptr = nsnull; + xft_max_freetype_files_ptr = (int*) PR_FindSymbolAndLibrary("XftMaxFreeTypeFiles", &xftLib); + if (xft_max_freetype_files_ptr && *xft_max_freetype_files_ptr < 50) + *xft_max_freetype_files_ptr = 50; + if (xftLib) + PR_UnloadLibrary(xftLib); +} + +static void +ShutdownPangoLib() +{ +} + +static void +MOZ_pango_font_description_set_absolute_size(PangoFontDescription *desc, + double size) +{ + if (PTR_pango_font_description_set_absolute_size) { + PTR_pango_font_description_set_absolute_size(desc, size); + } else { + pango_font_description_set_size(desc, + (gint)(size * 72.0 / + gfxPlatformGtk::DPI())); + } +} +#else +static inline void InitPangoLib() +{ +} + +static inline void ShutdownPangoLib() +{ +} + +static inline void +MOZ_pango_font_description_set_absolute_size(PangoFontDescription *desc, double size) +{ + pango_font_description_set_absolute_size(desc, size); +} +#endif + gfxPangoFont::gfxPangoFont(const nsAString &aName, const gfxFontStyle *aFontStyle) : gfxFont(aName, aFontStyle), - mPangoFontDesc(nsnull), mPangoCtx(nsnull), mPangoFont(nsnull), - mCairoFont(nsnull), mHasMetrics(PR_FALSE), mAdjustedSize(0) + mPangoFontDesc(nsnull), mPangoCtx(nsnull), + mXftFont(nsnull), mPangoFont(nsnull), mGlyphTestingFont(nsnull), + mCairoFont(nsnull), mHasMetrics(PR_FALSE), + mAdjustedSize(0) { + InitPangoLib(); } gfxPangoFont::~gfxPangoFont() @@ -200,6 +280,9 @@ gfxPangoFont::~gfxPangoFont() if (mPangoFont) g_object_unref(mPangoFont); + if (mGlyphTestingFont) + g_object_unref(mGlyphTestingFont); + if (mPangoFontDesc) pango_font_description_free(mPangoFontDesc); @@ -210,12 +293,9 @@ gfxPangoFont::~gfxPangoFont() /* static */ void gfxPangoFont::Shutdown() { + ShutdownPangoLib(); gfxPangoFontCache::Shutdown(); - - PangoFontMap *fontmap = pango_cairo_font_map_get_default (); - if (PANGO_IS_FC_FONT_MAP (fontmap)) - pango_fc_font_map_shutdown (PANGO_FC_FONT_MAP (fontmap)); - + gfxPangoFontNameMap::Shutdown(); } static PangoStyle @@ -295,24 +375,28 @@ gfxPangoFont::RealizeFont(PRBool force) if (mPangoFont) { g_object_unref(mPangoFont); mPangoFont = nsnull; + mXftFont = nsnull; + // XXX we don't need to reset mGlyphTestingFont } mPangoFontDesc = pango_font_description_new(); pango_font_description_set_family(mPangoFontDesc, NS_ConvertUTF16toUTF8(mName).get()); gfxFloat size = mAdjustedSize ? mAdjustedSize : GetStyle()->size; - pango_font_description_set_absolute_size(mPangoFontDesc, size * PANGO_SCALE); + MOZ_pango_font_description_set_absolute_size(mPangoFontDesc, size * PANGO_SCALE); pango_font_description_set_style(mPangoFontDesc, ThebesStyleToPangoStyle(GetStyle())); pango_font_description_set_weight(mPangoFontDesc, ThebesStyleToPangoWeight(GetStyle())); //printf ("%s, %f, %d, %d\n", NS_ConvertUTF16toUTF8(mName).get(), GetStyle()->size, ThebesStyleToPangoStyle(GetStyle()), ThebesStyleToPangoWeight(GetStyle())); - mPangoCtx = gdk_pango_context_get (); +#ifndef THEBES_USE_PANGO_CAIRO + mPangoCtx = pango_xft_get_context(GDK_DISPLAY(), 0); + gdk_pango_context_set_colormap(mPangoCtx, gdk_rgb_get_cmap()); +#else + mPangoCtx = pango_cairo_font_map_create_context(PANGO_CAIRO_FONT_MAP(pango_cairo_font_map_get_default())); +#endif - if (!GetStyle()->langGroup.IsEmpty()) { - PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup); - if (lang) - pango_context_set_language(mPangoCtx, lang); - } + if (!GetStyle()->langGroup.IsEmpty()) + pango_context_set_language(mPangoCtx, GetPangoLanguage(GetStyle()->langGroup)); pango_context_set_font_description(mPangoCtx, mPangoFontDesc); @@ -332,6 +416,20 @@ gfxPangoFont::RealizeFont(PRBool force) RealizeFont(PR_TRUE); } +void +gfxPangoFont::RealizeXftFont(PRBool force) +{ + // already realized? + if (!force && mXftFont) + return; + if (GDK_DISPLAY() == 0) { + mXftFont = nsnull; + return; + } + + mXftFont = pango_xft_font_get_font(GetPangoFont()); +} + void gfxPangoFont::RealizePangoFont(PRBool aForce) { @@ -340,6 +438,7 @@ gfxPangoFont::RealizePangoFont(PRBool aForce) if (mPangoFont) { g_object_unref(mPangoFont); mPangoFont = nsnull; + mXftFont = nsnull; } RealizeFont(); gfxPangoFontCache *cache = gfxPangoFontCache::GetPangoFontCache(); @@ -352,6 +451,19 @@ gfxPangoFont::RealizePangoFont(PRBool aForce) if (!mPangoFont) return; // Error cache->Put(mPangoFontDesc, mPangoFont); + + if (mGlyphTestingFont) + return; + + // 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 @@ -408,6 +520,89 @@ gfxPangoFont::GetMetrics() if (mHasMetrics) return mMetrics; +#ifndef THEBES_USE_PANGO_CAIRO + float val; + + XftFont *xftFont = GetXftFont(); // RealizeFont is called here. + if (!xftFont) + return mMetrics; // XXX error + + FT_Face face = XftLockFace(xftFont); + if (!face) + 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); + + mMetrics.maxAscent = xftFont->ascent; + mMetrics.maxDescent = xftFont->descent; + + double lineHeight = mMetrics.maxAscent + mMetrics.maxDescent; + + if (lineHeight > mMetrics.emHeight) + mMetrics.internalLeading = lineHeight - mMetrics.emHeight; + else + mMetrics.internalLeading = 0; + mMetrics.externalLeading = 0; + + mMetrics.maxHeight = lineHeight; + mMetrics.emAscent = mMetrics.maxAscent * mMetrics.emHeight / lineHeight; + mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent; + mMetrics.maxAdvance = xftFont->max_advance_width; + + gfxSize isz, lsz; + GetCharSize(' ', isz, lsz, &mSpaceGlyph); + mMetrics.spaceWidth = lsz.width; + + // XXX do some FcCharSetHasChar work here to make sure + // we have an "x" + GetCharSize('x', isz, lsz); + mMetrics.xHeight = isz.height; + mMetrics.aveCharWidth = isz.width; + + val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_position, + face->size->metrics.y_scale); + if (!val) + val = - PR_MAX(1, floor(0.1 * xftFont->height + 0.5)); + + mMetrics.underlineOffset = val; + + val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness, + face->size->metrics.y_scale); + if (!val) + val = floor(0.05 * xftFont->height + 0.5); + + mMetrics.underlineSize = PR_MAX(1, val); + + TT_OS2 *os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2); + + if (os2 && os2->ySuperscriptYOffset) { + val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset, + face->size->metrics.y_scale); + mMetrics.superscriptOffset = PR_MAX(1, val); + } else { + mMetrics.superscriptOffset = mMetrics.xHeight; + } + + // mSubscriptOffset + if (os2 && os2->ySubscriptYOffset) { + val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySubscriptYOffset, + face->size->metrics.y_scale); + // some fonts have the incorrect sign. + val = (val < 0) ? -val : val; + mMetrics.subscriptOffset = PR_MAX(1, val); + } else { + mMetrics.subscriptOffset = mMetrics.xHeight; + } + + mMetrics.strikeoutOffset = mMetrics.xHeight / 2.0; + mMetrics.strikeoutSize = mMetrics.underlineSize; + + XftUnlockFace(xftFont); +#else /* pango_cairo case; try to get all the metrics from pango itself */ PangoFont *font = GetPangoFont(); // RealizeFont is called here. @@ -432,8 +627,7 @@ gfxPangoFont::GetMetrics() mMetrics.emAscent = mMetrics.maxAscent * mMetrics.emHeight / lineHeight; mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent; - // XXX should we move this down, get max-advance from FT_Face? - mMetrics.maxAdvance = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE; + mMetrics.maxAdvance = pango_font_metrics_get_approximate_char_width(pfm) / FLOAT_PANGO_SCALE; // XXX gfxSize isz, lsz; GetCharSize(' ', isz, lsz, &mSpaceGlyph); @@ -449,43 +643,14 @@ gfxPangoFont::GetMetrics() mMetrics.strikeoutOffset = pango_font_metrics_get_strikethrough_position(pfm) / FLOAT_PANGO_SCALE; mMetrics.strikeoutSize = pango_font_metrics_get_strikethrough_thickness(pfm) / FLOAT_PANGO_SCALE; - FT_Face face = NULL; - if (PANGO_IS_FC_FONT (font)) - face = pango_fc_font_lock_face (PANGO_FC_FONT (font)); - - if (face) { - - float val; - - TT_OS2 *os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2); - - if (os2 && os2->ySuperscriptYOffset) { - val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset, - face->size->metrics.y_scale); - mMetrics.superscriptOffset = PR_MAX(1, val); - } else { - mMetrics.superscriptOffset = mMetrics.xHeight; - } - - // mSubscriptOffset - if (os2 && os2->ySubscriptYOffset) { - val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySubscriptYOffset, - face->size->metrics.y_scale); - // some fonts have the incorrect sign. - val = (val < 0) ? -val : val; - mMetrics.subscriptOffset = PR_MAX(1, val); - } else { - mMetrics.subscriptOffset = mMetrics.xHeight; - } - - } else { - - mMetrics.superscriptOffset = mMetrics.xHeight; - mMetrics.subscriptOffset = mMetrics.xHeight; - } - + // these are specified by the so-called OS2 SFNT info, but + // pango doesn't expose this to us. This really sucks, + // so we just assume it's the xHeight + mMetrics.superscriptOffset = mMetrics.xHeight; + mMetrics.subscriptOffset = mMetrics.xHeight; pango_font_metrics_unref (pfm); +#endif #if 0 fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(mName).get()); @@ -500,13 +665,66 @@ gfxPangoFont::GetMetrics() return mMetrics; } +// XXX we should replace this to |pango_is_zero_width| after we don't support pre pango 1.10 +static PRBool MOZ_pango_is_zero_width(PRUint32 aChar) +{ + if (aChar == 0x00AD) + return PR_TRUE; + if (aChar < 0x200B) + return PR_FALSE; + if (aChar <= 0x200F || aChar == 0x2028) + return PR_TRUE; + if (aChar < 0x202A) + return PR_FALSE; + if (aChar <= 0x202E) + return PR_TRUE; + if (aChar < 0x2060) + return PR_FALSE; + if (aChar <= 0x2063 || aChar == 0xFEFF) + return PR_TRUE; + return PR_FALSE; +} +PRBool +gfxPangoFont::HasGlyph(PRUint32 aChar) +{ + // Ensure that null character should be missing. + if (aChar == 0) + return PR_FALSE; + + if (MOZ_pango_is_zero_width(aChar)) + return PR_TRUE; + + 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; +} + PRUint32 gfxPangoFont::GetGlyph(const PRUint32 aChar) { // Ensure that null character should be missing. if (aChar == 0) return 0; - return pango_fc_font_get_glyph(PANGO_FC_FONT(GetPangoFont()), aChar); + RealizePangoFont(); + return pango_fc_font_get_glyph(PANGO_FC_FONT(mPangoFont), aChar); } nsString @@ -514,9 +732,16 @@ gfxPangoFont::GetUniqueName() { PangoFont *font = GetPangoFont(); PangoFontDescription *desc = pango_font_describe(font); - pango_font_description_unset_fields (desc, PANGO_FONT_MASK_SIZE); char *str = pango_font_description_to_string(desc); - pango_font_description_free (desc); + + // chop off the trailing size, e.g. "Albany AMT 15.359375" -> "Albany AMT" + PRUint32 end = strlen(str); + while (end > 0) { + --end; + if (str[end] == ' ') + break; + } + str[end] = 0; nsString result; CopyUTF8toUTF16(str, result); @@ -524,6 +749,32 @@ gfxPangoFont::GetUniqueName() return result; } +static const char *sCJKLangGroup[] = { + "ja", + "ko", + "zh-CN", + "zh-HK", + "zh-TW" +}; + +#define COUNT_OF_CJK_LANG_GROUP 5 +#define CJK_LANG_JA sCJKLangGroup[0] +#define CJK_LANG_KO sCJKLangGroup[1] +#define CJK_LANG_ZH_CN sCJKLangGroup[2] +#define CJK_LANG_ZH_HK sCJKLangGroup[3] +#define CJK_LANG_ZH_TW sCJKLangGroup[4] + +static PRInt32 +GetCJKLangGroupIndex(const char *aLangGroup) +{ + PRInt32 i; + for (i = 0; i < COUNT_OF_CJK_LANG_GROUP; i++) { + if (!PL_strcasecmp(aLangGroup, sCJKLangGroup[i])) + return i; + } + return -1; +} + /** ** gfxTextRun * @@ -537,6 +788,19 @@ gfxPangoFont::GetUniqueName() * **/ +/** + * We use this to append an LTR or RTL Override character to the start of the + * string. This forces Pango to honour our direction even if there are neutral characters + * in the string. + */ +static PRInt32 AppendDirectionalIndicatorUTF8(PRBool aIsRTL, nsACString& aString) +{ + static const PRUnichar overrides[2][2] = + { { 0x202d, 0 }, { 0x202e, 0 }}; // LRO, RLO + AppendUTF16toUTF8(overrides[aIsRTL], aString); + return 3; // both overrides map to 3 bytes in UTF8 +} + gfxTextRun * gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength, const Parameters *aParams, PRUint32 aFlags) @@ -548,33 +812,34 @@ gfxPangoFontGroup::MakeTextRun(const PRUint8 *aString, PRUint32 aLength, PRBool isRTL = run->IsRightToLeft(); if ((aFlags & TEXT_IS_ASCII) && !isRTL) { + // We don't need to send an override character here, the characters must be all + // LTR const gchar *utf8Chars = reinterpret_cast(aString); - InitTextRun(run, utf8Chars, aLength, PR_TRUE); + InitTextRun(run, utf8Chars, aLength, 0, PR_TRUE); } else { - // this is really gross... const char *chars = reinterpret_cast(aString); + // XXX this could be more efficient. + // Although chars in not necessarily ASCII (as it may point to the low + // bytes of any UCS-2 characters < 256), NS_ConvertASCIItoUTF16 seems + // to DTRT. NS_ConvertASCIItoUTF16 unicodeString(chars, aLength); - NS_ConvertUTF16toUTF8 utf8String(unicodeString); - InitTextRun(run, utf8String.get(), utf8String.Length(), PR_TRUE); + nsCAutoString utf8; + PRInt32 headerLen = AppendDirectionalIndicatorUTF8(isRTL, utf8); + AppendUTF16toUTF8(unicodeString, utf8); + InitTextRun(run, utf8.get(), utf8.Length(), headerLen, PR_TRUE); } - return run; } -#if defined(ENABLE_FAST_PATH_8BIT) -PRBool -gfxPangoFontGroup::CanTakeFastPath(PRUint32 aFlags) +static PRBool +CanTakeFastPath(PRUint32 aFlags) { - if (!PANGO_IS_FC_FONT (GetFontAt(0)->GetPangoFont ())) - return FALSE; - // Can take fast path only if OPTIMIZE_SPEED is set and IS_RTL isn't // We need to always use Pango for RTL text, in case glyph mirroring is required return (aFlags & (gfxTextRunFactory::TEXT_OPTIMIZE_SPEED | gfxTextRunFactory::TEXT_IS_RTL)) == gfxTextRunFactory::TEXT_OPTIMIZE_SPEED; } -#endif gfxTextRun * gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength, @@ -586,10 +851,11 @@ gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength, run->RecordSurrogates(aString); - NS_ConvertUTF16toUTF8 utf8(aString, aLength); + nsCAutoString utf8; + PRInt32 headerLen = AppendDirectionalIndicatorUTF8(run->IsRightToLeft(), utf8); + AppendUTF16toUTF8(Substring(aString, aString + aLength), utf8); PRBool is8Bit = PR_FALSE; - -#if defined(ENABLE_FAST_PATH_8BIT) +#if defined(ENABLE_XFT_FAST_PATH_8BIT) if (CanTakeFastPath(aFlags)) { PRUint32 allBits = 0; PRUint32 i; @@ -599,20 +865,21 @@ gfxPangoFontGroup::MakeTextRun(const PRUnichar *aString, PRUint32 aLength, is8Bit = (allBits & 0xFF00) == 0; } #endif - InitTextRun(run, utf8.get(), utf8.Length(), is8Bit); + InitTextRun(run, utf8.get(), utf8.Length(), headerLen, is8Bit); return run; } void gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text, - PRUint32 aUTF8Length, PRBool aTake8BitPath) + PRUint32 aUTF8Length, PRUint32 aUTF8HeaderLength, + PRBool aTake8BitPath) { -#if defined(ENABLE_FAST_PATH_ALWAYS) - CreateGlyphRunsFast(aTextRun, aUTF8Text, aUTF8Length); +#if defined(ENABLE_XFT_FAST_PATH_ALWAYS) + CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); #else -#if defined(ENABLE_FAST_PATH_8BIT) +#if defined(ENABLE_XFT_FAST_PATH_8BIT) if (aTake8BitPath && CanTakeFastPath(aTextRun->GetFlags())) { - CreateGlyphRunsFast(aTextRun, aUTF8Text, aUTF8Length); + CreateGlyphRunsXft(aTextRun, aUTF8Text + aUTF8HeaderLength, aUTF8Length - aUTF8HeaderLength); return; } #endif @@ -621,20 +888,13 @@ gfxPangoFontGroup::InitTextRun(gfxTextRun *aTextRun, const gchar *aUTF8Text, (aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR)); - CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length); + CreateGlyphRunsItemizing(aTextRun, aUTF8Text, aUTF8Length, aUTF8HeaderLength); #endif } static cairo_scaled_font_t* CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont) { -#if 0 -//XXX enalbe this #if defined(PANGO_VERSION_CHECK) && PANGO_VERSION_CHECK(1,17,5) - // Lets just use pango_cairo_font_get_scaled_font() for now. it's only - // available in pango 1.17.x though :( - return cairo_scaled_font_reference (pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (aPangoFont))); -#else - // XXX is this safe really? We should probably check the font type or something. // XXX does this really create the same font that Pango used for measurement? // We probably need to work harder here. We should pay particular attention @@ -653,7 +913,6 @@ CreateScaledFont(cairo_t *aCR, cairo_matrix_t *aCTM, PangoFont *aPangoFont) cairo_font_options_destroy(fontOptions); cairo_font_face_destroy(face); return scaledFont; -#endif } PRBool @@ -692,8 +951,6 @@ SetupClusterBoundaries(gfxTextRun* aTextRun, const gchar *aUTF8, PRUint32 aUTF8L if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_IS_8BIT) { // 8-bit text doesn't have clusters. // XXX is this true in all languages??? - // behdad: don't think so. Czech for example IIRC has a - // 'ch' grapheme. return; } @@ -938,9 +1195,8 @@ gfxPangoFontGroup::SetGlyphs(gfxTextRun *aTextRun, gfxPangoFont *aFont, gunichar ch = g_utf8_get_char(clusterUTF8); do { // Does pango ever provide more than one glyph in the cluster // if there is a missing glyph? - // behdad: yes if (IS_MISSING_GLYPH(glyphs[glyphIndex].glyph)) { - if (pango_is_zero_width(ch)) { + if (MOZ_pango_is_zero_width(ch)) { // the zero width characters returns empty glyph ID at shaping, // we should override it if the font has the character. glyphs[glyphIndex].glyph = aFont->GetGlyph(' '); @@ -1000,15 +1256,15 @@ gfxPangoFontGroup::SetMissingGlyphs(gfxTextRun *aTextRun, return NS_OK; } -#if defined(ENABLE_FAST_PATH_8BIT) || defined(ENABLE_FAST_PATH_ALWAYS) +#if defined(ENABLE_XFT_FAST_PATH_8BIT) || defined(ENABLE_XFT_FAST_PATH_ALWAYS) void -gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun, - const gchar *aUTF8, PRUint32 aUTF8Length) +gfxPangoFontGroup::CreateGlyphRunsXft(gfxTextRun *aTextRun, + const gchar *aUTF8, PRUint32 aUTF8Length) { const gchar *p = aUTF8; + Display *dpy = GDK_DISPLAY(); gfxPangoFont *font = GetFontAt(0); - PangoFont *pangofont = font->GetPangoFont(); - PangoFcFont *fcfont = PANGO_FC_FONT (pangofont); + XftFont *xfont = font->GetXftFont(); PRUint32 utf16Offset = 0; gfxTextRun::CompressedGlyph g; const PRUint32 appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); @@ -1029,11 +1285,14 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun, aTextRun->SetMissingGlyph(utf16Offset, 0); } else { NS_ASSERTION(!IsInvalidChar(ch), "Invalid char detected"); - FT_UInt glyph = pango_fc_font_get_glyph (fcfont, ch); - PangoRectangle rect; - pango_font_get_glyph_extents (pangofont, glyph, NULL, &rect); + FT_UInt glyph = XftCharIndex(dpy, xfont, ch); + XGlyphInfo info; + XftGlyphExtents(dpy, xfont, &glyph, 1, &info); + if (info.yOff > 0) { + NS_WARNING("vertical offsets not supported"); + } - PRInt32 advance = PANGO_PIXELS (rect.width * appUnitsPerDevUnit); + PRInt32 advance = info.xOff*appUnitsPerDevUnit; if (advance >= 0 && gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) && gfxTextRun::CompressedGlyph::IsSimpleGlyphID(glyph)) { @@ -1067,39 +1326,288 @@ gfxPangoFontGroup::CreateGlyphRunsFast(gfxTextRun *aTextRun, } #endif -void -gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun, - const gchar *aUTF8, PRUint32 aUTF8Length) +class FontSelector { - - PangoContext *context = gdk_pango_context_get (); - - PangoFontDescription *fontDesc = pango_font_description_new(); - - // these should be FontEntries or something similar rather than gfxPangoFonts... - nsString fontList; - - for (PRUint32 i = 0; i < mFonts.Length(); i++) { - fontList.Append(mFonts[i]->GetName()); - fontList.Append(NS_LITERAL_STRING(", ")); +public: + FontSelector(const gchar *aString, PRInt32 aLength, + gfxPangoFontGroup *aGroup, gfxTextRun *aTextRun, + PangoItem *aItem, PRUint32 aUTF16Offset, PRPackedBool aIsRTL) : + mItem(aItem), + mGroup(aGroup), mTextRun(aTextRun), mString(aString), + mFontIndex(0), mLength(aLength), mUTF16Offset(aUTF16Offset), + mTriedPrefFonts(0), mTriedOtherFonts(0), mIsRTL(aIsRTL) + { + for (PRUint32 i = 0; i < mGroup->FontListLength(); ++i) + mFonts.AppendElement(mGroup->GetFontAt(i)); + mSpaceWidth = NS_lround(mGroup->GetFontAt(0)->GetMetrics().spaceWidth * FLOAT_PANGO_SCALE); + } + + nsresult Run() + { + return InitSegments(mString, mLength); } - PangoLanguage *lang = GetPangoLanguage(GetStyle()->langGroup); + PRUint32 GetUTF16Offset() { return mUTF16Offset; } - pango_font_description_set_family(fontDesc, NS_ConvertUTF16toUTF8(fontList).get()); - pango_font_description_set_absolute_size(fontDesc, GetStyle()->size * PANGO_SCALE); - pango_font_description_set_style(fontDesc, ThebesStyleToPangoStyle(GetStyle())); - pango_font_description_set_weight(fontDesc, ThebesStyleToPangoWeight(GetStyle())); + static PRBool ExistsFont(FontSelector *aFs, + const nsAString &aName) { + PRUint32 len = aFs->mFonts.Length(); + for (PRUint32 i = 0; i < len; ++i) { + if (aName.Equals(aFs->mFonts[i]->GetName())) + return PR_TRUE; + } + return PR_FALSE; + } - pango_context_set_font_description(context, fontDesc); + static PRBool AddFontCallback(const nsAString &aName, + const nsACString &aGenericName, + void *closure) { + if (aName.IsEmpty()) + return PR_TRUE; - // we should set this to null if we don't have a text language from the page... - // except that we almost always have something... - pango_context_set_language(context, lang); + FontSelector *fs = static_cast(closure); - PangoDirection dir = aTextRun->IsRightToLeft() ? PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR; - GList *items = pango_itemize_with_base_dir(context, dir, aUTF8, 0, aUTF8Length, nsnull, nsnull); + // XXX do something better than this to remove dups + if (ExistsFont(fs, aName)) + return PR_TRUE; + nsRefPtr font = GetOrMakeFont(aName, fs->mGroup->GetStyle()); + if (font) { + fs->mFonts.AppendElement(font); + } + + return PR_TRUE; + } + +private: + PangoItem *mItem; + + nsTArray< nsRefPtr > mFonts; + + gfxPangoFontGroup *mGroup; + gfxTextRun *mTextRun; + const char *mString; // UTF-8 + PRUint32 mFontIndex; + PRInt32 mLength; + PRUint32 mUTF16Offset; + PRUint32 mSpaceWidth; + + PRPackedBool mTriedPrefFonts; + PRPackedBool mTriedOtherFonts; + PRPackedBool mIsRTL; + + nsresult InitSegments(const gchar *aUTF8, PRUint32 aLength) { + if (aLength == 0) + return NS_OK; + const gchar *start = aUTF8; + const gchar *last = start + aLength; + +RetryNextFont: + nsRefPtr font = GetNextFont(); + + // If we cannot found the font that has the current character glyph, + // we should return default font's missing data. + 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; + } + + 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 rv; + } + 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() { +TRY_AGAIN_HOPE_FOR_THE_BEST_2: + if (mFontIndex < mFonts.Length()) { + return mFonts[mFontIndex++]; + } else if (!mTriedPrefFonts) { + mTriedPrefFonts = PR_TRUE; + nsCAutoString mozLang; + GetMozLanguage(mItem->analysis.language, mozLang); + if (!mozLang.IsEmpty()) { + PRInt32 index = GetCJKLangGroupIndex(mozLang.get()); + if (index >= 0) + AppendCJKPrefFonts(); + else + AppendPrefFonts(mozLang.get()); + } else { + NS_ConvertUTF8toUTF16 str(mString); + PRBool appenedCJKFonts = PR_FALSE; + for (PRUint32 i = 0; i < str.Length(); ++i) { + const PRUnichar ch = str[i]; + PRUint32 unicodeRange = FindCharUnicodeRange(ch); + + /* special case CJK */ + if (unicodeRange == kRangeSetCJK) { + if (!appenedCJKFonts) { + appenedCJKFonts = PR_TRUE; + AppendCJKPrefFonts(); + } + } else { + const char *langGroup = + LangGroupFromUnicodeRange(unicodeRange); + if (langGroup) + AppendPrefFonts(langGroup); + } + } + } + goto TRY_AGAIN_HOPE_FOR_THE_BEST_2; + } else if (!mTriedOtherFonts) { + mTriedOtherFonts = PR_TRUE; + // XXX we should try by all system fonts + goto TRY_AGAIN_HOPE_FOR_THE_BEST_2; + } + return nsnull; + } + + void AppendPrefFonts(const char *aLangGroup) { + NS_ASSERTION(aLangGroup, "aLangGroup is null"); + gfxPlatform *platform = gfxPlatform::GetPlatform(); + nsString fonts; + platform->GetPrefFonts(aLangGroup, fonts); + if (fonts.IsEmpty()) + return; + gfxFontGroup::ForEachFont(fonts, nsDependentCString(aLangGroup), + FontSelector::AddFontCallback, this); + return; + } + + void AppendCJKPrefFonts() { + nsCOMPtr prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (!prefs) + return; + + nsCOMPtr prefBranch; + prefs->GetBranch(0, getter_AddRefs(prefBranch)); + if (!prefBranch) + return; + + // Add the accept languages. + nsXPIDLCString list; + nsresult rv = prefBranch->GetCharPref("intl.accept_languages", + getter_Copies(list)); + if (NS_SUCCEEDED(rv) && !list.IsEmpty()) { + const char kComma = ','; + const char *p, *p_end; + list.BeginReading(p); + list.EndReading(p_end); + while (p < p_end) { + while (nsCRT::IsAsciiSpace(*p)) { + if (++p == p_end) + break; + } + if (p == p_end) + break; + const char *start = p; + while (++p != p_end && *p != kComma) + /* nothing */ ; + nsCAutoString lang(Substring(start, p)); + lang.CompressWhitespace(PR_FALSE, PR_TRUE); + PRInt32 index = GetCJKLangGroupIndex(lang.get()); + if (index >= 0) + AppendPrefFonts(sCJKLangGroup[index]); + p++; + } + } + + // XXX I think that we should append system locale here if it is CJK. + + // last resort... + AppendPrefFonts(CJK_LANG_JA); + AppendPrefFonts(CJK_LANG_KO); + AppendPrefFonts(CJK_LANG_ZH_CN); + AppendPrefFonts(CJK_LANG_ZH_HK); + AppendPrefFonts(CJK_LANG_ZH_TW); + } +}; + +void +gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun, + const gchar *aUTF8, PRUint32 aUTF8Length, + PRUint32 aUTF8HeaderLen) +{ + GList *items = pango_itemize(GetFontAt(0)->GetPangoContext(), aUTF8, 0, + aUTF8Length, nsnull, nsnull); + PRUint32 utf16Offset = 0; PRBool isRTL = aTextRun->IsRightToLeft(); GList *pos = items; @@ -1109,51 +1617,29 @@ gfxPangoFontGroup::CreateGlyphRunsItemizing(gfxTextRun *aTextRun, PRUint32 offset = item->offset; PRUint32 length = item->length; - - // need to append glyph runs here. - PangoGlyphString *glyphString = pango_glyph_string_new(); - if (!glyphString) - return; // OOM - - pango_shape(aUTF8 + offset, length, &item->analysis, glyphString); - - /* look up the gfxPangoFont from the PangoFont */ - // XXX we need a function to do this.. until then do this - // behdad: use g_object_[sg]et_qdata() for it. - PangoFontDescription *d = pango_font_describe(item->analysis.font); - nsRefPtr font = GetOrMakeFont(NS_ConvertUTF8toUTF16(pango_font_description_get_family(d)), GetStyle()); - - //printf("Using %s\n", pango_font_description_get_family(d)); - - pango_font_description_free(d); - SetupClusterBoundaries(aTextRun, aUTF8 + offset, length, utf16Offset, &item->analysis); - - nsresult rv = aTextRun->AddGlyphRun(font, utf16Offset, PR_TRUE); - if (NS_FAILED(rv)) { - NS_ERROR("AddGlyphRun Failed"); - pango_glyph_string_free(glyphString); - return; + if (offset < aUTF8HeaderLen) { + if (offset + length <= aUTF8HeaderLen) { + pango_item_free(item); + continue; + } + length -= aUTF8HeaderLen - offset; + offset = aUTF8HeaderLen; } - - PRUint32 spaceWidth = NS_lround(font->GetMetrics().spaceWidth * FLOAT_PANGO_SCALE); - - rv = SetGlyphs(aTextRun, font, aUTF8 + offset, length, &utf16Offset, glyphString, spaceWidth, PR_FALSE); - - pango_glyph_string_free(glyphString); + + SetupClusterBoundaries(aTextRun, aUTF8 + offset, length, utf16Offset, &item->analysis); + FontSelector fs(aUTF8 + offset, length, this, aTextRun, item, utf16Offset, isRTL); + fs.Run(); // appends GlyphRuns + utf16Offset = fs.GetUTF16Offset(); + pango_item_free(item); } + NS_ASSERTION(utf16Offset == aTextRun->GetLength(), + "Didn't resolve all characters"); + if (items) g_list_free(items); - - pango_font_description_free(fontDesc); - - g_object_unref(context); - - aTextRun->SortGlyphRuns(); } - - /** ** language group helpers **/ @@ -1222,7 +1708,7 @@ GetPangoLanguage(const nsACString& cname) else if (langGroup->PangoLang) return pango_language_from_string(langGroup->PangoLang); - return nsnull; + return pango_language_from_string("en"); } // See pango-script-lang-table.h in pango. @@ -1406,7 +1892,38 @@ static const MozPangoLangGroup PangoAllLangGroup[] = { { "x-western", "zu" }, }; -#define NUM_PANGO_ALL_LANG_GROUPS (G_N_ELEMENTS (PangoAllLangGroup)) +#define NUM_PANGO_ALL_LANG_GROUPS (sizeof (PangoAllLangGroup) / \ + sizeof (PangoAllLangGroup[0])) + +/* static */ +void +GetMozLanguage(const PangoLanguage *aLang, nsACString &aMozLang) +{ + aMozLang.Truncate(); + if (!aLang) + return; + + nsCAutoString lang(pango_language_to_string(aLang)); + if (lang.IsEmpty() || lang.Equals("xx")) + return; + + while (1) { + for (PRUint32 i = 0; i < NUM_PANGO_ALL_LANG_GROUPS; ++i) { + if (lang.Equals(PangoAllLangGroup[i].PangoLang)) { + if (PangoAllLangGroup[i].mozLangGroup) + aMozLang.Assign(PangoAllLangGroup[i].mozLangGroup); + return; + } + } + + PRInt32 hyphen = lang.FindChar('-'); + if (hyphen != kNotFound) { + lang.Cut(hyphen, lang.Length()); + continue; + } + break; + } +} gfxPangoFontCache::gfxPangoFontCache() { @@ -1440,3 +1957,39 @@ gfxPangoFontCache::Get(const PangoFontDescription *aFontDesc) 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; +} diff --git a/gfx/thebes/src/gfxPlatformGtk.cpp b/gfx/thebes/src/gfxPlatformGtk.cpp index 826f997094e..4d5f4e06e3d 100644 --- a/gfx/thebes/src/gfxPlatformGtk.cpp +++ b/gfx/thebes/src/gfxPlatformGtk.cpp @@ -60,6 +60,12 @@ #include +#ifndef THEBES_USE_PANGO_CAIRO +#include +#endif // THEBES_USE_PANGO_CAIRO + +#include + #include "nsMathUtils.h" #include "lcms.h" @@ -83,8 +89,6 @@ gfxPlatformGtk::gfxPlatformGtk() #endif if (!sFontconfigUtils) sFontconfigUtils = gfxFontconfigUtils::GetFontconfigUtils(); - - InitDPI(); } gfxPlatformGtk::~gfxPlatformGtk() @@ -94,6 +98,10 @@ gfxPlatformGtk::~gfxPlatformGtk() gfxPangoFont::Shutdown(); +#ifndef THEBES_USE_PANGO_CAIRO + pango_xft_shutdown_display(GDK_DISPLAY(), 0); +#endif + #if 0 // It would be nice to do this (although it might need to be after // the cairo shutdown that happens in ~gfxPlatform). It even looks @@ -270,17 +278,93 @@ gfxPlatformGtk::CreateFontGroup(const nsAString &aFamilies, return new gfxPangoFontGroup(aFamilies, aStyle); } +static PRInt32 +GetXftDPI() +{ + char *val = XGetDefault(GDK_DISPLAY(), "Xft", "dpi"); + if (val) { + char *e; + double d = strtod(val, &e); + + if (e != val) + return NS_lround(d); + } + + return -1; +} + +static PRInt32 +GetDPIFromPangoFont() +{ +#ifndef THEBES_USE_PANGO_CAIRO + PangoContext* ctx = pango_xft_get_context(GDK_DISPLAY(), 0); + gdk_pango_context_set_colormap(ctx, gdk_rgb_get_cmap()); +#else + PangoContext* ctx = + pango_cairo_font_map_create_context( + PANGO_CAIRO_FONT_MAP(pango_cairo_font_map_get_default())); +#endif + + if (!ctx) { + return 0; + } + + double dblDPI = 0.0f; + GList *items = nsnull; + PangoItem *item = nsnull; + PangoFcFont *fcfont = nsnull; + + PangoAttrList *al = pango_attr_list_new(); + + if (!al) { + goto cleanup; + } + + // Just using the string "a" because we need _some_ text. + items = pango_itemize(ctx, "a", 0, 1, al, NULL); + + if (!items) { + goto cleanup; + } + + item = (PangoItem*)items->data; + + if (!item) { + goto cleanup; + } + + fcfont = PANGO_FC_FONT(item->analysis.font); + + if (!fcfont) { + goto cleanup; + } + + FcPatternGetDouble(fcfont->font_pattern, FC_DPI, 0, &dblDPI); + + cleanup: + if (al) + pango_attr_list_unref(al); + if (item) + pango_item_free(item); + if (items) + g_list_free(items); + if (ctx) + g_object_unref(ctx); + + return NS_lround(dblDPI); +} + /* static */ void gfxPlatformGtk::InitDPI() { - PangoContext *context = gdk_pango_context_get (); - sDPI = pango_cairo_context_get_resolution (context); - g_object_unref (context); - + sDPI = GetXftDPI(); if (sDPI <= 0) { - // Fall back to something sane - sDPI = 96; + sDPI = GetDPIFromPangoFont(); + if (sDPI <= 0) { + // Fall back to something sane + sDPI = 96; + } } }