--- mozilla.back/gfx/src/gtk/nsFontMetricsPango.cpp.orig 2007-06-28 14:44:31.000000000 +0200 +++ mozilla.back/gfx/src/gtk/nsFontMetricsPango.cpp 2007-06-28 15:48:04.000000000 +0200 @@ -21,6 +21,8 @@ * are Copyright (C) 2004 the Initial Developer. All Rights Reserved. * * Contributor(s): + * Christopher Blizzard + * Behdad Esfahbod * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -36,6 +38,10 @@ * * ***** END LICENSE BLOCK ***** */ +#define PANGO_ENABLE_BACKEND + +#include "nsFontMetricsPango.h" + #include #include "nsFont.h" #include "nsIDeviceContext.h" @@ -43,27 +49,37 @@ #include "nsIPref.h" #include "nsServiceManagerUtils.h" -#define PANGO_ENABLE_BACKEND -#define PANGO_ENABLE_ENGINE - -#include "nsFontMetricsPango.h" -#include "nsRenderingContextGTK.h" -#include "nsDeviceContextGTK.h" #include "nsFontConfigUtils.h" #include "nsUnicharUtils.h" #include "nsQuickSort.h" #include "nsFontConfigUtils.h" +#include "mozilla-decoder.h" + +#define FORCE_PR_LOG +#include "prlog.h" + #include +#include + +#include +#include + +#ifdef PSPANGO +#include +#include "nsRenderingContextPS.h" +#include "nsDeviceContextPS.h" +#include "nsType1.h" +#else #include #include -#include +#include "nsRenderingContextGTK.h" +#include "nsDeviceContextGTK.h" +#endif + -#include "mozilla-decoder.h" -#define FORCE_PR_LOG -#include "prlog.h" // Globals @@ -108,6 +124,49 @@ static nsresult EnumFontsPango (nsI PRUint32* aCount, PRUnichar*** aResult); static int CompareFontNames (const void* aArg1, const void* aArg2, void* aClosure); +static void utf16_to_utf8 (const PRUnichar* aString, PRUint32 aLength, + char *&text, gint &text_len); + +#ifdef PSPANGO +static void +default_substitute (FcPattern *pattern, + gpointer data) +{ + FcPatternDel (pattern, FC_HINTING); + FcPatternAddBool (pattern, FC_HINTING, 0); +} +#endif + +static PangoFontMap * +get_fontmap (void) +{ + static PangoFontMap *fontmap = NULL; + + if (!fontmap) { +#ifdef PSPANGO + fontmap = pango_ft2_font_map_new (); + pango_ft2_font_map_set_resolution ((PangoFT2FontMap *)fontmap, 72., 72.); + pango_ft2_font_map_set_default_substitute ((PangoFT2FontMap *)fontmap, default_substitute, NULL, NULL); +#else + PangoContext* context = gdk_pango_context_get (); + fontmap = pango_context_get_font_map (context); + g_object_unref (context); +#endif + } + + return fontmap; +} + +static PangoContext * +get_context (void) +{ +#ifdef PSPANGO + return pango_ft2_font_map_create_context ((PangoFT2FontMap *) get_fontmap ()); +#else + return gdk_pango_context_get(); +#endif +} + nsFontMetricsPango::nsFontMetricsPango() { @@ -169,15 +228,21 @@ mLangGroup = aLangGroup; // Hang on to the device context +#ifdef PSPANGO + mDeviceContext = (nsDeviceContextPS *)aContext; +#else mDeviceContext = aContext; - +#endif + mPointSize = NSTwipsToFloatPoints(mFont.size); +#ifndef PSPANGO // Make sure to clamp the pixel size to something reasonable so we // don't make the X server blow up. nscoord screenPixels = gdk_screen_height(); mPointSize = PR_MIN((screenPixels - 1) * FONT_MAX_FONT_SCALE, mPointSize); mPointSize = PR_MIN(2000, mPointSize); +#endif // enumerate over the font names passed in mFont.EnumerateFamilies(nsFontMetricsPango::EnumFontCallback, this); @@ -329,7 +394,7 @@ nsFontMetricsPango::CacheFontMetrics(voi // mPangoSpaceWidth PangoLayout *layout = pango_layout_new(mPangoContext); - pango_layout_set_text(layout, " ", 1); + pango_layout_set_text(layout, " ", -1); int pswidth, psheight; pango_layout_get_size(layout, &pswidth, &psheight); mPangoSpaceWidth = pswidth; @@ -337,14 +402,14 @@ nsFontMetricsPango::CacheFontMetrics(voi // mSpaceWidth (width of a space) nscoord tmpWidth; - GetWidth(" ", 1, tmpWidth, NULL); + GetWidth(" ", 1, tmpWidth CONTEXT_ARG_NULL); mSpaceWidth = tmpWidth; // mAveCharWidth (width of an 'average' char) // XftTextExtents16(GDK_DISPLAY(), xftFont, &xUnichar, 1, &extents); //rawWidth = extents.width; //mAveCharWidth = NSToCoordRound(rawWidth * f); - GetWidth("x", 1, tmpWidth, NULL); + GetWidth("x", 1, tmpWidth CONTEXT_ARG_NULL); mAveCharWidth = tmpWidth; // mXHeight (height of an 'x' character) @@ -460,130 +525,96 @@ nsFontMetricsPango::GetFontHandle(nsFont // nsIFontMetricsPango impl -nsresult -nsFontMetricsPango::GetWidth(const char* aString, PRUint32 aLength, - nscoord& aWidth, - nsRenderingContextGTK *aContext) +#ifdef PSPANGO +NS_IMETHODIMP +nsFontMetricsPSPango::GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength) { - PangoLayout *layout = pango_layout_new(mPangoContext); - - pango_layout_set_text(layout, aString, aLength); + return GetWidth (String, (PRUint32) aLength, aWidth CONTEXT_ARG_NULL); +} - if (mPangoSpaceWidth) - FixupSpaceWidths(layout, aString); +NS_IMETHODIMP +nsFontMetricsPSPango::GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength) +{ + return GetWidth (aString, (PRUint32)aLength, aWidth, NULL CONTEXT_ARG_NULL); +} +#endif +nsresult +nsFontMetricsPango::GetWidth(const char* aString, PRUint32 aLength, + nscoord& aWidth + CONTEXT_ARG_DEF) +{ int width, height; - + PangoLayout *layout = GetLayout(aString, aLength); pango_layout_get_size(layout, &width, &height); - g_object_unref(layout); - float f; - f = mDeviceContext->DevUnitsToAppUnits(); + float f = mDeviceContext->DevUnitsToAppUnits(); aWidth = NSToCoordRound(width * f / PANGO_SCALE); - // printf("GetWidth (char *) %d\n", aWidth); - return NS_OK; } nsresult nsFontMetricsPango::GetWidth(const PRUnichar* aString, PRUint32 aLength, - nscoord& aWidth, PRInt32 *aFontID, - nsRenderingContextGTK *aContext) + nscoord& aWidth, PRInt32 *aFontID + CONTEXT_ARG_DEF) { - nsresult rv = NS_OK; - PangoLayout *layout = pango_layout_new(mPangoContext); - - gchar *text = g_utf16_to_utf8(aString, aLength, - NULL, NULL, NULL); - - if (!text) { - aWidth = 0; -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); - DUMP_PRUNICHAR(aString, aLength) -#endif - rv = NS_ERROR_FAILURE; - goto loser; - } - gint width, height; - - pango_layout_set_text(layout, text, strlen(text)); - FixupSpaceWidths(layout, text); + PangoLayout *layout = GetLayout(aString, aLength); pango_layout_get_size(layout, &width, &height); + g_object_unref(layout); - float f; - f = mDeviceContext->DevUnitsToAppUnits(); + float f = mDeviceContext->DevUnitsToAppUnits(); aWidth = NSToCoordRound(width * f / PANGO_SCALE); - // printf("GetWidth %d\n", aWidth); - - loser: - g_free(text); - g_object_unref(layout); - - return rv; + return NS_OK; } nsresult -nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString, +nsFontMetricsPango::GetTextDimensions(const char* aString, PRUint32 aLength, - nsTextDimensions& aDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext) + nsTextDimensions& aDimensions + CONTEXT_ARG_DEF) { - nsresult rv = NS_OK; - - PangoLayout *layout = pango_layout_new(mPangoContext); + PangoLayout *layout = GetLayout(aString, aLength); + PangoLayoutLine *line = pango_layout_get_line(layout, 0); - gchar *text = g_utf16_to_utf8(aString, aLength, - NULL, NULL, NULL); - - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetTextDimensions invalid unicode to follow"); - DUMP_PRUNICHAR(aString, aLength) -#endif - aDimensions.width = 0; - aDimensions.ascent = 0; - aDimensions.descent = 0; - - rv = NS_ERROR_FAILURE; - goto loser; - } - + PangoRectangle logical; + pango_layout_line_get_extents(line, NULL, &logical); + g_object_unref(layout); - pango_layout_set_text(layout, text, strlen(text)); - FixupSpaceWidths(layout, text); + float P2T = mDeviceContext->DevUnitsToAppUnits(); - // Get the logical extents - PangoLayoutLine *line; - if (pango_layout_get_line_count(layout) != 1) { - printf("Warning: more than one line!\n"); - } - line = pango_layout_get_line(layout, 0); + aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(logical) * P2T / PANGO_SCALE); + aDimensions.descent = NSToCoordRound(PANGO_DESCENT(logical) * P2T / PANGO_SCALE); + aDimensions.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE); - PangoRectangle rect; - pango_layout_line_get_extents(line, NULL, &rect); + return NS_OK; +} - float P2T; - P2T = mDeviceContext->DevUnitsToAppUnits(); +nsresult +nsFontMetricsPango::GetTextDimensions(const PRUnichar* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions, + PRInt32* aFontID + CONTEXT_ARG_DEF) +{ + PangoLayout *layout = GetLayout(aString, aLength); + PangoLayoutLine *line = pango_layout_get_line(layout, 0); - aDimensions.width = NSToCoordRound(rect.width * P2T / PANGO_SCALE); - aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(rect) * P2T / PANGO_SCALE); - aDimensions.descent = NSToCoordRound(PANGO_DESCENT(rect) * P2T / PANGO_SCALE); + PangoRectangle logical; + pango_layout_line_get_extents(line, NULL, &logical); + g_object_unref(layout); - // printf("GetTextDimensions %d %d %d\n", aDimensions.width, - //aDimensions.ascent, aDimensions.descent); + float P2T = mDeviceContext->DevUnitsToAppUnits(); - loser: - g_free(text); - g_object_unref(layout); + aDimensions.ascent = NSToCoordRound(PANGO_ASCENT(logical) * P2T / PANGO_SCALE); + aDimensions.descent = NSToCoordRound(PANGO_DESCENT(logical) * P2T / PANGO_SCALE); + aDimensions.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE); - return rv; + return NS_OK; } nsresult @@ -595,13 +626,13 @@ nsFontMetricsPango::GetTextDimensions(co nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext) + PRInt32* aFontID + CONTEXT_ARG_DEF) { return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks, aNumBreaks, aDimensions, aNumCharsFit, - aLastWordDimensions, aContext); + aLastWordDimensions CONTEXT_ARG_PASS); } @@ -614,8 +645,8 @@ nsFontMetricsPango::GetTextDimensions(co nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext) + PRInt32* aFontID + CONTEXT_ARG_DEF) { nsresult rv = NS_OK; PRInt32 curBreak = 0; @@ -623,23 +654,15 @@ nsFontMetricsPango::GetTextDimensions(co PRInt32 *utf8Breaks = new PRInt32[aNumBreaks]; - gchar *text = g_utf16_to_utf8(aString, (PRInt32)aLength, - NULL, NULL, NULL); + gchar* text; + gint text_len; + utf16_to_utf8 (aString, aLength, text, text_len); curChar = text; - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); - DUMP_PRUNICHAR(aString, (PRUint32)aLength) -#endif - rv = NS_ERROR_FAILURE; - goto loser; - } - // Covert the utf16 break offsets to utf8 break offsets for (PRInt32 curOffset=0; curOffset < aLength; - curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { + curOffset++, curChar = g_utf8_next_char(curChar)) { if (aBreaks[curBreak] == curOffset) { utf8Breaks[curBreak] = curChar - text; curBreak++; @@ -653,10 +676,10 @@ nsFontMetricsPango::GetTextDimensions(co utf8Breaks[curBreak] = curChar - text; #if 0 - if (strlen(text) != aLength) { - printf("Different lengths for utf16 %d and utf8 %d\n", aLength, strlen(text)); + if (text_len != aLength) { + printf("Different lengths for utf16 %d and utf8 %d\n", aLength, text_len); DUMP_PRUNICHAR(aString, aLength) - DUMP_PRUNICHAR(text, strlen(text)) + DUMP_PRUNICHAR(text, text_len) for (PRInt32 i = 0; i < aNumBreaks; ++i) { printf(" break %d utf16 %d utf8 %d\n", i, aBreaks[i], utf8Breaks[i]); } @@ -666,9 +689,9 @@ nsFontMetricsPango::GetTextDimensions(co // We'll use curBreak to indicate which of the breaks end up being // used for the break point for this line. curBreak = 0; - rv = GetTextDimensionsInternal(text, strlen(text), aAvailWidth, utf8Breaks, + rv = GetTextDimensionsInternal(text, text_len, aAvailWidth, utf8Breaks, aNumBreaks, aDimensions, aNumCharsFit, - aLastWordDimensions, aContext); + aLastWordDimensions CONTEXT_ARG_PASS); // Figure out which of the breaks we ended up using to convert // back to utf16 - start from the end. @@ -681,200 +704,365 @@ nsFontMetricsPango::GetTextDimensions(co } } - loser: - if (text) - g_free(text); + g_free(text); delete[] utf8Breaks; return rv; } -nsresult -nsFontMetricsPango::DrawString(const char *aString, PRUint32 aLength, - nscoord aX, nscoord aY, - const nscoord* aSpacing, - nsRenderingContextGTK *aContext, - nsDrawingSurfaceGTK *aSurface) +#ifdef PSPANGO + +typedef struct _nsPSPangoRenderer nsPSPangoRenderer; +typedef struct _nsPSPangoRendererClass nsPSPangoRendererClass; + +struct _nsPSPangoRenderer { - PangoLayout *layout = pango_layout_new(mPangoContext); + PangoRenderer parent_instance; + nsRenderingContextPS *psContext; + nsFontMetricsPSPango *psPangoFontMetrics; + float zoom; +}; - pango_layout_set_text(layout, aString, aLength); - FixupSpaceWidths(layout, aString); +struct _nsPSPangoRendererClass +{ + PangoRendererClass parent_class; +}; - int x = aX; - int y = aY; +#define _PS_TYPE_PANGO_RENDERER (_ps_pango_renderer_get_type()) +#define _PS_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRenderer)) +#define _PS_IS_PANGO_RENDERER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), _PS_TYPE_PANGO_RENDERER)) +#define _PS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRendererClass)) +#define _PS_IS_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), _PS_TYPE_PANGO_RENDERER)) +#define _PS_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), _PS_TYPE_PANGO_RENDERER, _nsPSPangoRendererClass)) - aContext->GetTranMatrix()->TransformCoord(&x, &y); +G_DEFINE_TYPE (_nsPSPangoRenderer, _ps_pango_renderer, PANGO_TYPE_RENDERER) - PangoLayoutLine *line; - if (pango_layout_get_line_count(layout) != 1) { - printf("Warning: more than one line!\n"); - } - line = pango_layout_get_line(layout, 0); +static PangoRenderer * +get_renderer (void) +{ + static PangoRenderer *renderer = NULL; - aContext->UpdateGC(); - GdkGC *gc = aContext->GetGC(); + if (!renderer) + renderer = (PangoRenderer *) g_object_new (_PS_TYPE_PANGO_RENDERER, NULL); - if (aSpacing && *aSpacing) { - DrawStringSlowly(aString, NULL, aLength, aSurface->GetDrawable(), - gc, x, y, line, aSpacing); - } - else { - gdk_draw_layout_line(aSurface->GetDrawable(), gc, - x, y, - line); - } + return renderer; +} - g_object_unref(gc); - g_object_unref(layout); +static void +_ps_pango_renderer_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y); - // printf("DrawString (char *)\n"); +static void +_ps_pango_renderer_class_init (nsPSPangoRendererClass *klass) +{ + PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); + + renderer_class->draw_glyphs = _ps_pango_renderer_draw_glyphs; +} - return NS_OK; +static void +_ps_pango_renderer_init (nsPSPangoRenderer *renderer) +{ +} + +class nsPangoType1Generator : public nsPSFontGenerator { +public: + nsPangoType1Generator(); + ~nsPangoType1Generator(); + nsresult Init(PangoFont *aFont); + void GeneratePSFont(FILE* aFile); + +protected: + PangoFont *mFont; +}; + +nsPangoType1Generator::nsPangoType1Generator() +{ } nsresult -nsFontMetricsPango::DrawString(const PRUnichar* aString, PRUint32 aLength, - nscoord aX, nscoord aY, - PRInt32 aFontID, - const nscoord* aSpacing, - nsRenderingContextGTK *aContext, - nsDrawingSurfaceGTK *aSurface) +nsPangoType1Generator::Init(PangoFont *aFont) + { + NS_ENSURE_TRUE(aFont, NS_ERROR_FAILURE); + mFont = aFont; + g_object_ref (mFont); + return NS_OK; +} + +nsPangoType1Generator::~nsPangoType1Generator() { - nsresult rv = NS_OK; - int x = aX; - int y = aY; + g_object_unref (mFont); + mFont = nsnull; +} - aContext->UpdateGC(); - GdkGC *gc = aContext->GetGC(); +void nsPangoType1Generator::GeneratePSFont(FILE* aFile) +{ + FT_Face face = pango_fc_font_lock_face ((PangoFcFont *) mFont); - PangoLayout *layout = pango_layout_new(mPangoContext); + if (face == nsnull) + return; - gchar *text = g_utf16_to_utf8(aString, aLength, - NULL, NULL, NULL); + int wmode = 0; + if (mGlyphSubset->Count()) + FT2SubsetToType1FontSet(face, mGlyphSubset, wmode, aFile); - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::DrawString invalid unicode to follow"); - DUMP_PRUNICHAR(aString, aLength) -#endif - rv = NS_ERROR_FAILURE; - goto loser; - } + pango_fc_font_unlock_face ((PangoFcFont *) mFont); +} - pango_layout_set_text(layout, text, strlen(text)); - FixupSpaceWidths(layout, text); +typedef struct +{ + nsCString *FontNameBase; + nsCStringKey *key; + int font_size; +} PSPangoFontData; - aContext->GetTranMatrix()->TransformCoord(&x, &y); +static void +ps_pango_font_data_destroy (PSPangoFontData *data) +{ + delete data->key; + delete data->FontNameBase; + g_free (data); +} - PangoLayoutLine *line; - if (pango_layout_get_line_count(layout) != 1) { - printf("Warning: more than one line!\n"); - } - line = pango_layout_get_line(layout, 0); +static void +_ps_pango_renderer_draw_glyphs (PangoRenderer *renderer, + PangoFont *font, + PangoGlyphString *glyphs, + int x, + int y) +{ + if (!glyphs->num_glyphs) + return; - if (aSpacing && *aSpacing) { - DrawStringSlowly(text, aString, aLength, aSurface->GetDrawable(), - gc, x, y, line, aSpacing); - } - else { - gdk_draw_layout_line(aSurface->GetDrawable(), gc, - x, y, - line); - } + static GQuark data_quark = 0; + if (!data_quark) + data_quark = g_quark_from_static_string ("ps-pango-font-data"); - loser: + PSPangoFontData *data; + if (!(data = (PSPangoFontData *) g_object_get_qdata (G_OBJECT (font), data_quark))) + { + data = g_new (PSPangoFontData, 1); - g_free(text); - g_object_unref(gc); - g_object_unref(layout); + FT_Face face = pango_fc_font_lock_face ((PangoFcFont *) font); + if (face == nsnull) + return; + int wmode = 0; + data->FontNameBase = new nsCString (); + if (NS_FAILED(FT2ToType1FontName(face, wmode, *data->FontNameBase))) { + g_free (data); + pango_fc_font_unlock_face ((PangoFcFont *) font); + return; + } + pango_fc_font_unlock_face ((PangoFcFont *) font); - // printf("DrawString\n"); + PangoFontDescription *desc = pango_font_describe (font); + data->font_size = pango_font_description_get_size (desc); + pango_font_description_free (desc); + + data->key = new nsCStringKey (*data->FontNameBase); + + g_object_set_qdata_full (G_OBJECT (font), data_quark, data, (GDestroyNotify) ps_pango_font_data_destroy); + } + + nsPSPangoRenderer *ps_renderer = (nsPSPangoRenderer *)renderer; + nsRenderingContextPS *aContext = ps_renderer->psContext; + nsFontMetricsPSPango *metrics = ps_renderer->psPangoFontMetrics; + nsDeviceContextPS* dc = NS_REINTERPRET_CAST (nsDeviceContextPS*, metrics->GetDeviceContext()); + nsPostScriptObj* psObj = aContext->GetPostScriptObj(); + nsHashtable *psFGList = dc->GetPSFontGeneratorList(); + g_return_if_fail (psFGList); + nsPSFontGenerator* psFontGen = (nsPSFontGenerator*) psFGList->Get(data->key); + if (!psFontGen) { + nsresult rv; + psFontGen = new nsPangoType1Generator; + g_return_if_fail (psFontGen); + rv = ((nsPangoType1Generator*)psFontGen)->Init(font); + if (NS_FAILED(rv)) { + delete psFontGen; + return; + } + psFGList->Put(data->key, (void *) psFontGen); + } + nscoord font_size = NSToCoordRound (ps_renderer->zoom * data->font_size / PANGO_SCALE); + + g_return_if_fail (aContext); + g_return_if_fail (psObj); + + nscoord aX = NSToCoordRound(ps_renderer->zoom * x / PANGO_SCALE); + nscoord aY = NSToCoordRound(ps_renderer->zoom * y / PANGO_SCALE); + psObj->moveto(aX, aY); + + PRInt32 currSubFont, prevSubFont = -1; + PRUint32 i; + PangoGlyphString gl; + + gl.glyphs = glyphs->glyphs; + gl.num_glyphs = 0; + currSubFont = prevSubFont; + for (i = 0; i < glyphs->num_glyphs; ++i) { + PangoGlyph glyph = glyphs->glyphs[i].glyph; + + if (glyph != PANGO_GLYPH_EMPTY) + currSubFont = psFontGen->AddToGlyphSubset(glyph > 0x0fffffff ? 0 : glyph); + + if (prevSubFont != currSubFont) { + if (prevSubFont != -1) + psObj->show(&gl, ps_renderer->zoom, psFontGen, prevSubFont); + + psObj->setfont(*data->FontNameBase, (PRUint32) font_size, currSubFont); + prevSubFont = currSubFont; + gl.glyphs = glyphs->glyphs + i; + gl.num_glyphs = 0; + } - return rv; + gl.num_glyphs++; + } + + if (prevSubFont != -1) + psObj->show(&gl, ps_renderer->zoom, psFontGen, prevSubFont); } +#endif + +static void +draw_layout_line (int x, int y, + PangoLayoutLine *line, + nsFontMetricsPango *fm + CONTEXT_AND_SURFACE_ARG_DEF) +{ +#ifdef PSPANGO + PangoRenderer *renderer = get_renderer (); + nsPSPangoRenderer *ps_renderer = (nsPSPangoRenderer *)renderer; + ps_renderer->psContext = aContext; + ps_renderer->psPangoFontMetrics = fm; + nsDeviceContextPS* dc = NS_REINTERPRET_CAST (nsDeviceContextPS*, fm->GetDeviceContext()); + ps_renderer->zoom = dc->DevUnitsToAppUnits(); + + pango_renderer_draw_layout_line (renderer, line, + NSToCoordRound (x * PANGO_SCALE / ps_renderer->zoom), + NSToCoordRound (y * PANGO_SCALE / ps_renderer->zoom)); +#else + aContext->UpdateGC(); + GdkGC *gc = aContext->GetGC(); + gdk_draw_layout_line(aSurface->GetDrawable(), gc, x, y, line); + g_object_unref(gc); +#endif +} + -#ifdef MOZ_MATHML nsresult -nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength, - nsBoundingMetrics &aBoundingMetrics, - nsRenderingContextGTK *aContext) +nsFontMetricsPango::DrawString(const char *aString, PRUint32 aLength, + nscoord aX, nscoord aY, + const nscoord* aSpacing + CONTEXT_AND_SURFACE_ARG_DEF) { - printf("GetBoundingMetrics (char *)\n"); - return NS_ERROR_FAILURE; + int x = aX; + int y = aY; + + aContext->GetTranMatrix()->TransformCoord(&x, &y); + + PangoLayout *layout = GetLayout(aString, aLength); + PangoLayoutLine *line = pango_layout_get_line(layout, 0); + + ApplySpacing(aString, aLength, line, aSpacing); + draw_layout_line(x, y, line, this CONTEXT_AND_SURFACE_ARG_PASS); + + g_object_unref(layout); + + return NS_OK; } nsresult -nsFontMetricsPango::GetBoundingMetrics(const PRUnichar *aString, - PRUint32 aLength, - nsBoundingMetrics &aBoundingMetrics, - PRInt32 *aFontID, - nsRenderingContextGTK *aContext) +nsFontMetricsPango::DrawString(const PRUnichar* aString, PRUint32 aLength, + nscoord aX, nscoord aY, + PRInt32 aFontID, + const nscoord* aSpacing + CONTEXT_AND_SURFACE_ARG_DEF) { - nsresult rv = NS_OK; - PangoLayout *layout = pango_layout_new(mPangoContext); + int x = aX; + int y = aY; - gchar *text = g_utf16_to_utf8(aString, aLength, - NULL, NULL, NULL); + aContext->GetTranMatrix()->TransformCoord(&x, &y); - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetBoundingMetrics invalid unicode to follow"); - DUMP_PRUNICHAR(aString, aLength) -#endif - aBoundingMetrics.Clear(); + PangoLayout *layout = GetLayout(aString, aLength); + PangoLayoutLine *line = pango_layout_get_line(layout, 0); - rv = NS_ERROR_FAILURE; - goto loser; - } + ApplySpacing(aString, aLength, line, aSpacing); + draw_layout_line(x, y, line, this CONTEXT_AND_SURFACE_ARG_PASS); - pango_layout_set_text(layout, text, -1); - FixupSpaceWidths(layout, text); + g_object_unref(layout); + + return NS_OK; +} - PangoLayoutLine *line; - if (pango_layout_get_line_count(layout) != 1) { - printf("Warning: more than one line!\n"); - } - line = pango_layout_get_line(layout, 0); + +#ifdef MOZ_MATHML +void +nsFontMetricsPango::GetBoundingMetricsInternal(PangoLayout *aLayout, + nsBoundingMetrics &aBoundingMetrics + CONTEXT_ARG_DEF) +{ + PangoLayoutLine *line = pango_layout_get_line(aLayout, 0); // Get the ink and logical extents PangoRectangle ink, logical; pango_layout_line_get_extents(line, &ink, &logical); - float P2T; - P2T = mDeviceContext->DevUnitsToAppUnits(); + float P2T = mDeviceContext->DevUnitsToAppUnits(); aBoundingMetrics.leftBearing = NSToCoordRound(PANGO_LBEARING(ink) * P2T / PANGO_SCALE); aBoundingMetrics.rightBearing = NSToCoordRound(PANGO_RBEARING(ink) * P2T / PANGO_SCALE); aBoundingMetrics.ascent = NSToCoordRound(PANGO_ASCENT(ink) * P2T / PANGO_SCALE); aBoundingMetrics.descent = NSToCoordRound(PANGO_DESCENT(ink) * P2T / PANGO_SCALE); aBoundingMetrics.width = NSToCoordRound(logical.width * P2T / PANGO_SCALE); +} - loser: - g_free(text); +nsresult +nsFontMetricsPango::GetBoundingMetrics(const char *aString, PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics + CONTEXT_ARG_DEF) +{ + PangoLayout *layout = GetLayout(aString, aLength); + GetBoundingMetricsInternal (layout, aBoundingMetrics CONTEXT_ARG_PASS); g_object_unref(layout); - return rv; + return NS_OK; +} + +nsresult +nsFontMetricsPango::GetBoundingMetrics(const PRUnichar *aString, + PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics, + PRInt32 *aFontID + CONTEXT_ARG_DEF) +{ + PangoLayout *layout = GetLayout(aString, aLength); + GetBoundingMetricsInternal (layout, aBoundingMetrics CONTEXT_ARG_PASS); + g_object_unref(layout); + + return NS_OK; } #endif /* MOZ_MATHML */ +#ifndef PSPANGO GdkFont* nsFontMetricsPango::GetCurrentGDKFont(void) { return nsnull; } +#endif nsresult nsFontMetricsPango::SetRightToLeftText(PRBool aIsRTL) { if (aIsRTL) { if (!mRTLPangoContext) { - mRTLPangoContext = gdk_pango_context_get(); + mRTLPangoContext = get_context(); pango_context_set_base_dir(mRTLPangoContext, PANGO_DIRECTION_RTL); - - gdk_pango_context_set_colormap(mRTLPangoContext, gdk_rgb_get_cmap()); pango_context_set_language(mRTLPangoContext, GetPangoLanguage(mLangGroup)); pango_context_set_font_description(mRTLPangoContext, mPangoFontDesc); } @@ -899,34 +1087,18 @@ nsFontMetricsPango::GetClusterInfo(const PRUint32 aLength, PRUint8 *aClusterStarts) { - nsresult rv = NS_OK; PangoLogAttr *attrs = NULL; gint n_attrs = 0; - PangoLayout *layout = pango_layout_new(mPangoContext); - - // Convert the incoming UTF-16 to UTF-8 - gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL); - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); - DUMP_PRUNICHAR(aText, aLength) -#endif - rv = NS_ERROR_FAILURE; - goto loser; - } - - // Set up the pango layout - pango_layout_set_text(layout, text, strlen(text)); - FixupSpaceWidths(layout, text); + PangoLayout *layout = GetLayout(aText, aLength); + pango_layout_get_log_attrs(layout, &attrs, &n_attrs); + g_object_unref(layout); // Convert back to UTF-16 while filling in the cluster info // structure. - pango_layout_get_log_attrs(layout, &attrs, &n_attrs); - for (PRUint32 pos = 0; pos < aLength; pos++) { if (IS_HIGH_SURROGATE(aText[pos])) { - aClusterStarts[pos] = 1; + aClusterStarts[pos] = 1;//FIXME: shouldn't this be zero?! --be pos++; } else { @@ -934,56 +1106,34 @@ nsFontMetricsPango::GetClusterInfo(const } } - loser: - if (attrs) - g_free(attrs); - if (text) - g_free(text); - if (layout) - g_object_unref(layout); + g_free(attrs); - return rv; + return NS_OK; } PRInt32 -nsFontMetricsPango::GetPosition(const PRUnichar *aText, PRUint32 aLength, - nsPoint aPt) +nsFontMetricsPango::GetPosition(const PRUnichar *aText, PRUint32 aLength, nsPoint aPt) { int trailing = 0; int inx = 0; - const gchar *curChar; PRInt32 retval = 0; float f = mDeviceContext->AppUnitsToDevUnits(); - PangoLayout *layout = pango_layout_new(mPangoContext); PRUint32 localX = (PRUint32)(aPt.x * PANGO_SCALE * f); PRUint32 localY = (PRUint32)(aPt.y * PANGO_SCALE * f); - // Convert the incoming UTF-16 to UTF-8 - gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL); - - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); - DUMP_PRUNICHAR(aText, aLength) -#endif - retval = -1; - goto loser; - } - - // Set up the pango layout - pango_layout_set_text(layout, text, strlen(text)); - FixupSpaceWidths(layout, text); + PangoLayout *layout = GetLayout(aText, aLength); pango_layout_xy_to_index(layout, localX, localY, &inx, &trailing); // Convert the index back to the utf-16 index - curChar = text; + const gchar *text = pango_layout_get_text (layout); + const gchar *curChar = text; for (PRUint32 curOffset=0; curOffset < aLength; - curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { + curOffset++, curChar = g_utf8_next_char(curChar)) { // Check for a match before checking for a surrogate pair if (curChar - text == inx) { @@ -1006,13 +1156,9 @@ nsFontMetricsPango::GetPosition(const PR trailing--; } - loser: - if (text) - g_free(text); - if (layout) - g_object_unref(layout); + g_object_unref(layout); - return retval; + return retval; } nsresult @@ -1022,28 +1168,21 @@ nsFontMetricsPango::GetRangeWidth(const PRUint32 aEnd, PRUint32 &aWidth) { - nsresult rv = NS_OK; PRUint32 utf8Start = 0; PRUint32 utf8End = 0; aWidth = 0; // Convert the incoming UTF-16 to UTF-8 - gchar *text = g_utf16_to_utf8(aText, aLength, NULL, NULL, NULL); - gchar *curChar = text; - if (!text) { -#ifdef DEBUG - NS_WARNING("nsFontMetricsPango::GetWidth invalid unicode to follow"); - DUMP_PRUNICHAR(aText, aLength) -#endif - rv = NS_ERROR_FAILURE; - goto loser; - } + gchar* text; + gint text_len; + utf16_to_utf8 (aText, aLength, text, text_len); + gchar *curChar = text; // Convert the utf16 offsets into utf8 offsets for (PRUint32 curOffset = 0; curOffset < aLength; - curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { + curOffset++, curChar = g_utf8_next_char(curChar)) { if (curOffset == aStart) utf8Start = curChar - text; @@ -1057,15 +1196,13 @@ nsFontMetricsPango::GetRangeWidth(const // Special case where the end index is the same as the length if (aLength == aEnd) - utf8End = strlen(text); + utf8End = text_len; - rv = GetRangeWidth(text, strlen(text), utf8Start, utf8End, aWidth); + GetRangeWidth(text, text_len, utf8Start, utf8End, aWidth); - loser: - if (text) - g_free(text); + g_free(text); - return rv; + return NS_OK; } nsresult @@ -1075,43 +1212,26 @@ nsFontMetricsPango::GetRangeWidth(const PRUint32 aEnd, PRUint32 &aWidth) { - nsresult rv = NS_OK; int *ranges = NULL; int n_ranges = 0; float f; aWidth = 0; - PangoLayout *layout = pango_layout_new(mPangoContext); - - if (!aText) { - rv = NS_ERROR_FAILURE; - goto loser; - } - - pango_layout_set_text(layout, aText, aLength); - FixupSpaceWidths(layout, aText); - - PangoLayoutLine *line; - if (pango_layout_get_line_count(layout) != 1) { - printf("Warning: more than one line!\n"); - } - line = pango_layout_get_line(layout, 0); + PangoLayout *layout = GetLayout(aText, aLength); + PangoLayoutLine *line = pango_layout_get_line(layout, 0); pango_layout_line_get_x_ranges(line, aStart, aEnd, &ranges, &n_ranges); aWidth = (ranges[((n_ranges - 1) * 2) + 1] - ranges[0]); f = mDeviceContext-> DevUnitsToAppUnits(); - aWidth = nscoord(aWidth * f / PANGO_SCALE); + aWidth = NSToCoordRound(aWidth * f / PANGO_SCALE); - loser: - if (ranges) - g_free(ranges); - if (layout) - g_object_unref(layout); + g_free(ranges); + g_object_unref(layout); - return rv; + return NS_OK; } /* static */ @@ -1134,7 +1254,7 @@ nsFontMetricsPango::FamilyExists(nsIDevi NS_ConvertUTF16toUTF8 name(aName); nsresult rv = NS_ERROR_FAILURE; - PangoContext *context = gdk_pango_context_get(); + PangoContext *context = get_context(); PangoFontFamily **familyList; int n; @@ -1233,16 +1353,13 @@ nsFontMetricsPango::RealizeFont(void) // Now that we have the font description set up, create the // context. - mLTRPangoContext = gdk_pango_context_get(); + mLTRPangoContext = get_context(); mPangoContext = mLTRPangoContext; // Make sure to set the base direction to LTR - if layout needs to // render RTL text it will use ::SetRightToLeftText() pango_context_set_base_dir(mPangoContext, PANGO_DIRECTION_LTR); - // Set the color map so we can draw later. - gdk_pango_context_set_colormap(mPangoContext, gdk_rgb_get_cmap()); - // Set the pango language now that we have a context pango_context_set_language(mPangoContext, GetPangoLanguage(mLangGroup)); @@ -1280,79 +1397,268 @@ nsFontMetricsPango::EnumFontCallback(con * This is only used when there's per-character spacing happening. * Well, really it can be either line or character spacing but it's * just turtles all the way down! + * + * To do it correctly (ligatures, etc) we need machinery that is private + * in Pango. IMPORT IT: + */ + +#define _PangoGlyphItemIter _nsFontMetricsPangoGlyphItemIter +#define PangoGlyphItemIter nsFontMetricsPangoGlyphItemIter + +#define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0) + +/* Structure holding state when we're iterating over a GlyphItem. + * start_index/cluster_end (and range_start/range_end in + * apply_attrs()) are offsets into the text, so note the difference + * of glyph_item->item->offset between them and clusters in the + * log_clusters[] array. */ +typedef struct _PangoGlyphItemIter PangoGlyphItemIter; + +struct _PangoGlyphItemIter +{ + PangoGlyphItem *glyph_item; + const gchar *text; + + int start_glyph; + int start_index; + int start_char; + + int end_glyph; + int end_index; + int end_char; +}; + +/** + * _pango_glyph_item_iter_next_cluster: + * @iter: a #PangoGlyphItemIter + * + * Advances the iterator to the next cluster in the glyph item. + * + * Return value: %TRUE if the iterator was advanced, %FALSE if we were already on the + * last cluster. + **/ +static gboolean +_pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter) +{ + int glyph_index = iter->end_glyph; + PangoGlyphString *glyphs = iter->glyph_item->glyphs; + PangoItem *item = iter->glyph_item->item; + + if (LTR (iter->glyph_item)) + { + if (glyph_index == glyphs->num_glyphs) + return FALSE; + } + else + { + if (glyph_index < 0) + return FALSE; + } + + iter->start_glyph = iter->end_glyph; + iter->start_index = iter->end_index; + iter->start_char = iter->end_char; + + if (LTR (iter->glyph_item)) + { + while (TRUE) + { + glyph_index++; + + if (glyph_index == glyphs->num_glyphs) + { + iter->end_index = item->offset + item->length; + iter->end_char = item->num_chars; + break; + } + + if (item->offset + glyphs->log_clusters[glyph_index] != iter->start_index) + { + iter->end_index = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_char += g_utf8_strlen (iter->text + iter->start_index, + iter->end_index - iter->start_index); + break; + } + } + } + else /* RTL */ + { + while (TRUE) + { + glyph_index--; + + if (glyph_index < 0) + { + iter->end_index = item->offset + item->length; + iter->end_char = item->num_chars; + break; + } + + if (item->offset + glyphs->log_clusters[glyph_index] != iter->start_index) + { + iter->end_index = item->offset + glyphs->log_clusters[glyph_index]; + iter->end_char += g_utf8_strlen (iter->text + iter->start_index, + iter->end_index - iter->start_index); + break; + } + } + } + + iter->end_glyph = glyph_index; + return TRUE; +} + +/** + * _pango_glyph_item_iter_init_start: + * @iter: pointer to a #PangoGlyphItemIter structure + * @glyph_item: the glyph item that the iter points into + * @text: text corresponding to the glyph item + * + * Initializes a #PangoGlyphItemIter structure to point to the + * first cluster in a glyph item. + * + * Return value: %FALSE if there are no clusters in the glyph item; + * in this case, the state of the iter is undefined. + **/ +static gboolean +_pango_glyph_item_iter_init_start (PangoGlyphItemIter *iter, + PangoGlyphItem *glyph_item, + const char *text) +{ + iter->glyph_item = glyph_item; + iter->text = text; + + if (LTR (glyph_item)) + iter->end_glyph = 0; + else + iter->end_glyph = glyph_item->glyphs->num_glyphs - 1; + + iter->end_index = glyph_item->item->offset; + iter->end_char = 0; + + /* Advance onto the first cluster of the glyph item */ + return _pango_glyph_item_iter_next_cluster (iter); +} + void -nsFontMetricsPango::DrawStringSlowly(const gchar *aText, - const PRUnichar *aOrigString, - PRUint32 aLength, - GdkDrawable *aDrawable, - GdkGC *aGC, gint aX, gint aY, - PangoLayoutLine *aLine, - const nscoord *aSpacing) -{ - float app2dev; - app2dev = mDeviceContext->AppUnitsToDevUnits(); - gint offset = 0; +nsFontMetricsPango::ApplySpacing(const gchar *aText, + PRUint32 aLength, + PangoLayoutLine *aLine, + const nscoord *aSpacing) +{ + if (!(aSpacing && *aSpacing)) + return; + + float app2dev = mDeviceContext->AppUnitsToDevUnits(); /* * We walk the list of glyphs returned in each layout run, * matching up the glyphs with the characters in the source text. * We use the aSpacing argument to figure out where to place those - * glyphs. It's important to note that since the string we're - * working with is in UTF-8 while the spacing argument assumes - * that offset will be part of the UTF-16 string. Logical - * attributes in pango are in byte offsets in the UTF-8 string, so - * we need to store the offsets based on the UTF-8 string. + * glyphs. */ - nscoord *utf8spacing = new nscoord[strlen(aText)]; + for (GSList *tmpList = aLine->runs; tmpList && tmpList->data; + tmpList = tmpList->next) { + PangoGlyphItem *glyph_item = (PangoGlyphItem *)tmpList->data; + PangoGlyphItemIter iter; + gboolean have_cluster; + PangoGlyphInfo *glyphs = glyph_item->glyphs->glyphs; + int residualWidth = 0; + + for (have_cluster = _pango_glyph_item_iter_init_start (&iter, glyph_item, aText); + have_cluster; + have_cluster = _pango_glyph_item_iter_next_cluster (&iter)) + { + int clusterOldWidth = 0; + int clusterNewWidth = 0; + int dir = iter.start_glyph < iter.end_glyph ? +1 : -1; + gboolean has_zero_width = FALSE; + + for (const char *p = iter.text + iter.start_index; + p < iter.text + iter.end_index; + p = g_utf8_next_char (p)) + clusterNewWidth += aSpacing[p - iter.text]; + + clusterNewWidth = (gint)(clusterNewWidth * app2dev * PANGO_SCALE); + + for (gint i = iter.start_glyph; i != iter.end_glyph; i += dir) { + if (!glyphs[i].geometry.width) + has_zero_width = TRUE; + clusterOldWidth += glyphs[i].geometry.width; + } + + /* if a zero-width glyph exists, don't touch the glyph widths. + * required for combining marks. ff thinks they have a width. + * instead, we charge the difference to the next space glyph. */ + if (has_zero_width) { + residualWidth += clusterNewWidth - clusterOldWidth; + continue; + } - if (aOrigString) { - const gchar *curChar = aText; - bzero(utf8spacing, sizeof(nscoord) * strlen(aText)); - - // Covert the utf16 spacing offsets to utf8 spacing offsets - for (PRUint32 curOffset=0; curOffset < aLength; - curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) { - utf8spacing[curChar - aText] = aSpacing[curOffset]; + /* If a space glyph is found, charge it whatever residual we + * have accumulated so far. */ + if (iter.end_index - iter.start_index == 1 && + *(iter.text + iter.start_index) == ' ') { + clusterNewWidth += residualWidth; + residualWidth = 0; + } + +#ifndef PSPANGO + /* do some hinting for display */ + + if (clusterOldWidth % PANGO_SCALE == 0 && clusterNewWidth % PANGO_SCALE != 0) { + int tmp = clusterNewWidth; + clusterNewWidth = PANGO_PIXELS (clusterNewWidth) * PANGO_SCALE; + residualWidth += tmp - clusterNewWidth; + } +#endif - if (IS_HIGH_SURROGATE(aOrigString[curOffset])) - curOffset++; + /* find the first non-zero-width glyph and adjust its width */ + for (gint i = iter.start_glyph; i != iter.end_glyph; i += dir) + if (glyphs[i].geometry.width) { + glyphs[i].geometry.width += clusterNewWidth - clusterOldWidth; + break; + } } } - else { - memcpy(utf8spacing, aSpacing, (sizeof(nscoord *) * aLength)); - } +} - gint curRun = 0; +void +nsFontMetricsPango::ApplySpacing(const PRUnichar *aText, + PRUint32 aLength, + PangoLayoutLine *aLine, + const nscoord *aSpacing) +{ + if (!(aSpacing && *aSpacing)) + return; - for (GSList *tmpList = aLine->runs; tmpList && tmpList->data; - tmpList = tmpList->next, curRun++) { - PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data; - gint tmpOffset = 0; + const char *utf8Text = pango_layout_get_text (aLine->layout); + int utf8Text_len = aLine->start_index + aLine->length; - /* printf(" Rendering run %d: \"%s\"\n", curRun, - &aText[layoutRun->item->offset]); */ + /* Since the string we're + * working with is in UTF-8 while the spacing argument assumes + * that offset will be part of the UTF-16 string. Logical + * attributes in pango are in byte offsets in the UTF-8 string, so + * we need to store the offsets based on the UTF-8 string. + */ + nscoord *utf8spacing = g_new0 (nscoord, utf8Text_len); - for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) { - /* printf("glyph %d offset %d orig width %d new width %d\n", i, - * layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset, - * layoutRun->glyphs->glyphs[i].geometry.width, - * (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] * app2dev * PANGO_SCALE)); - */ - gint thisOffset = (gint)(utf8spacing[layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset] - * app2dev * PANGO_SCALE); - layoutRun->glyphs->glyphs[i].geometry.width = thisOffset; - tmpOffset += thisOffset; - } + const gchar *curChar = utf8Text + aLine->start_index; - /* printf(" rendering at X coord %d\n", aX + offset); */ - offset += tmpOffset; + // Covert the utf16 spacing offsets to utf8 spacing offsets + for (PRUint32 curOffset=0; curOffset < aLength; + curOffset++, curChar = g_utf8_next_char(curChar)) { + utf8spacing[curChar - utf8Text] = aSpacing[curOffset]; + + if (IS_HIGH_SURROGATE(aText[curOffset])) + curOffset++; } - gdk_draw_layout_line(aDrawable, aGC, aX, aY, aLine); + ApplySpacing (utf8Text, utf8Text_len, aLine, utf8spacing); - delete[] utf8spacing; + g_free (utf8spacing); } nsresult @@ -1363,8 +1669,8 @@ nsFontMetricsPango::GetTextDimensionsInt PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, - nsTextDimensions& aLastWordDimensions, - nsRenderingContextGTK *aContext) + nsTextDimensions& aLastWordDimensions + CONTEXT_ARG_DEF) { NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array"); @@ -1410,7 +1716,7 @@ nsFontMetricsPango::GetTextDimensionsInt // All the characters should fit numChars = aLength - start; breakIndex = aNumBreaks - 1; - } + } else { breakIndex = prevBreakState_BreakIndex; while (((breakIndex + 1) < aNumBreaks) && @@ -1431,7 +1737,7 @@ nsFontMetricsPango::GetTextDimensionsInt if ((1 == numChars) && (aString[start] == ' ')) GetSpaceWidth(twWidth); else if (numChars > 0) - GetWidth(&aString[start], numChars, twWidth, aContext); + GetWidth(&aString[start], numChars, twWidth CONTEXT_ARG_PASS); // See if the text fits PRBool textFits = (twWidth + width) <= aAvailWidth; @@ -1481,8 +1787,7 @@ nsFontMetricsPango::GetTextDimensionsInt if ((1 == numChars) && (aString[start] == ' ')) GetSpaceWidth(twWidth); else if (numChars > 0) - GetWidth(&aString[start], numChars, twWidth, - aContext); + GetWidth(&aString[start], numChars, twWidth CONTEXT_ARG_PASS); width -= twWidth; aNumCharsFit = start; breakIndex--; @@ -1504,9 +1809,16 @@ nsFontMetricsPango::GetTextDimensionsInt } void -nsFontMetricsPango::FixupSpaceWidths (PangoLayout *aLayout, - const char *aString) +nsFontMetricsPango::FixupSpaceWidths (PangoLayout *aLayout) { + if (!mPangoSpaceWidth) + return; + + const char *aString = pango_layout_get_text (aLayout); + + if (pango_layout_get_line_count(aLayout) != 1) { + printf("Warning: more than one line!\n"); + } PangoLayoutLine *line = pango_layout_get_line(aLayout, 0); gint curRun = 0; @@ -1523,6 +1835,107 @@ nsFontMetricsPango::FixupSpaceWidths (Pa } } +PangoLayout* +nsFontMetricsPango::GetLayout (const PRUnichar* aText, + PRUint32 aLength) +{ + gchar* text; + gint length; + utf16_to_utf8 (aText, aLength, text, length); + + PangoLayout *layout = pango_layout_new(mPangoContext); + pango_layout_set_text (layout, text, length); + FixupSpaceWidths (layout); + + g_free ((gpointer) text); + + return layout; +} + +PangoLayout* +nsFontMetricsPango::GetLayout (const gchar* aText, + PRInt32 aLength) +{ + gboolean has_nul = FALSE; + int i; + + for (i = 0; i < aLength; i++) + if (!aText[i]) { + has_nul = TRUE; + break; + } + + if (has_nul) { + /* Pango doesn't correctly handle nuls. We convert them to 0xff. */ + + char *p = (char *) g_memdup (aText, aLength); + + /* don't need to reset i */ + for (; i < aLength; i++) + if (!p[i]) + p[i] = (char) 0xff; + + aText = p; + } + + PangoLayout *layout = pango_layout_new(mPangoContext); + pango_layout_set_text (layout, aText, aLength); + FixupSpaceWidths (layout); + + if (has_nul) + g_free ((gpointer) aText); + + return layout; +} + +static void +utf16_to_utf8 (const PRUnichar* aText, PRUint32 aLength, char *&text, gint &length) +{ + gboolean need_copy = FALSE; + int i; + + for (i = 0; i < aLength; i++) { + if (!aText[i] || IS_LOW_SURROGATE (aText[i])) + need_copy = TRUE; + else if (IS_HIGH_SURROGATE (aText[i])) { + if (i < aLength - 1 && IS_LOW_SURROGATE (aText[i+1])) + i++; + else + need_copy = TRUE; + } + } + + if (need_copy) { + + /* Pango doesn't correctly handle nuls. We convert them to 0xff. */ + /* Also "validate" UTF-16 text to make sure conversion doesn't fail. */ + + PRUnichar *p = (PRUnichar *) g_memdup (aText, aLength * sizeof (aText[0])); + + /* don't need to reset i */ + for (i = 0; i < aLength; i++) { + if (!p[i] || IS_LOW_SURROGATE (p[i])) + p[i] = 0xFFFD; + else if (IS_HIGH_SURROGATE (p[i])) { + if (i < aLength - 1 && IS_LOW_SURROGATE (aText[i+1])) + i++; + else + p[i] = 0xFFFD; + } + } + + aText = p; + } + + glong items_written; + text = g_utf16_to_utf8 (aText, aLength, NULL, &items_written, NULL); + length = items_written; + + if (need_copy) + g_free ((gpointer) aText); + +} + /* static */ PangoLanguage * GetPangoLanguage(nsIAtom *aLangGroup) --- mozilla.back/gfx/src/gtk/nsFontMetricsPango.h.orig 2006-06-30 01:18:34.000000000 +0200 +++ mozilla.back/gfx/src/gtk/nsFontMetricsPango.h 2007-06-28 15:16:39.000000000 +0200 @@ -37,17 +37,53 @@ * * ***** END LICENSE BLOCK ***** */ + #include "nsIFontMetrics.h" #include "nsIFontEnumerator.h" #include "nsCRT.h" #include "nsIAtom.h" #include "nsString.h" #include "nsVoidArray.h" + +#ifdef PSPANGO +#include "nsFontMetricsPS.h" +#else #include "nsIFontMetricsGTK.h" +#endif #include -class nsFontMetricsPango : public nsIFontMetricsGTK +#ifdef PSPANGO + +#define CONTEXT_ARG_DEF +#define CONTEXT_ARG_PASS +#define CONTEXT_ARG_NULL +#define CONTEXT_AND_SURFACE_ARG_DEF , nsRenderingContextPS *aContext +#define CONTEXT_AND_SURFACE_ARG_PASS , aContext + +#else + +#define CONTEXT_ARG_DEF , nsRenderingContextGTK *aContext +#define CONTEXT_ARG_PASS , aContext +#define CONTEXT_ARG_NULL , NULL +#define CONTEXT_AND_SURFACE_ARG_DEF , nsRenderingContextGTK *aContext, nsDrawingSurfaceGTK *aSurface +#define CONTEXT_AND_SURFACE_ARG_PASS , aContext, aSurface + +#endif + + +#ifdef PSPANGO + +#define nsFontMetricsPango nsFontMetricsPSPango +#define PSPANGO_PARENT_CLASS nsFontMetricsPS + +#else + +#define PSPANGO_PARENT_CLASS nsIFontMetricsGTK + +#endif + +class nsFontMetricsPango : public PSPANGO_PARENT_CLASS { public: nsFontMetricsPango(); @@ -136,20 +172,30 @@ public: PRInt32 GetMaxStringLength() { return mMaxStringLength; } - // nsIFontMetricsGTK (calls from the font rendering layer) - virtual nsresult GetWidth(const char* aString, PRUint32 aLength, - nscoord& aWidth, - nsRenderingContextGTK *aContext); - virtual nsresult GetWidth(const PRUnichar* aString, PRUint32 aLength, - nscoord& aWidth, PRInt32 *aFontID, - nsRenderingContextGTK *aContext); + // nsIFontMetrics (calls from the font rendering layer) - virtual nsresult GetTextDimensions(const PRUnichar* aString, +#ifdef PSPANGO + NS_IMETHOD GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength); + NS_IMETHOD GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength); +#endif + + NS_METHOD GetWidth(const char* aString, PRUint32 aLength, + nscoord& aWidth + CONTEXT_ARG_DEF); + NS_METHOD GetWidth(const PRUnichar* aString, PRUint32 aLength, + nscoord& aWidth, PRInt32 *aFontID + CONTEXT_ARG_DEF); + + NS_METHOD GetTextDimensions(const char* aString, + PRUint32 aLength, + nsTextDimensions& aDimensions + CONTEXT_ARG_DEF); + NS_METHOD GetTextDimensions(const PRUnichar* aString, PRUint32 aLength, nsTextDimensions& aDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext); - virtual nsresult GetTextDimensions(const char* aString, + PRInt32* aFontID + CONTEXT_ARG_DEF); + NS_METHOD GetTextDimensions(const char* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, @@ -157,9 +203,9 @@ public: nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext); - virtual nsresult GetTextDimensions(const PRUnichar* aString, + PRInt32* aFontID + CONTEXT_ARG_DEF); + NS_METHOD GetTextDimensions(const PRUnichar* aString, PRInt32 aLength, PRInt32 aAvailWidth, PRInt32* aBreaks, @@ -167,38 +213,37 @@ public: nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, nsTextDimensions& aLastWordDimensions, - PRInt32* aFontID, - nsRenderingContextGTK *aContext); + PRInt32* aFontID + CONTEXT_ARG_DEF); - virtual nsresult DrawString(const char *aString, PRUint32 aLength, + NS_METHOD DrawString(const char *aString, PRUint32 aLength, nscoord aX, nscoord aY, - const nscoord* aSpacing, - nsRenderingContextGTK *aContext, - nsDrawingSurfaceGTK *aSurface); - virtual nsresult DrawString(const PRUnichar* aString, PRUint32 aLength, + const nscoord* aSpacing + CONTEXT_AND_SURFACE_ARG_DEF); + + NS_METHOD DrawString(const PRUnichar* aString, PRUint32 aLength, nscoord aX, nscoord aY, PRInt32 aFontID, - const nscoord* aSpacing, - nsRenderingContextGTK *aContext, - nsDrawingSurfaceGTK *aSurface); + const nscoord* aSpacing + CONTEXT_AND_SURFACE_ARG_DEF); #ifdef MOZ_MATHML - virtual nsresult GetBoundingMetrics(const char *aString, PRUint32 aLength, - nsBoundingMetrics &aBoundingMetrics, - nsRenderingContextGTK *aContext); - virtual nsresult GetBoundingMetrics(const PRUnichar *aString, + NS_METHOD GetBoundingMetrics(const char *aString, PRUint32 aLength, + nsBoundingMetrics &aBoundingMetrics + CONTEXT_ARG_DEF); + NS_METHOD GetBoundingMetrics(const PRUnichar *aString, PRUint32 aLength, nsBoundingMetrics &aBoundingMetrics, - PRInt32 *aFontID, - nsRenderingContextGTK *aContext); + PRInt32 *aFontID + CONTEXT_ARG_DEF); #endif /* MOZ_MATHML */ - +#ifndef PSPANGO virtual GdkFont* GetCurrentGDKFont(void); - - virtual nsresult SetRightToLeftText(PRBool aIsRTL); +#endif virtual PRBool GetRightToLeftText(); - - virtual nsresult GetClusterInfo(const PRUnichar *aText, + NS_METHOD SetRightToLeftText(PRBool aIsRTL); + + NS_METHOD GetClusterInfo(const PRUnichar *aText, PRUint32 aLength, PRUint8 *aClusterStarts); @@ -206,32 +251,35 @@ public: PRUint32 aLength, nsPoint aPt); - virtual nsresult GetRangeWidth(const PRUnichar *aText, + NS_METHOD GetRangeWidth(const PRUnichar *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth); - virtual nsresult GetRangeWidth(const char *aText, + NS_METHOD GetRangeWidth(const char *aText, PRUint32 aLength, PRUint32 aStart, PRUint32 aEnd, PRUint32 &aWidth); // get hints for the font - static PRUint32 GetHints (void); +#ifndef PSPANGO + static +#endif + PRUint32 GetHints (void); // drawing surface methods static nsresult FamilyExists (nsIDeviceContext *aDevice, const nsString &aName); + private: // generic font metrics class bits nsCStringArray mFontList; nsAutoVoidArray mFontIsGeneric; - nsIDeviceContext *mDeviceContext; nsCOMPtr mLangGroup; nsCString *mGenericFont; float mPointSize; @@ -246,6 +294,9 @@ private: PangoAttrList *mPangoAttrList; PRBool mIsRTL; +#ifndef PSPANGO + nsIDeviceContext *mDeviceContext; + // Cached font metrics nscoord mXHeight; nscoord mSuperscriptOffset; @@ -263,6 +314,7 @@ private: nscoord mMaxDescent; nscoord mMaxAdvance; nscoord mSpaceWidth; +#endif nscoord mPangoSpaceWidth; nscoord mAveCharWidth; PRInt32 mMaxStringLength; @@ -274,13 +326,14 @@ private: static PRBool EnumFontCallback(const nsString &aFamily, PRBool aIsGeneric, void *aData); - void DrawStringSlowly(const gchar *aText, - const PRUnichar *aOrigString, - PRUint32 aLength, - GdkDrawable *aDrawable, - GdkGC *aGC, gint aX, gint aY, - PangoLayoutLine *aLine, - const nscoord *aSpacing); + void ApplySpacing(const gchar *aText, + PRUint32 aLength, + PangoLayoutLine *aLine, + const nscoord *aSpacing); + void ApplySpacing(const PRUnichar *aText, + PRUint32 aLength, + PangoLayoutLine *aLine, + const nscoord *aSpacing); nsresult GetTextDimensionsInternal(const gchar* aString, PRInt32 aLength, @@ -289,10 +342,20 @@ private: PRInt32 aNumBreaks, nsTextDimensions& aDimensions, PRInt32& aNumCharsFit, - nsTextDimensions& aLastWordDimensions, - nsRenderingContextGTK *aContext); + nsTextDimensions& aLastWordDimensions + CONTEXT_ARG_DEF); +#ifdef MOZ_MATHML + void GetBoundingMetricsInternal(PangoLayout *aLayout, + nsBoundingMetrics &aBoundingMetrics + CONTEXT_ARG_DEF); +#endif /* MOZ_MATHML */ + + void FixupSpaceWidths (PangoLayout *aLayout); - void FixupSpaceWidths (PangoLayout *aLayout, const char *aString); + PangoLayout* GetLayout (const PRUnichar* aText, + PRUint32 aLength); + PangoLayout* GetLayout (const gchar* aText, + PRInt32 aLength); }; class nsFontEnumeratorPango : public nsIFontEnumerator