ofourdan / rpms / firefox

Forked from rpms/firefox 2 years ago
Clone
Blob Blame History Raw
Patch for Firefox 1.5.0.7 to add support for printing via Pango.
This also implements printing MathML via Pango, and prints bitmap
fonts too.

Authors:
	Behdad Esfahbod
	Chris Blizzard
	Akira TAGOH

Index: gfx/src/freetype/nsFreeType.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/freetype/nsFreeType.cpp,v
retrieving revision 1.28
diff -u -p -d -r1.28 nsFreeType.cpp
--- gfx/src/freetype/nsFreeType.cpp	13 Jul 2005 18:21:10 -0000	1.28
+++ gfx/src/freetype/nsFreeType.cpp	23 Oct 2006 17:37:09 -0000
@@ -123,6 +123,8 @@ FtFuncList nsFreeType2::FtFuncs [] = {
 // #endif
   {"FT_Get_First_Char",       NS_FT2_OFFSET(nsFT_Get_First_Char),       PR_FALSE},
   {"FT_Get_Next_Char",        NS_FT2_OFFSET(nsFT_Get_Next_Char),        PR_FALSE},
+  {"FT_Has_PS_Glyph_Names",   NS_FT2_OFFSET(nsFT_Has_PS_Glyph_Names),   PR_FALSE},
+  {"FT_Get_Glyph_Name",       NS_FT2_OFFSET(nsFT_Get_Glyph_Name),       PR_TRUE},
   {nsnull,                    0, 0}
 };
 
@@ -388,6 +390,22 @@ nsFreeType2::GetNextChar(FT_Face face, F
 } 
 
 NS_IMETHODIMP
+nsFreeType2::HasPSGlyphNames(FT_Face face, FT_Int *result)
+{
+  // call the FreeType2 function via the function pointer
+  *result = nsFT_Has_PS_Glyph_Names(face);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFreeType2::GetGlyphName(FT_Face face, FT_UInt glyph_index, FT_Pointer buffer, FT_UInt buffer_max)
+{
+  // call the FreeType2 function via the function pointer
+  FT_Error error = nsFT_Get_Glyph_Name(face, glyph_index, buffer, buffer_max);
+  return error ? NS_ERROR_FAILURE : NS_OK;
+}
+
+NS_IMETHODIMP
 nsFreeType2::SupportsExtFunc(PRBool *res)
 { 
   *res = gHasExtFunc;
Index: gfx/src/freetype/nsFreeType.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/freetype/nsFreeType.h,v
retrieving revision 1.18
diff -u -p -d -r1.18 nsFreeType.h
--- gfx/src/freetype/nsFreeType.h	1 May 2005 17:36:19 -0000	1.18
+++ gfx/src/freetype/nsFreeType.h	23 Oct 2006 17:37:09 -0000
@@ -52,6 +52,7 @@
 #include FT_CACHE_H
 #include FT_CACHE_IMAGE_H
 #include FT_TRUETYPE_TABLES_H
+#include FT_TYPE1_TABLES_H
 #include "nsIFreeType2.h"
 
 typedef struct FT_FaceRec_*  FT_Face;
@@ -138,6 +139,8 @@ typedef FT_Error (*FT_Glyph_To_Bitmap_t)
 
 typedef FT_ULong (*FT_Get_First_Char_t)(FT_Face, FT_UInt*);
 typedef FT_ULong (*FT_Get_Next_Char_t)(FT_Face, FT_ULong, FT_UInt*);
+typedef FT_Int   (*FT_Has_PS_Glyph_Names_t)(FT_Face);
+typedef FT_Error (*FT_Get_Glyph_Name_t)(FT_Face, FT_UInt, FT_Pointer, FT_UInt);
 
 class nsFreeTypeFace;
 
@@ -193,11 +196,13 @@ protected:
 // #endif
   FT_Get_First_Char_t       nsFT_Get_First_Char;
   FT_Get_Next_Char_t        nsFT_Get_Next_Char;
+  FT_Has_PS_Glyph_Names_t   nsFT_Has_PS_Glyph_Names;
+  FT_Get_Glyph_Name_t       nsFT_Get_Glyph_Name;
 
   // this array needs to be big enough to hold all the function pointers
   // plus one extra for the null at the end
 // #ifdef MOZ_SVG
-  static FtFuncList FtFuncs[24];
+  static FtFuncList FtFuncs[28];
 // #else
 //  static FtFuncList FtFuncs[20];
 // #endif
Index: gfx/src/ps/Makefile.in
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/Makefile.in,v
retrieving revision 1.57.8.1
diff -d -u -p -r1.57.8.1 Makefile.in
--- gfx/src/ps/Makefile.in	17 Jun 2006 15:16:14 -0000	1.57.8.1
+++ gfx/src/ps/Makefile.in	24 Oct 2006 18:36:45 -0000
@@ -98,6 +98,15 @@ EXTRA_DSO_LDOPTS = \
 		$(MOZ_UNICHARUTIL_LIBS) \
 		$(NULL)
 
+ifdef MOZ_ENABLE_PANGO
+CPPSRCS                += \
+               nsFontMetricsPSPango.cpp \
+               mozilla-ps-decoder.cpp
+EXTRA_DSO_LDOPTS += $(MOZ_PANGO_LIBS)
+CXXFLAGS       += $(MOZ_PANGO_CFLAGS)
+CFLAGS         += $(MOZ_PANGO_CFLAGS)
+endif
+
 ifdef MOZ_ENABLE_XFT
 EXTRA_DSO_LDOPTS += \
 		$(MOZ_XFT_LIBS) \
@@ -105,7 +114,7 @@ EXTRA_DSO_LDOPTS += \
 		$(NULL)
 endif
 
-ifneq (,$(MOZ_ENABLE_FREETYPE2)$(MOZ_ENABLE_XFT))
+ifneq (,$(MOZ_ENABLE_FREETYPE2)$(MOZ_ENABLE_XFT)$(MOZ_ENABLE_PANGO))
 CPPSRCS		+= \
 		nsType1.cpp \
 		$(NULL)
Index: gfx/src/ps/mozilla-ps-decoder.cpp
===================================================================
RCS file: gfx/src/ps/mozilla-ps-decoder.cpp
diff -N gfx/src/ps/mozilla-ps-decoder.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gfx/src/ps/mozilla-ps-decoder.cpp	23 Oct 2006 17:37:10 -0000
@@ -0,0 +1,376 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Christopher Blizzard
+ * <blizzard@mozilla.org>.  Portions created by the Initial Developer
+ * are Copyright (C) 2004 the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#define PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_ENGINE
+
+#include "mozilla-ps-decoder.h"
+#include <pango/pangofc-fontmap.h>
+#include <pango/pangofc-font.h>
+
+#include "nsString.h"
+#include "nsIPersistentProperties2.h"
+#include "nsNetUtil.h"
+#include "nsReadableUtils.h"
+#include "nsICharsetConverterManager.h"
+#include "nsICharRepresentable.h"
+#include "nsCompressedCharMap.h"
+
+#undef DEBUG_CUSTOM_ENCODER
+
+G_DEFINE_TYPE (MozillaPSDecoder, mozilla_ps_decoder, PANGO_TYPE_FC_DECODER)
+
+MozillaPSDecoder *mozilla_ps_decoder_new      (void);
+
+static FcCharSet  *mozilla_ps_decoder_get_charset (PangoFcDecoder *decoder,
+                                                PangoFcFont    *fcfont);
+static PangoGlyph  mozilla_ps_decoder_get_glyph   (PangoFcDecoder *decoder,
+                                                PangoFcFont    *fcfont,
+                                                guint32         wc);
+
+static PangoFcDecoder *mozilla_find_ps_decoder    (FcPattern *pattern,
+                                                gpointer   user_data);
+
+typedef struct _MozillaPSDecoderPrivate MozillaPSDecoderPrivate;
+
+#define MOZILLA_PS_DECODER_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MOZILLA_TYPE_DECODER, MozillaPSDecoderPrivate))
+
+struct _MozillaPSDecoderPrivate {
+    char *family;
+    char *encoder;
+    char *cmap;
+    gboolean is_wide;
+    FcCharSet *charset;
+    nsCOMPtr<nsIUnicodeEncoder> uEncoder;
+};
+
+static nsICharsetConverterManager *gCharsetManager = NULL;
+
+static NS_DEFINE_CID(kCharsetConverterManagerCID,
+                     NS_ICHARSETCONVERTERMANAGER_CID);
+
+// Hash tables that hold the custom encodings and custom cmaps used in
+// various fonts.
+static GHashTable *encoder_hash = NULL;
+static GHashTable *cmap_hash = NULL;
+static GHashTable *wide_hash = NULL;
+
+void
+mozilla_ps_decoder_init (MozillaPSDecoder *decoder)
+{
+}
+
+void
+mozilla_ps_decoder_class_init (MozillaPSDecoderClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    PangoFcDecoderClass *parent_class = PANGO_FC_DECODER_CLASS (klass);
+
+    /*   object_class->finalize = test_finalize; */
+
+    parent_class->get_charset = mozilla_ps_decoder_get_charset;
+    parent_class->get_glyph = mozilla_ps_decoder_get_glyph;
+
+    g_type_class_add_private (object_class, sizeof (MozillaPSDecoderPrivate));
+}
+
+MozillaPSDecoder *
+mozilla_ps_decoder_new(void)
+{
+    return (MozillaPSDecoder *)g_object_new(MOZILLA_TYPE_DECODER, NULL);
+}
+
+#ifdef DEBUG_CUSTOM_ENCODER
+void
+dump_hash(char *key, char *val, void *arg)
+{
+    printf("%s -> %s\n", key, val);
+}
+#endif
+
+/**
+ * mozilla_ps_decoders_init:
+ *
+ * #mozilla_ps_decoders_init:
+ *
+ * This initializes all of the application-specific custom decoders
+ * that Mozilla uses.  This should only be called once during the
+ * lifetime of the application.
+ *
+ * Return value: zero on success, not zero on failure.
+ *
+ **/
+
+int
+mozilla_ps_decoders_init(PangoFontMap *fontmap)
+{
+    static PRBool initialized = PR_FALSE;
+    if (initialized)
+        return 0;
+
+    if (!PANGO_IS_FC_FONT_MAP (fontmap))
+        return -1;
+
+    encoder_hash = g_hash_table_new(g_str_hash, g_str_equal);
+    cmap_hash = g_hash_table_new(g_str_hash, g_str_equal);
+    wide_hash = g_hash_table_new(g_str_hash, g_str_equal);
+
+    PRBool dumb = PR_FALSE;
+    nsCOMPtr<nsIPersistentProperties> props;
+    nsCOMPtr<nsISimpleEnumerator> encodeEnum;
+
+    NS_LoadPersistentPropertiesFromURISpec(getter_AddRefs(props),
+        NS_LITERAL_CSTRING("resource://gre/res/fonts/pangoFontEncoding.properties"));
+
+    if (!props)
+        goto loser;
+
+    // Enumerate the properties in this file and figure out all of the
+    // fonts for which we have custom encodings.
+    props->Enumerate(getter_AddRefs(encodeEnum));
+    if (!encodeEnum)
+        goto loser;
+
+    while (encodeEnum->HasMoreElements(&dumb), dumb) {
+        nsCOMPtr<nsIPropertyElement> prop;
+        encodeEnum->GetNext(getter_AddRefs(prop));
+        if (!prop)
+            goto loser;
+
+        nsCAutoString name;
+        prop->GetKey(name);
+        nsAutoString value;
+        prop->GetValue(value);
+
+        if (!StringBeginsWith(name, NS_LITERAL_CSTRING("encoding."))) {
+            printf("string doesn't begin with encoding?\n");
+            continue;
+        }
+
+        name = Substring(name, 9);
+
+        if (StringEndsWith(name, NS_LITERAL_CSTRING(".ttf"))) {
+            name = Substring(name, 0, name.Length() - 4);
+
+            // Strip off a .wide if it's there.
+            if (StringEndsWith(value, NS_LITERAL_STRING(".wide"))) {
+                g_hash_table_insert(wide_hash, g_strdup(name.get()),
+                                    g_strdup("wide"));
+                value = Substring(value, 0, name.Length() - 5);
+            }
+
+            g_hash_table_insert(encoder_hash,
+                                g_strdup(name.get()),
+                                g_strdup(NS_ConvertUTF16toUTF8(value).get()));
+        }
+        else if (StringEndsWith(name, NS_LITERAL_CSTRING(".ftcmap"))) {
+            name = Substring(name, 0, name.Length() - 7);
+            g_hash_table_insert(cmap_hash,
+                                g_strdup(name.get()),
+                                g_strdup(NS_ConvertUTF16toUTF8(value).get()));
+        }
+        else {
+            printf("unknown suffix used for mapping\n");
+        }
+    }
+
+    pango_fc_font_map_add_decoder_find_func(PANGO_FC_FONT_MAP(fontmap),
+                                            mozilla_find_ps_decoder,
+                                            NULL,
+                                            NULL);
+
+    initialized = PR_TRUE;
+
+#ifdef DEBUG_CUSTOM_ENCODER
+    printf("*** encoders\n");
+    g_hash_table_foreach(encoder_hash, (GHFunc)dump_hash, NULL);
+
+    printf("*** cmaps\n");
+    g_hash_table_foreach(cmap_hash, (GHFunc)dump_hash, NULL);
+#endif
+
+    return 0;
+
+ loser:
+    return -1;
+}
+
+static FcCharSet *
+mozilla_ps_decoder_get_charset (PangoFcDecoder *decoder,
+                             PangoFcFont    *fcfont)
+{
+    MozillaPSDecoderPrivate *priv = MOZILLA_PS_DECODER_GET_PRIVATE(decoder);
+
+    if (priv->charset)
+        return priv->charset;
+
+    // First time this has been accessed.  Populate the charset.
+    priv->charset = FcCharSetCreate();
+
+    if (!gCharsetManager) {
+        CallGetService(kCharsetConverterManagerCID, &gCharsetManager);
+    }
+
+    nsCOMPtr<nsIUnicodeEncoder> encoder;
+    nsCOMPtr<nsICharRepresentable> represent;
+
+    if (!gCharsetManager)
+        goto end;
+
+    gCharsetManager->GetUnicodeEncoderRaw(priv->encoder, getter_AddRefs(encoder));
+    if (!encoder)
+        goto end;
+    
+    encoder->SetOutputErrorBehavior(encoder->kOnError_Replace, nsnull, '?');
+
+    priv->uEncoder = encoder;
+
+    represent = do_QueryInterface(encoder);
+    if (!represent)
+        goto end;
+
+    PRUint32 map[UCS2_MAP_LEN];
+    memset(map, 0, sizeof(map));
+
+    represent->FillInfo(map);
+
+    for (int i = 0; i < NUM_UNICODE_CHARS; i++) {
+        if (IS_REPRESENTABLE(map, i))
+            FcCharSetAddChar(priv->charset, i);
+    }
+
+ end:
+    return priv->charset;
+}
+
+static PangoGlyph
+mozilla_ps_decoder_get_glyph   (PangoFcDecoder *decoder,
+                             PangoFcFont    *fcfont,
+                             guint32         wc)
+{
+    MozillaPSDecoderPrivate *priv = MOZILLA_PS_DECODER_GET_PRIVATE(decoder);
+
+    PangoGlyph retval = 0;
+    PRUnichar inchar = wc;
+    PRInt32 inlen = 1;
+    char outchar[2] = {0,0};
+    PRInt32 outlen = 2;
+
+    priv->uEncoder->Convert(&inchar, &inlen, outchar, &outlen);
+    if (outlen != 1) {
+        printf("Warning: mozilla_ps_decoder_get_glyph doesn't support more than one character conversions.\n");
+        return 0;
+    }
+
+    FT_Face face = pango_fc_font_lock_face(fcfont);
+
+#ifdef DEBUG_CUSTOM_ENCODER
+    char *filename;
+    FcPatternGetString(fcfont->font_pattern, FC_FILE, 0, (FcChar8 **)&filename);
+    printf("filename is %s\n", filename);
+#endif
+
+    // Make sure to set the right charmap before trying to get the
+    // glyph
+    if (priv->cmap) {
+        if (!strcmp(priv->cmap, "mac_roman")) {
+            FT_Select_Charmap(face, ft_encoding_apple_roman);
+        }
+        else if (!strcmp(priv->cmap, "unicode")) {
+            FT_Select_Charmap(face, ft_encoding_unicode);
+        }
+        else {
+            printf("Warning: Invalid charmap entry for family %s\n",
+                   priv->family);
+        }
+    }
+
+    // Standard 8 bit to glyph translation
+    if (!priv->is_wide) {
+        FcChar32 blah = PRUint8(outchar[0]);
+        retval = FT_Get_Char_Index(face, blah);
+#ifdef DEBUG_CUSTOM_ENCODER
+        printf("wc 0x%x outchar[0] 0x%x index 0x%x retval 0x%x face %p\n",
+               wc, outchar[0], blah, retval, (void *)face);
+#endif
+    }
+    else {
+        printf("Warning: We don't support .wide fonts!\n");
+        retval = 0;
+    }
+
+    pango_fc_font_unlock_face(fcfont);
+
+    return retval;
+}
+
+static PangoFcDecoder *
+mozilla_find_ps_decoder (FcPattern *pattern, gpointer user_data)
+{
+    // Compare the family name of the font that's been opened to see
+    // if we have a custom decoder.
+    const char *orig = NULL;
+    FcPatternGetString(pattern, FC_FAMILY, 0, (FcChar8 **)&orig);
+
+    nsCAutoString family;
+    family.Assign(orig);
+
+    family.StripWhitespace();
+    ToLowerCase(family);
+
+    char *encoder = (char *)g_hash_table_lookup(encoder_hash, family.get());
+    if (!encoder)
+        return NULL;
+
+    MozillaPSDecoder *decoder = mozilla_ps_decoder_new();
+
+    MozillaPSDecoderPrivate *priv = MOZILLA_PS_DECODER_GET_PRIVATE(decoder);
+
+    priv->family = g_strdup(family.get());
+    priv->encoder = g_strdup(encoder);
+
+    char *cmap = (char *)g_hash_table_lookup(cmap_hash, family.get());
+    if (cmap)
+        priv->cmap = g_strdup(cmap);
+
+    char *wide = (char *)g_hash_table_lookup(wide_hash, family.get());
+    if (wide)
+        priv->is_wide = TRUE;
+
+    return PANGO_FC_DECODER(decoder);
+}
Index: gfx/src/ps/mozilla-ps-decoder.h
===================================================================
RCS file: gfx/src/ps/mozilla-ps-decoder.h
diff -N gfx/src/ps/mozilla-ps-decoder.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gfx/src/ps/mozilla-ps-decoder.h	23 Oct 2006 17:37:10 -0000
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Christopher Blizzard
+ * <blizzard@mozilla.org>.  Portions created by the Initial Developer
+ * are Copyright (C) 2004 the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef _MOZILLA_PS_DECODER_H
+#define _MOZILLA_PS_DECODER_H
+
+#include <pango/pangofc-decoder.h>
+
+G_BEGIN_DECLS
+
+#define MOZILLA_TYPE_DECODER (mozilla_ps_decoder_get_type())
+#define MOZILLA_PS_DECODER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), MOZILLA_TYPE_DECODER, MozillaPSDecoder))
+#define MOZILLA_IS_DECODER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), MOZILLA_TYPE_DECODER))
+
+typedef struct _MozillaPSDecoder      MozillaPSDecoder;
+typedef struct _MozillaPSDecoderClass MozillaPSDecoderClass;
+
+#define MOZILLA_PS_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MOZILLA_TYPE_DECODER, MozillaPSDecoderClass))
+#define MOZILLA_IS_DECODER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MOZILLA_TYPE_DECODER))
+#define MOZILLA_PS_DECODER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MOZILLA_TYPE_DECODER, MozillaPSDecoderClass))
+
+struct _MozillaPSDecoder
+{
+  PangoFcDecoder parent_instance;
+};
+
+struct _MozillaPSDecoderClass
+{
+  PangoFcDecoderClass parent_class;
+};
+
+GType           mozilla_ps_decoder_get_type (void);
+int             mozilla_ps_decoders_init    (PangoFontMap *fontmap);
+
+G_END_DECLS
+
+#endif /*_MOZILLA_PS_DECODER_H */
Index: gfx/src/ps/nsDeviceContextPS.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsDeviceContextPS.cpp,v
retrieving revision 1.73
diff -u -p -d -r1.73 nsDeviceContextPS.cpp
--- gfx/src/ps/nsDeviceContextPS.cpp	21 May 2005 15:33:08 -0000	1.73
+++ gfx/src/ps/nsDeviceContextPS.cpp	23 Oct 2006 17:37:10 -0000
@@ -58,12 +58,15 @@
 #include "nsIPref.h"
 #include "nsString.h"
 #include "nsFontMetricsPS.h"
+#ifdef MOZ_ENABLE_PANGO
+#include "nsFontMetricsPSPango.h"
+#endif
 #include "nsPostScriptObj.h"
 #include "nspr.h"
 #include "nsILanguageAtomService.h"
 #include "nsPrintJobPS.h"
 #include "nsPrintJobFactoryPS.h"
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
 #include "nsType1.h"
 #endif
 
@@ -223,7 +226,7 @@ nsDeviceContextPS::InitDeviceContextPS(n
  
   nsresult rv;
   nsCOMPtr<nsIPref> pref(do_GetService(NS_PREF_CONTRACTID, &rv));
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
   if (NS_SUCCEEDED(rv)) {
       rv = pref->GetBoolPref("font.FreeType2.printing", &mFTPEnable);
       if (NS_FAILED(rv))
@@ -469,7 +472,7 @@ NS_IMETHODIMP nsDeviceContextPS::EndDocu
       NS_ASSERTION(submitFP, "No print job submission handle");
 
       // Start writing the print job to the job handler
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
       mPSObj->write_prolog(submitFP, mFTPEnable);
 #else 
       mPSObj->write_prolog(submitFP);
@@ -550,15 +553,52 @@ public:
   virtual nsresult CreateFontMetricsInstance(nsIFontMetrics** aResult);
 };
 
+#if defined(MOZ_ENABLE_PANGO)
+PRBool
+NS_IsPangoEnabled(void)
+{
+    static PRBool beenHere;
+    static PRBool pangoEnabled;
+
+    if (!beenHere) {
+        beenHere = PR_TRUE;
+
+        char *val = PR_GetEnv("MOZ_DISABLE_PANGO");
+        pangoEnabled = !(val);
+
+        if (pangoEnabled) {
+            nsCOMPtr<nsIPref> prefService = do_GetService(NS_PREF_CONTRACTID);
+            if (prefService)
+                prefService->SetDefaultCharPref("general.useragent.extra.pango",
+                                                "pango-text");
+        }
+    }
+
+    return pangoEnabled;
+}
+#endif
 
 nsresult nsFontCachePS::CreateFontMetricsInstance(nsIFontMetrics** aResult)
 {
   NS_PRECONDITION(aResult, "null out param");
-  nsIFontMetrics *fm = new nsFontMetricsPS();
-  if (!fm)
-    return NS_ERROR_OUT_OF_MEMORY;
-  NS_ADDREF(fm);
-  *aResult = fm;
+#ifdef MOZ_ENABLE_PANGO
+  if (NS_IsPangoEnabled())
+  {
+    nsIFontMetrics *fm = new nsFontMetricsPSPango();
+    if (!fm)
+      return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(fm);
+    *aResult = fm;
+  }
+  else
+#endif
+  {
+    nsIFontMetrics *fm = new nsFontMetricsPS();
+    if (!fm)
+      return NS_ERROR_OUT_OF_MEMORY;
+    NS_ADDREF(fm);
+    *aResult = fm;
+  }
   return NS_OK;
 }
 
Index: gfx/src/ps/nsFontMetricsPS.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsFontMetricsPS.cpp,v
retrieving revision 1.57.16.2
diff -u -p -d -r1.57.16.2 nsFontMetricsPS.cpp
--- gfx/src/ps/nsFontMetricsPS.cpp	7 May 2006 02:01:25 -0000	1.57.16.2
+++ gfx/src/ps/nsFontMetricsPS.cpp	23 Oct 2006 17:37:11 -0000
@@ -461,6 +461,239 @@ nsFontMetricsPS :: GetStringWidth(const 
   return NS_OK;
 }
 
+nsresult
+nsFontMetricsPS::DrawString(const char *aString, PRUint32 aLength,
+                               nscoord aX, nscoord aY,
+                               const nscoord* aSpacing,
+                               nsRenderingContextPS *aContext)
+{
+  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
+  // When FT2 printing is enabled, we don't need to set langgroup
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+  if (!NS_REINTERPRET_CAST(nsDeviceContextPS *, GetDeviceContext())->mFTPEnable) {
+#endif
+    nsCOMPtr<nsIAtom> langGroup;
+    GetLangGroup(getter_AddRefs(langGroup));
+    psObj->setlanggroup(langGroup);
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+  }
+#endif
+
+  if (aLength == 0)
+    return NS_OK;
+  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], Font(), this);
+  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
+  fontPS->SetupFont(aContext);
+
+  PRUint32 i, start = 0;
+  for (i=0; i<aLength; i++) {
+    nsFontPS* fontThisChar;
+    fontThisChar = nsFontPS::FindFont(aString[i], Font(), this);
+    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
+    if (fontThisChar != fontPS) {
+      // draw text up to this point
+      aX += DrawString(aString+start, i-start, aX, aY, fontPS, 
+                       aSpacing?aSpacing+start:nsnull, aContext);
+      start = i;
+
+      // setup for following text
+      fontPS = fontThisChar;
+      fontPS->SetupFont(aContext);
+    }
+  }
+
+  // draw the last part
+  if (aLength-start)
+    DrawString(aString+start, aLength-start, aX, aY, fontPS, 
+               aSpacing?aSpacing+start:nsnull, aContext);
+
+  return NS_OK;
+}
+
+nsresult
+nsFontMetricsPS::DrawString(const PRUnichar* aString, PRUint32 aLength,
+                               nscoord aX, nscoord aY,
+                               PRInt32 aFontID,
+                               const nscoord* aSpacing,
+                               nsRenderingContextPS *aContext)
+{
+  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+  // When FT2 printing is enabled, we don't need to set langgroup
+  if (!NS_REINTERPRET_CAST(nsDeviceContextPS *, GetDeviceContext())->mFTPEnable) {
+#endif
+    nsCOMPtr<nsIAtom> langGroup = nsnull;
+    GetLangGroup(getter_AddRefs(langGroup));
+    psObj->setlanggroup(langGroup);
+#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
+  }
+#endif
+
+  /* build up conversion table */
+  psObj->preshow(aString, aLength);
+
+  if (aLength == 0)
+    return NS_OK;
+  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], Font(), this);
+  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
+  fontPS->SetupFont(aContext);
+
+  PRUint32 i, start = 0;
+  for (i=0; i<aLength; i++) {
+    nsFontPS* fontThisChar;
+    fontThisChar = nsFontPS::FindFont(aString[i], Font(), this);
+    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
+    if (fontThisChar != fontPS) {
+      // draw text up to this point
+      aX += DrawString(aString+start, i-start, aX, aY, fontPS, 
+                       aSpacing?aSpacing+start:nsnull, aContext);
+      start = i;
+
+      // setup for following text
+      fontPS = fontThisChar;
+      fontPS->SetupFont(aContext);
+    }
+  }
+
+  // draw the last part
+  if (aLength-start)
+    DrawString(aString+start, aLength-start, aX, aY, fontPS, 
+               aSpacing?aSpacing+start:nsnull, aContext);
+
+  return NS_OK;
+}
+
+PRInt32
+nsFontMetricsPS::DrawString(const char *aString, PRUint32 aLength,
+                        nscoord aX, nscoord aY, nsFontPS* aFontPS,
+                        const nscoord* aSpacing,
+			nsRenderingContextPS *aContext)
+{
+  nscoord width = 0;
+  PRInt32 x = aX;
+  PRInt32 y = aY;
+
+  PRInt32 dxMem[500];
+  PRInt32* dx0 = 0;
+  if (aSpacing) {
+    dx0 = dxMem;
+    if (aLength > 500) {
+      dx0 = new PRInt32[aLength];
+      NS_ENSURE_TRUE(dx0, NS_ERROR_OUT_OF_MEMORY);
+    }
+    aContext->GetTranMatrix()->ScaleXCoords(aSpacing, aLength, dx0);
+  }
+
+  aContext->GetTranMatrix()->TransformCoord(&x, &y);
+  width = aFontPS->DrawString(aContext, x, y, aString, aLength);
+
+  if ((aSpacing) && (dx0 != dxMem)) {
+    delete [] dx0;
+  }
+
+  return width;
+}
+
+
+PRInt32
+nsFontMetricsPS::DrawString(const PRUnichar* aString, PRUint32 aLength,
+                                 nscoord &aX, nscoord &aY, nsFontPS* aFontPS,
+                                 const nscoord* aSpacing,
+				 nsRenderingContextPS *aContext)
+{
+  nscoord width = 0;
+  PRInt32 x = aX;
+  PRInt32 y = aY;
+
+  if (aSpacing) {
+    // Slow, but accurate rendering
+    const PRUnichar* end = aString + aLength;
+    while (aString < end){
+      x = aX;
+      y = aY;
+      aContext->GetTranMatrix()->TransformCoord(&x, &y);
+      aFontPS->DrawString(aContext, x, y, aString, 1);
+      aX += *aSpacing++;
+      aString++;
+    }
+    width = aX;
+  } else {
+    aContext->GetTranMatrix()->TransformCoord(&x, &y);
+    width = aFontPS->DrawString(aContext, x, y, aString, aLength);
+  }
+
+  return width;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPS::GetTextDimensions(const char*       aString,
+                                        PRInt32           aLength,
+                                        PRInt32           aAvailWidth,
+                                        PRInt32*          aBreaks,
+                                        PRInt32           aNumBreaks,
+                                        nsTextDimensions& aDimensions,
+                                        PRInt32&          aNumCharsFit,
+                                        nsTextDimensions& aLastWordDimensions,
+                                        PRInt32*          aFontID)
+{
+  NS_NOTYETIMPLEMENTED("nsFontMetricsPS::GetTextDimensions");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPS::GetTextDimensions(const PRUnichar*  aString,
+                                        PRInt32           aLength,
+                                        PRInt32           aAvailWidth,
+                                        PRInt32*          aBreaks,
+                                        PRInt32           aNumBreaks,
+                                        nsTextDimensions& aDimensions,
+                                        PRInt32&          aNumCharsFit,
+                                        nsTextDimensions& aLastWordDimensions,
+                                        PRInt32*          aFontID)
+{
+  NS_NOTYETIMPLEMENTED("nsFontMetricsPS::GetTextDimensions");
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPS :: GetTextDimensions(const char* aString, PRUint32 aLength,
+                                          nsTextDimensions& aDimensions)
+{
+  GetStringWidth(aString, aDimensions.width, aLength);
+  GetMaxAscent(aDimensions.ascent);
+  GetMaxDescent(aDimensions.descent);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPS :: GetTextDimensions(const PRUnichar* aString, PRUint32 aLength,
+                                          nsTextDimensions& aDimensions, PRInt32* aFontID)
+{
+  GetStringWidth(aString, aDimensions.width, aLength);
+  //XXX temporary - bug 96609
+  GetMaxAscent(aDimensions.ascent);
+  GetMaxDescent(aDimensions.descent);
+  return NS_OK;
+}
+
+nsresult
+nsFontMetricsPS::GetBoundingMetrics(const char*        aString,
+                                     PRUint32           aLength,
+                                     nsBoundingMetrics& aBoundingMetrics)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+nsresult
+nsFontMetricsPS::GetBoundingMetrics(const PRUnichar*   aString,
+                                     PRUint32           aLength,
+                                     nsBoundingMetrics &aBoundingMetrics,
+                                     PRInt32 *aFontID)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
 nsFontPS*
 nsFontPS::FindFont(char aChar, const nsFont& aFont, 
                    nsFontMetricsPS* aFontMetrics)
@@ -1128,23 +1361,38 @@ nsFontPSXft::DrawString(nsRenderingConte
   PRUint32 start = 0;
   PRUint32 i;
 
+  FT_Face face = getFTFace();
+  if (!face) {
+    NS_WARNING("Failed to get FT Face in nsFontPSXft::DrawString\n");
+    return 0;
+  }
+
+  nsValueArray glyphs(PR_UINT16_MAX);
+
   // XXX : ignore surrogate pairs for now
-  nsString *subSet = mPSFontGenerator->GetSubset();
   for (i = 0; i < aLength; ++i) {
-    currSubFont = mPSFontGenerator->AddToSubset(aString[i]);
+    PRUint32 glyph = FT_Get_Char_Index(face, aString[i]);
+    currSubFont = mPSFontGenerator->AddToGlyphSubset(glyph);
+
+    // Check if we need to render the current string
     if (prevSubFont != currSubFont) {
-      if (prevSubFont != -1)
-        psObj->show(&aString[start], i - start, *subSet, prevSubFont);
+      if (prevSubFont != -1) {
+        psObj->show(&glyphs, mPSFontGenerator, prevSubFont);
+      }
       NS_ASSERTION(!mFontNameBase.IsEmpty(),
                   "font base name shouldn't be empty");
       psObj->setfont(mFontNameBase, mHeight, currSubFont);
       prevSubFont = currSubFont;
       start = i;
+      glyphs.Clear();
     }
+
+    glyphs.AppendValue(glyph);
   }
 
-  if (prevSubFont != -1)
-    psObj->show(&aString[start], i - start, *subSet, prevSubFont); 
+  if (prevSubFont != -1) {
+    psObj->show(&glyphs, mPSFontGenerator, prevSubFont);
+  }
   
   return GetWidth(aString, aLength);
 }
@@ -2278,10 +2526,13 @@ nsFontPSFreeType::GetBoundingMetrics(con
 // Implementation of nsPSFontGenerator
 nsPSFontGenerator::nsPSFontGenerator()
 {
+  mGlyphSubset = new nsValueArray(PR_UINT16_MAX, 40);
 }
 
 nsPSFontGenerator::~nsPSFontGenerator()
 {
+  if (mGlyphSubset)
+    delete mGlyphSubset;
 }
 
 void nsPSFontGenerator::GeneratePSFont(FILE* aFile)
@@ -2289,24 +2540,29 @@ void nsPSFontGenerator::GeneratePSFont(F
   NS_ERROR("should never call nsPSFontGenerator::GeneratePSFont");
 }
 
-// Add a Unicode character to mSubset which will be divided into 
-// multiple chunks (subfonts) of 255 (kSubFontSize) characters each. 
-// Each chunk will be converted to a Type 1 font. Return the index of 
-// a subfont (chunk) this character belongs to.
+// Add a glyph offset to mSubset which will be divided into multiple
+// chunks (subfonts) of 255 (kSubFontSize) glyphs each.  Each chunk
+// will then be converted into a Type 1 font.  Return the index of a
+// subfont (chunk) this glyph belongs to.
 PRInt32
-nsPSFontGenerator::AddToSubset(PRUnichar aChar)
+nsPSFontGenerator::AddToGlyphSubset(PRUint32 aGlyph)
 {
-  PRInt32 index = mSubset.FindChar(aChar);
-  if (index == kNotFound) {
-    mSubset.Append(aChar);
-    index = mSubset.Length() - 1;
+  nsValueArrayIndex index = mGlyphSubset->IndexOf(aGlyph);
+  if (index == NSVALUEARRAY_INVALID) {
+    mGlyphSubset->AppendValue(aGlyph);
+    index = mGlyphSubset->Count() - 1;
   }
+
   return index / kSubFontSize;
 }
 
-nsString *nsPSFontGenerator::GetSubset()
+PRInt32
+nsPSFontGenerator::InSubsetIndexOf(PRUint32 aGlyph)
 {
-  return &mSubset;
+  nsValueArrayIndex index = mGlyphSubset->IndexOf(aGlyph);
+  if (index == NSVALUEARRAY_INVALID)
+    return 0;
+  return (index % kSubFontSize) + 1;
 }
 
 #ifdef MOZ_ENABLE_XFT
@@ -2353,8 +2609,8 @@ void nsXftType1Generator::GeneratePSFont
   }
 
   int wmode = 0;
-  if (!mSubset.IsEmpty())
-    FT2SubsetToType1FontSet(face, mSubset, wmode, aFile);
+  if (mGlyphSubset->Count())
+    FT2SubsetToType1FontSet(face, mGlyphSubset, wmode, aFile);
 }
 
 #else
@@ -2402,8 +2658,8 @@ void nsFT2Type1Generator::GeneratePSFont
     return;
  
   int wmode = 0;
-  if (!mSubset.IsEmpty())
-    FT2SubsetToType1FontSet(face, mSubset, wmode, aFile);
+  if (mGlyphSubset->Count())
+    FT2SubsetToType1FontSet(face, mGlyphSubset, wmode, aFile);
 }
 
 #endif //MOZ_ENABLE_FREETYPE2
Index: gfx/src/ps/nsFontMetricsPS.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsFontMetricsPS.h,v
retrieving revision 1.31
diff -u -p -d -r1.31 nsFontMetricsPS.h
--- gfx/src/ps/nsFontMetricsPS.h	28 Jun 2005 18:29:10 -0000	1.31
+++ gfx/src/ps/nsFontMetricsPS.h	23 Oct 2006 17:37:11 -0000
@@ -66,6 +66,7 @@
 #endif
 #include "nsVoidArray.h"
 #include "nsHashtable.h"
+#include "nsValueArray.h"
 
 class nsPSFontGenerator;
 class nsDeviceContextPS;
@@ -108,6 +109,65 @@ public:
   NS_IMETHOD  GetFontHandle(nsFontHandle &aHandle);
   NS_IMETHOD  GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength);
   NS_IMETHOD  GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength);
+
+    NS_IMETHOD       GetTextDimensions(const char* aString,
+                                       PRUint32 aLength,
+                                       nsTextDimensions& aDimensions);
+    NS_IMETHOD       GetTextDimensions(const PRUnichar* aString,
+                                       PRUint32 aLength,
+                                       nsTextDimensions& aDimensions, 
+                                       PRInt32* aFontID);
+    NS_IMETHOD       GetTextDimensions(const char*         aString,
+                                       PRInt32             aLength,
+                                       PRInt32             aAvailWidth,
+                                       PRInt32*            aBreaks,
+                                       PRInt32             aNumBreaks,
+                                       nsTextDimensions&   aDimensions,
+                                       PRInt32&            aNumCharsFit,
+                                       nsTextDimensions&   aLastWordDimensions,
+                                       PRInt32*            aFontID);
+    NS_IMETHOD       GetTextDimensions(const PRUnichar*    aString,
+                                       PRInt32             aLength,
+                                       PRInt32             aAvailWidth,
+                                       PRInt32*            aBreaks,
+                                       PRInt32             aNumBreaks,
+                                       nsTextDimensions&   aDimensions,
+                                       PRInt32&            aNumCharsFit,
+                                       nsTextDimensions&   aLastWordDimensions,
+                                       PRInt32*            aFontID);
+#ifdef MOZ_MATHML
+    NS_IMETHOD       GetBoundingMetrics(const char *aString, PRUint32 aLength,
+                                        nsBoundingMetrics &aBoundingMetrics);
+    NS_IMETHOD       GetBoundingMetrics(const PRUnichar *aString,
+                                        PRUint32 aLength,
+                                        nsBoundingMetrics &aBoundingMetrics,
+                                        PRInt32 *aFontID);
+#endif /* MOZ_MATHML */
+
+  NS_IMETHOD DrawString(const char *aString, PRUint32 aLength,
+                        nscoord aX, nscoord aY,
+                        const nscoord* aSpacing,
+			nsRenderingContextPS *aContext);
+  NS_IMETHOD DrawString(const PRUnichar *aString, PRUint32 aLength,
+                        nscoord aX, nscoord aY,
+                        PRInt32 aFontID,
+                        const nscoord* aSpacing,
+			nsRenderingContextPS *aContext);
+
+protected:
+  PRInt32 DrawString(const char *aString, PRUint32 aLength,
+                        nscoord aX, nscoord aY, nsFontPS* aFontPS,
+                        const nscoord* aSpacing,
+			nsRenderingContextPS *aContext);
+  PRInt32 DrawString(const PRUnichar *aString, PRUint32 aLength,
+                        nscoord &aX, nscoord &aY, nsFontPS* aFontPS,
+                        const nscoord* aSpacing,
+			nsRenderingContextPS *aContext);
+
+public:
+
+  virtual PRUint32    GetHints     (void) { return 0; }
+
   
   inline void SetXHeight(nscoord aXHeight) { mXHeight = aXHeight; };
   inline void SetSuperscriptOffset(nscoord aSuperscriptOffset) { mSuperscriptOffset = aSuperscriptOffset; };
@@ -455,16 +515,14 @@ public:
   nsPSFontGenerator();
   virtual ~nsPSFontGenerator();
   virtual void  GeneratePSFont(FILE* aFile);
-  PRInt32  AddToSubset(PRUnichar aChar);
-  nsString *GetSubset();
+  PRInt32  AddToGlyphSubset(PRUint32 aGlyph);
+  PRInt32  InSubsetIndexOf(PRUint32 aGlyph);
 
   // 256 (PS type 1 encoding vector size) - 1 (1 is for mandatory /.notdef)
   const static PRUint16 kSubFontSize; 
 
 protected:
-  // XXX To support non-BMP characters, we may have to use 
-  // nsValueArray with PRUint32
-  nsString mSubset;
+  nsValueArray *mGlyphSubset;
 };
 
 
Index: gfx/src/ps/nsFontMetricsPSPango.cpp
===================================================================
RCS file: gfx/src/ps/nsFontMetricsPSPango.cpp
diff -N gfx/src/ps/nsFontMetricsPSPango.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gfx/src/ps/nsFontMetricsPSPango.cpp	23 Oct 2006 17:37:13 -0000
@@ -0,0 +1,2107 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is Christopher Blizzard
+ * <blizzard@mozilla.org>.  Portions created by the Initial Developer
+ * are Copyright (C) 2004 the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Christopher Blizzard <blizzard@mozilla.org>
+ *   Behdad Esfahbod <behdad@behdad.org>
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <strings.h>
+#include "nsFont.h"
+#include "nsIDeviceContext.h"
+#include "nsICharsetConverterManager.h"
+#include "nsIPref.h"
+#include "nsServiceManagerUtils.h"
+
+#define PANGO_ENABLE_BACKEND
+#define PANGO_ENABLE_ENGINE
+
+#include "nsFontMetricsPSPango.h"
+#include "nsRenderingContextPS.h"
+#include "nsDeviceContextPS.h"
+#include "nsFontConfigUtils.h"
+
+#include "nsPrintfCString.h"
+#include "nsUnicharUtils.h"
+#include "nsQuickSort.h"
+#include "nsFontConfigUtils.h"
+
+#include <fontconfig/fontconfig.h>
+#include <pango/pangoft2.h>
+#include <freetype/tttables.h>
+#include "nsType1.h"
+
+#include "mozilla-ps-decoder.h"
+
+#define FORCE_PR_LOG
+#include "prlog.h"
+
+// Globals
+
+static PRLogModuleInfo            *gPangoFontLog;
+static int                         gNumInstances;
+
+
+static void
+default_substitute (FcPattern *pattern,
+                    gpointer   data)
+{
+  FcPatternDel (pattern, FC_HINTING);
+  FcPatternAddBool (pattern, FC_HINTING, 0);
+}
+
+static PangoFontMap *
+get_fontmap (void)
+{
+  static PangoFontMap               *fontmap = NULL;
+
+  if (!fontmap) {
+    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);
+  }
+
+  return fontmap;
+}
+
+static PangoContext *
+get_context (void)
+{
+  return pango_ft2_font_map_create_context ((PangoFT2FontMap *) get_fontmap ());
+}
+
+// Defines
+
+// This is the scaling factor that we keep fonts limited to against
+// the display size.  If a pixel size is requested that is more than
+// this factor larger than the height of the display, it's clamped to
+// that value instead of the requested size.
+#define FONT_MAX_FONT_SCALE 2
+
+static NS_DEFINE_CID(kCharsetConverterManagerCID,
+                     NS_ICHARSETCONVERTERMANAGER_CID);
+
+#ifdef DEBUG
+#define DUMP_PRUNICHAR(ustr, ulen) for (PRUint32 llen=0;llen<ulen;llen++) \
+                                      printf("0x%x ", ustr[llen]); \
+                                   printf("\n");
+#endif
+
+// rounding and truncation functions for a Freetype floating point number 
+// (FT26Dot6) stored in a 32bit integer with high 26 bits for the integer
+// part and low 6 bits for the fractional part. 
+#define MOZ_FT_ROUND(x) (((x) + 32) & ~63) // 63 = 2^6 - 1
+#define MOZ_FT_TRUNC(x) ((x) >> 6)
+#define CONVERT_DESIGN_UNITS_TO_PIXELS(v, s) \
+        MOZ_FT_TRUNC(MOZ_FT_ROUND(FT_MulFix((v) , (s))))
+
+// Static function decls
+
+static PangoLanguage *GetPangoLanguage(nsIAtom *aLangGroup);
+
+static void   FreeGlobals    (void);
+
+static PangoStyle  CalculateStyle  (PRUint8 aStyle);
+static PangoWeight CalculateWeight (PRUint16 aWeight);
+
+static nsresult    EnumFontsPango   (nsIAtom* aLangGroup, const char* aGeneric,
+                                     PRUint32* aCount, PRUnichar*** aResult);
+static int         CompareFontNames (const void* aArg1, const void* aArg2,
+                                     void* aClosure);
+
+nsFontMetricsPSPango::nsFontMetricsPSPango()
+{
+    if (!gPangoFontLog)
+        gPangoFontLog = PR_NewLogModule("PangoFont");
+
+    gNumInstances++;
+
+    mPangoFontDesc = nsnull;
+    mPangoContext = nsnull;
+    mLTRPangoContext = nsnull;
+    mRTLPangoContext = nsnull;
+    mPangoAttrList = nsnull;
+    mIsRTL = PR_FALSE;
+    mPangoSpaceWidth = 0;
+
+    static PRBool initialized = PR_FALSE;
+    if (initialized)
+        return;
+
+    // Initialized the custom decoders
+    if (!mozilla_ps_decoders_init(get_fontmap ()))
+        initialized = PR_TRUE;
+}
+
+nsFontMetricsPSPango::~nsFontMetricsPSPango()
+{
+    if (mDeviceContext)
+        mDeviceContext->FontMetricsDeleted(this);
+
+    if (mPangoFontDesc)
+        pango_font_description_free(mPangoFontDesc);
+
+    if (mLTRPangoContext)
+        g_object_unref(mLTRPangoContext);
+
+    if (mRTLPangoContext)
+        g_object_unref(mRTLPangoContext);
+
+    if (mPangoAttrList)
+        pango_attr_list_unref(mPangoAttrList);
+
+    // XXX clean up all the pango objects
+
+    if (--gNumInstances == 0)
+        FreeGlobals();
+}
+
+
+NS_IMPL_ISUPPORTS1(nsFontMetricsPSPango, nsIFontMetrics)
+
+// nsIFontMetrics impl
+
+NS_IMETHODIMP
+nsFontMetricsPSPango::Init(const nsFont& aFont, nsIAtom* aLangGroup,
+                         nsIDeviceContext *aContext)
+{
+    mFont = aFont;
+    mLangGroup = aLangGroup;
+
+    // Hang on to the device context
+    mDeviceContext = aContext;
+    
+    mPointSize = NSTwipsToFloatPoints(mFont.size);
+
+    // enumerate over the font names passed in
+    mFont.EnumerateFamilies(nsFontMetricsPSPango::EnumFontCallback, this);
+
+    nsCOMPtr<nsIPref> prefService;
+    prefService = do_GetService(NS_PREF_CONTRACTID);
+    if (!prefService)
+        return NS_ERROR_FAILURE;
+        
+    nsXPIDLCString value;
+    const char* langGroup;
+    mLangGroup->GetUTF8String(&langGroup);
+
+    // Set up the default font name if it's not set
+    if (!mGenericFont) {
+        nsCAutoString name("font.default.");
+        name.Append(langGroup);
+        prefService->CopyCharPref(name.get(), getter_Copies(value));
+
+        if (value.get())
+            mDefaultFont = value.get();
+        else
+            mDefaultFont = "serif";
+        
+        mGenericFont = &mDefaultFont;
+    }
+
+    // set up the minimum sizes for fonts
+    if (mLangGroup) {
+        nsCAutoString name("font.min-size.");
+
+        if (mGenericFont->Equals("monospace"))
+            name.Append("fixed");
+        else
+            name.Append("variable");
+
+        name.Append(char('.'));
+        name.Append(langGroup);
+
+        PRInt32 minimumInt = 0;
+        float minimum;
+        nsresult res;
+        res = prefService->GetIntPref(name.get(), &minimumInt);
+        if (NS_FAILED(res))
+            prefService->GetDefaultIntPref(name.get(), &minimumInt);
+
+        if (minimumInt < 0)
+            minimumInt = 0;
+
+        minimum = minimumInt;
+
+        // The minimum size is specified in pixels, not in points.
+        // Convert the size from pixels to points.
+        minimum = NSTwipsToFloatPoints(NSFloatPixelsToTwips(minimum, mDeviceContext->DevUnitsToAppUnits()));
+        if (mPointSize < minimum)
+            mPointSize = minimum;
+    }
+
+    // Make sure that the pixel size is at least greater than zero
+    if (mPointSize < 1) {
+#ifdef DEBUG
+        printf("*** Warning: nsFontMetricsPSPango created with point size %f\n",
+               mPointSize);
+#endif
+        mPointSize = 1;
+    }
+
+    nsresult rv = RealizeFont();
+    if (NS_FAILED(rv))
+        return rv;
+
+    // Cache font metrics for the 'x' character
+    return CacheFontMetrics();
+}
+
+nsresult
+nsFontMetricsPSPango::CacheFontMetrics(void)
+{
+    // Get our scale factor
+    float f;
+    float val;
+    f = mDeviceContext->DevUnitsToAppUnits();
+
+    mPangoAttrList = pango_attr_list_new();
+
+    GList *items = pango_itemize(mPangoContext,
+                                 "a", 0, 1, mPangoAttrList, NULL);
+
+    if (!items)
+        return NS_ERROR_FAILURE;
+
+    guint nitems = g_list_length(items);
+    if (nitems != 1)
+        return NS_ERROR_FAILURE;
+
+    PangoItem *item = (PangoItem *)items->data;
+    PangoFcFont  *fcfont = PANGO_FC_FONT(item->analysis.font);
+    if (!fcfont)
+        return NS_ERROR_FAILURE;
+
+    // Get our font face
+    FT_Face face;
+    face = pango_fc_font_lock_face(fcfont);
+    if (!face)
+    	return NS_ERROR_NOT_AVAILABLE;
+    	
+    TT_OS2 *os2;
+    os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
+
+    // mEmHeight (size in pixels of EM height)
+    int size;
+    if (FcPatternGetInteger(fcfont->font_pattern, FC_PIXEL_SIZE, 0, &size) !=
+        FcResultMatch) {
+        size = 12;
+    }
+    mEmHeight = PR_MAX(1, nscoord(size * f));
+
+    // mMaxAscent
+    val = MOZ_FT_TRUNC(face->size->metrics.ascender);
+    mMaxAscent = NSToIntRound(val * f);
+
+    // mMaxDescent
+    val = -MOZ_FT_TRUNC(face->size->metrics.descender);
+    mMaxDescent = NSToIntRound(val * f);
+
+    nscoord lineHeight = mMaxAscent + mMaxDescent;
+
+    // mLeading (needs ascent and descent and EM height) 
+    if (lineHeight > mEmHeight)
+        mLeading = lineHeight - mEmHeight;
+    else
+        mLeading = 0;
+
+    // mMaxHeight (needs ascent and descent)
+    mMaxHeight = lineHeight;
+
+    // mEmAscent (needs maxascent, EM height, ascent and descent)
+    mEmAscent = nscoord(mMaxAscent * mEmHeight / lineHeight);
+
+    // mEmDescent (needs EM height and EM ascent
+    mEmDescent = mEmHeight - mEmAscent;
+
+    // mMaxAdvance
+    val = MOZ_FT_TRUNC(face->size->metrics.max_advance);
+    mMaxAdvance = NSToIntRound(val * f);
+
+    // mPangoSpaceWidth
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+    pango_layout_set_text(layout, " ", 1);
+    int pswidth, psheight;
+    pango_layout_get_size(layout, &pswidth, &psheight);
+    mPangoSpaceWidth = pswidth;
+    g_object_unref(layout);
+
+    // mSpaceWidth (width of a space)
+    nscoord tmpWidth;
+    GetWidth(" ", 1, tmpWidth);
+    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);
+    mAveCharWidth = tmpWidth;
+
+    // mXHeight (height of an 'x' character)
+    if (pango_fc_font_has_char(fcfont, 'x')) {
+        PangoRectangle rect;
+        PangoGlyph glyph = pango_fc_font_get_glyph (fcfont, 'x');
+        pango_font_get_glyph_extents (PANGO_FONT (fcfont), glyph, &rect, NULL);
+        mXHeight = NSToIntRound(rect.height * f / PANGO_SCALE);
+    }
+    else {
+        // 56% of ascent, best guess for non-true type or asian fonts
+        mXHeight = nscoord(((float)mMaxAscent) * 0.56 * f);
+    }
+
+    // mUnderlineOffset (offset for underlines)
+    val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_position,
+                                         face->size->metrics.y_scale);
+    if (val) {
+        mUnderlineOffset = NSToIntRound(val * f);
+    }
+    else {
+        mUnderlineOffset =
+            -NSToIntRound(PR_MAX(1, floor(0.1 *
+                MOZ_FT_TRUNC(face->size->metrics.height) + 0.5)) * f);
+    }
+
+    // mUnderlineSize (thickness of an underline)
+    val = CONVERT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness,
+                                         face->size->metrics.y_scale);
+    if (val) {
+        mUnderlineSize = nscoord(PR_MAX(f, NSToIntRound(val * f)));
+    }
+    else {
+        mUnderlineSize =
+            NSToIntRound(PR_MAX(1,
+               floor(0.05 * MOZ_FT_TRUNC(face->size->metrics.height) + 0.5)) * f);
+    }
+
+    // mSuperscriptOffset
+    if (os2 && os2->ySuperscriptYOffset) {
+        val = CONVERT_DESIGN_UNITS_TO_PIXELS(os2->ySuperscriptYOffset,
+                                             face->size->metrics.y_scale);
+        mSuperscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f)));
+    }
+    else {
+        mSuperscriptOffset = mXHeight;
+    }
+
+    // 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;
+        mSubscriptOffset = nscoord(PR_MAX(f, NSToIntRound(val * f)));
+    }
+    else {
+        mSubscriptOffset = mXHeight;
+    }
+
+    // mStrikeoutOffset
+    mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0);
+
+    // mStrikeoutSize
+    mStrikeoutSize = mUnderlineSize;
+
+    pango_fc_font_unlock_face(fcfont);
+
+    /*
+    printf("%i\n", mXHeight);
+    printf("%i\n", mSuperscriptOffset);
+    printf("%i\n", mSubscriptOffset);
+    printf("%i\n", mStrikeoutOffset);
+    printf("%i\n", mStrikeoutSize);
+    printf("%i\n", mUnderlineOffset);
+    printf("%i\n", mUnderlineSize);
+    printf("%i\n", mMaxHeight);
+    printf("%i\n", mLeading);
+    printf("%i\n", mEmHeight);
+    printf("%i\n", mEmAscent);
+    printf("%i\n", mEmDescent);
+    printf("%i\n", mMaxAscent);
+    printf("%i\n", mMaxDescent);
+    printf("%i\n", mMaxAdvance);
+    printf("%i\n", mSpaceWidth);
+    printf("%i\n", mAveCharWidth);
+    */
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPSPango::Destroy()
+{
+    mDeviceContext = nsnull;
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPSPango::GetLangGroup(nsIAtom** aLangGroup)
+{
+    *aLangGroup = mLangGroup;
+    NS_IF_ADDREF(*aLangGroup);
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontMetricsPSPango::GetFontHandle(nsFontHandle &aHandle)
+{
+    return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIFontMetricsPango impl
+NS_IMETHODIMP
+nsFontMetricsPSPango::GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength)
+{
+    return GetWidth (String, (PRUint32) aLength, aWidth);
+}
+
+NS_IMETHODIMP
+nsFontMetricsPSPango::GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength)
+{
+    return GetWidth (aString, (PRUint32)aLength, aWidth);
+}
+
+nsresult
+nsFontMetricsPSPango::GetWidth(const char* aString, PRUint32 aLength,
+                             nscoord& aWidth)
+{
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    pango_layout_set_text(layout, aString, aLength);
+
+    if (mPangoSpaceWidth)
+        FixupSpaceWidths(layout, aString);
+
+    int width, height;
+
+    pango_layout_get_size(layout, &width, &height);
+
+    g_object_unref(layout);
+
+    float f;
+    f = mDeviceContext->DevUnitsToAppUnits();
+    aWidth = NSToCoordRound(width * f / PANGO_SCALE);
+
+    //    printf("GetWidth (char *) %d\n", aWidth);
+
+    return NS_OK;
+}
+
+nsresult
+nsFontMetricsPSPango::GetWidth(const PRUnichar* aString, PRUint32 aLength,
+                             nscoord& aWidth)
+{
+    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("nsFontMetricsPSPango::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);
+    pango_layout_get_size(layout, &width, &height);
+
+    float f;
+    f = mDeviceContext->DevUnitsToAppUnits();
+    aWidth = NSToCoordRound(width * f / PANGO_SCALE);
+
+    //    printf("GetWidth %d\n", aWidth);
+
+ loser:
+    g_free(text);
+    g_object_unref(layout);
+
+    return rv;
+}
+
+
+nsresult
+nsFontMetricsPSPango :: GetTextDimensions(const char* aString, PRUint32 aLength,
+                                          nsTextDimensions& aDimensions)
+{
+    nsresult rv = NS_OK;
+
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    pango_layout_set_text(layout, aString, aLength);
+    FixupSpaceWidths(layout,aString);
+
+    // 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);
+
+    PangoRectangle rect;
+    pango_layout_line_get_extents(line, NULL, &rect);
+
+    float P2T;
+    P2T = mDeviceContext->DevUnitsToAppUnits();
+
+    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);
+
+    //    printf("GetTextDimensions %d %d %d\n", aDimensions.width,
+    //aDimensions.ascent, aDimensions.descent);
+
+ loser:
+    g_object_unref(layout);
+
+    return rv;
+}
+
+nsresult
+nsFontMetricsPSPango::GetTextDimensions(const PRUnichar* aString,
+                                      PRUint32 aLength,
+                                      nsTextDimensions& aDimensions, 
+                                      PRInt32* aFontID)
+{
+    nsresult rv = NS_OK;
+
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    gchar *text = g_utf16_to_utf8(aString, aLength,
+                                  NULL, NULL, NULL);
+
+    if (!text) {
+#ifdef DEBUG
+        NS_WARNING("nsFontMetricsPSPango::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;
+    }
+        
+
+    pango_layout_set_text(layout, text, strlen(text));
+    FixupSpaceWidths(layout, text);
+
+    // 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);
+
+    PangoRectangle rect;
+    pango_layout_line_get_extents(line, NULL, &rect);
+
+    float P2T;
+    P2T = mDeviceContext->DevUnitsToAppUnits();
+
+    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);
+
+    //    printf("GetTextDimensions %d %d %d\n", aDimensions.width,
+    //aDimensions.ascent, aDimensions.descent);
+
+ loser:
+    g_free(text);
+    g_object_unref(layout);
+
+    return rv;
+}
+
+nsresult
+nsFontMetricsPSPango::GetTextDimensions(const char*         aString,
+                                      PRInt32             aLength,
+                                      PRInt32             aAvailWidth,
+                                      PRInt32*            aBreaks,
+                                      PRInt32             aNumBreaks,
+                                      nsTextDimensions&   aDimensions,
+                                      PRInt32&            aNumCharsFit,
+                                      nsTextDimensions&   aLastWordDimensions,
+                                      PRInt32*            aFontID)
+{
+
+    return GetTextDimensionsInternal(aString, aLength, aAvailWidth, aBreaks,
+                                     aNumBreaks, aDimensions, aNumCharsFit,
+                                     aLastWordDimensions);
+
+}
+
+nsresult
+nsFontMetricsPSPango::GetTextDimensions(const PRUnichar*    aString,
+                                      PRInt32             aLength,
+                                      PRInt32             aAvailWidth,
+                                      PRInt32*            aBreaks,
+                                      PRInt32             aNumBreaks,
+                                      nsTextDimensions&   aDimensions,
+                                      PRInt32&            aNumCharsFit,
+                                      nsTextDimensions&   aLastWordDimensions,
+                                      PRInt32*            aFontID)
+{
+    nsresult rv = NS_OK;
+    PRInt32 curBreak = 0;
+    gchar *curChar;
+
+    PRInt32 *utf8Breaks = new PRInt32[aNumBreaks];
+
+    gchar *text = g_utf16_to_utf8(aString, (PRInt32)aLength,
+                                  NULL, NULL, NULL);
+
+    curChar = text;
+
+    if (!text) {
+#ifdef DEBUG
+        NS_WARNING("nsFontMetricsPSPango::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)) {
+        if (aBreaks[curBreak] == curOffset) {
+            utf8Breaks[curBreak] = curChar - text;
+            curBreak++;
+        }
+
+        if (IS_HIGH_SURROGATE(aString[curOffset]))
+            curOffset++;
+    }
+
+    // Always catch the last break
+    utf8Breaks[curBreak] = curChar - text;
+
+#if 0
+    if (strlen(text) != aLength) {
+        printf("Different lengths for utf16 %d and utf8 %d\n", aLength, strlen(text));
+        DUMP_PRUNICHAR(aString, aLength)
+        DUMP_PRUNICHAR(text, strlen(text))
+        for (PRInt32 i = 0; i < aNumBreaks; ++i) {
+            printf("  break %d utf16 %d utf8 %d\n", i, aBreaks[i], utf8Breaks[i]);
+        }
+    }
+#endif
+
+    // 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,
+                                   aNumBreaks, aDimensions, aNumCharsFit,
+                                   aLastWordDimensions);
+
+    // Figure out which of the breaks we ended up using to convert
+    // back to utf16 - start from the end.
+    for (PRInt32 i = aNumBreaks - 1; i >= 0; --i) {
+        if (utf8Breaks[i] == aNumCharsFit) {
+            //      if (aNumCharsFit != aBreaks[i])
+            //                printf("Fixing utf8 -> utf16 %d -> %d\n", aNumCharsFit, aBreaks[i]);
+            aNumCharsFit = aBreaks[i];
+            break;
+        }
+    }
+
+ loser:
+    if (text)
+        g_free(text);
+
+    delete[] utf8Breaks;
+
+    return rv;
+}
+
+typedef struct _nsPSPangoRenderer        nsPSPangoRenderer;
+typedef struct _nsPSPangoRendererClass   nsPSPangoRendererClass;
+
+struct _nsPSPangoRenderer
+{
+  PangoRenderer parent_instance;
+  nsRenderingContextPS *psContext;
+  nsFontMetricsPSPango *psPangoFontMetrics;
+  float zoom;
+};
+
+struct _nsPSPangoRendererClass
+{
+  PangoRendererClass parent_class;
+};
+
+#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))
+
+G_DEFINE_TYPE (_nsPSPangoRenderer, _ps_pango_renderer, PANGO_TYPE_RENDERER)
+
+static PangoRenderer *
+get_renderer (void)
+{
+  static PangoRenderer               *renderer = NULL;
+
+  if (!renderer)
+    renderer = (PangoRenderer *) g_object_new (_PS_TYPE_PANGO_RENDERER, NULL);
+
+  return renderer;
+}
+
+static void
+_ps_pango_renderer_draw_glyphs (PangoRenderer    *renderer,
+				PangoFont        *font,
+				PangoGlyphString *glyphs,
+				int               x,
+				int               y);
+
+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;
+}
+
+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
+nsPangoType1Generator::Init(PangoFont *aFont)
+{
+  NS_ENSURE_TRUE(aFont, NS_ERROR_FAILURE);
+  mFont = aFont;
+  g_object_ref (mFont);
+  return NS_OK;
+}
+
+nsPangoType1Generator::~nsPangoType1Generator()
+{
+  g_object_unref (mFont);
+  mFont = nsnull;
+}
+
+void nsPangoType1Generator::GeneratePSFont(FILE* aFile)
+{
+  FT_Face face = pango_fc_font_lock_face ((PangoFcFont *) mFont);
+
+  if (face == nsnull)
+    return;
+
+  int wmode = 0;
+  if (mGlyphSubset->Count())
+    FT2SubsetToType1FontSet(face, mGlyphSubset, wmode, aFile);
+
+  pango_fc_font_unlock_face ((PangoFcFont *) mFont);
+}
+
+typedef struct
+{
+  nsCString    *FontNameBase;
+  nsCStringKey *key;
+  int           font_size;
+} PSPangoFontData;
+
+static void
+ps_pango_font_data_destroy (PSPangoFontData *data)
+{
+  delete data->key;
+  delete data->FontNameBase;
+  g_free (data);
+}
+
+static void
+flattenName(nsCString& aString)
+{
+  nsCString::iterator start, end;
+  aString.BeginWriting(start);
+  aString.EndWriting(end);
+  while(start != end) {
+    if (*start == ' ')
+      *start= '_';
+    else if (*start == '(')
+      *start = '_';
+    else if (*start == ')')
+      *start = '_';
+    ++start;
+  }
+}
+
+static void
+_ps_pango_renderer_draw_glyphs (PangoRenderer    *renderer,
+				PangoFont        *font,
+				PangoGlyphString *glyphs,
+				int               x,
+				int               y)
+{
+  if (!glyphs->num_glyphs)
+    return;
+
+  static GQuark data_quark = 0;
+  if (!data_quark)
+    data_quark = g_quark_from_static_string ("ps-pango-font-data");
+
+  PSPangoFontData *data;
+  if (!(data = (PSPangoFontData *) g_object_get_qdata (G_OBJECT (font), data_quark)))
+    {
+      data = g_new (PSPangoFontData, 1);
+
+      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);
+
+      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;
+  for (i = 0; i < glyphs->num_glyphs; ++i) {
+    currSubFont = psFontGen->AddToGlyphSubset(glyphs->glyphs[i].glyph >= 0x00ffffff ? 0 : glyphs->glyphs[i].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;
+    }
+
+    gl.num_glyphs++;
+  }
+
+  if (prevSubFont != -1)
+    psObj->show(&gl, ps_renderer->zoom, psFontGen, prevSubFont);
+}
+
+static void
+draw_layout_line (int x, int y, PangoLayoutLine *line, nsFontMetricsPSPango *aPSPangoFontMetrics, nsRenderingContextPS *aContext)
+{
+  PangoRenderer *renderer = get_renderer ();
+  nsPSPangoRenderer *ps_renderer = (nsPSPangoRenderer *)renderer;
+  ps_renderer->psContext = aContext;
+  ps_renderer->psPangoFontMetrics = aPSPangoFontMetrics;
+  nsDeviceContextPS* dc = NS_REINTERPRET_CAST (nsDeviceContextPS*, aPSPangoFontMetrics->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));
+}
+
+nsresult
+nsFontMetricsPSPango::DrawString(const char *aString, PRUint32 aLength,
+                               nscoord aX, nscoord aY,
+                               const nscoord* aSpacing,
+                               nsRenderingContextPS *aContext)
+{
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    pango_layout_set_text(layout, aString, aLength);
+    FixupSpaceWidths(layout, aString);
+
+    int x = aX;
+    int y = aY;
+
+    aContext->GetTranMatrix()->TransformCoord(&x, &y);
+
+    PangoLayoutLine *line;
+    if (pango_layout_get_line_count(layout) != 1) {
+        printf("Warning: more than one line!\n");
+    }
+    line = pango_layout_get_line(layout, 0);
+
+    if (aSpacing && *aSpacing) {
+        DrawStringSlowly(aString, NULL, aLength, x, y, line, aSpacing, aContext);
+    }
+    else {
+       draw_layout_line (x, y, line, this, aContext);
+    }
+
+    g_object_unref(layout);
+
+    return NS_OK;
+}
+
+nsresult
+nsFontMetricsPSPango::DrawString(const PRUnichar* aString, PRUint32 aLength,
+                               nscoord aX, nscoord aY,
+                               PRInt32 aFontID,
+                               const nscoord* aSpacing,
+                               nsRenderingContextPS *aContext)
+{
+    nsresult rv = NS_OK;
+    int x = aX;
+    int y = aY;
+
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    gchar *text = g_utf16_to_utf8(aString, aLength,
+                                  NULL, NULL, NULL);
+    if (!text) {
+#ifdef DEBUG
+        NS_WARNING("nsFontMetricsPSPango::DrawString invalid unicode to follow");
+        DUMP_PRUNICHAR(aString, aLength)
+#endif
+        rv = NS_ERROR_FAILURE;
+        goto loser;
+    }
+
+    pango_layout_set_text(layout, text, strlen(text));
+    FixupSpaceWidths(layout, text);
+
+    aContext->GetTranMatrix()->TransformCoord(&x, &y);
+
+    PangoLayoutLine *line;
+    if (pango_layout_get_line_count(layout) != 1) {
+        printf("Warning: more than one line!\n");
+    }
+    line = pango_layout_get_line(layout, 0);
+
+    if (aSpacing && *aSpacing) {
+        DrawStringSlowly(text, aString, aLength, x, y, line, aSpacing, aContext);
+    }
+    else {
+       draw_layout_line (x, y, line, this, aContext);
+    }
+
+ loser:
+
+    g_free(text);
+    g_object_unref(layout);
+
+    return rv;
+}
+
+#ifdef MOZ_MATHML
+nsresult
+nsFontMetricsPSPango::GetBoundingMetrics(const char *aString, PRUint32 aLength,
+                                       nsBoundingMetrics &aBoundingMetrics)
+{
+    printf("GetBoundingMetrics (char *)\n");
+    return NS_ERROR_FAILURE;
+}
+
+nsresult
+nsFontMetricsPSPango::GetBoundingMetrics(const PRUnichar *aString,
+                                       PRUint32 aLength,
+                                       nsBoundingMetrics &aBoundingMetrics,
+                                       PRInt32 *aFontID)
+{
+    nsresult rv = NS_OK;
+    PangoLayout *layout = pango_layout_new(mPangoContext);
+
+    gchar *text = g_utf16_to_utf8(aString, aLength,
+                                  NULL, NULL, NULL);
+
+    if (!text) {
+#ifdef DEBUG
+        NS_WARNING("nsFontMetricsPSPango::GetBoundingMetrics invalid unicode to follow");
+        DUMP_PRUNICHAR(aString, aLength)
+#endif
+        aBoundingMetrics.leftBearing = 0;
+        aBoundingMetrics.rightBearing = 0;
+        aBoundingMetrics.width = 0;
+        aBoundingMetrics.ascent = 0;
+        aBoundingMetrics.descent = 0;
+
+        rv = NS_ERROR_FAILURE;
+        goto loser;
+    }
+
+    pango_layout_set_text(layout, text, -1);
+    FixupSpaceWidths(layout, text);
+
+    PangoLayoutLine *line;
+    if (pango_layout_get_line_count(layout) != 1) {
+        printf("Warning: more than one line!\n");
+    }
+    line = pango_layout_get_line(layout, 0);
+
+    // Get the ink and logical extents
+    PangoRectangle ink, logical;
+    pango_layout_line_get_extents(line, &ink, &logical);
+
+    float P2T;
+    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);
+    g_object_unref(layout);
+
+    return rv;
+}
+
+#endif /* MOZ_MATHML */
+
+nsresult
+nsFontMetricsPSPango::SetRightToLeftText(PRBool aIsRTL)
+{
+    if (aIsRTL) {
+        if (!mRTLPangoContext) {
+            mRTLPangoContext = get_context();
+            pango_context_set_base_dir(mRTLPangoContext, PANGO_DIRECTION_RTL);
+
+            pango_context_set_language(mRTLPangoContext, GetPangoLanguage(mLangGroup));
+            pango_context_set_font_description(mRTLPangoContext, mPangoFontDesc);
+        }
+        mPangoContext = mRTLPangoContext;
+    }
+    else {
+        mPangoContext = mLTRPangoContext;
+    }
+
+    mIsRTL = aIsRTL;
+    return NS_OK;
+}
+
+nsresult
+nsFontMetricsPSPango::GetClusterInfo(const PRUnichar *aText,
+                                   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("nsFontMetricsPSPango::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);
+
+    // 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;
+            pos++;
+        }
+        else {
+            aClusterStarts[pos] = attrs[pos].is_cursor_position;
+        }
+    }
+
+ loser:
+    if (attrs)
+        g_free(attrs);
+    if (text)
+        g_free(text);
+    if (layout)
+        g_object_unref(layout);
+
+    return rv;
+}
+
+PRInt32
+nsFontMetricsPSPango::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("nsFontMetricsPSPango::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);
+    
+    pango_layout_xy_to_index(layout, localX, localY,
+                             &inx, &trailing);
+
+    // Convert the index back to the utf-16 index
+    curChar = text;
+
+    for (PRUint32 curOffset=0; curOffset < aLength;
+         curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
+
+        // Check for a match before checking for a surrogate pair
+        if (curChar - text == inx) {
+            retval = curOffset;
+            break;
+        }
+
+        if (IS_HIGH_SURROGATE(aText[curOffset]))
+            curOffset++;
+    }
+
+    // If there was a trailing result, advance the index pointer the
+    // number of characters equal to the trailing result.
+    while (trailing) {
+        retval++;
+        // Yes, this can make aInx > length to indicate the end of the
+        // string.
+        if (retval < (PRInt32)aLength && IS_HIGH_SURROGATE(aText[retval]))
+            retval++;
+        trailing--;
+    }
+
+ loser:
+    if (text)
+        g_free(text);
+    if (layout)
+        g_object_unref(layout);
+
+    return retval;
+}
+
+nsresult
+nsFontMetricsPSPango::GetRangeWidth(const PRUnichar *aText,
+                                  PRUint32 aLength,
+                                  PRUint32 aStart,
+                                  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("nsFontMetricsPSPango::GetWidth invalid unicode to follow");
+        DUMP_PRUNICHAR(aText, aLength)
+#endif
+        rv = NS_ERROR_FAILURE;
+        goto loser;
+    }
+
+    // Convert the utf16 offsets into utf8 offsets
+    for (PRUint32 curOffset = 0; curOffset < aLength;
+         curOffset++, curChar = g_utf8_find_next_char(curChar, NULL)) {
+
+        if (curOffset == aStart)
+            utf8Start = curChar - text;
+
+        if (curOffset == aEnd)
+            utf8End = curChar - text;
+        
+        if (IS_HIGH_SURROGATE(aText[curOffset]))
+            curOffset++;
+    }
+
+    // Special case where the end index is the same as the length
+    if (aLength == aEnd)
+        utf8End = strlen(text);
+
+    rv = GetRangeWidth(text, strlen(text), utf8Start, utf8End, aWidth);
+
+ loser:
+    if (text)
+        g_free(text);
+
+    return rv;
+}
+
+nsresult
+nsFontMetricsPSPango::GetRangeWidth(const char *aText,
+                                  PRUint32 aLength,
+                                  PRUint32 aStart,
+                                  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);
+
+    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);
+
+ loser:
+    if (ranges)
+        g_free(ranges);
+    if (layout)
+        g_object_unref(layout);
+
+    return rv;
+}
+
+PRUint32
+nsFontMetricsPSPango::GetHints(void)
+{
+    return (NS_RENDERING_HINT_BIDI_REORDERING |
+            NS_RENDERING_HINT_ARABIC_SHAPING | 
+            NS_RENDERING_HINT_FAST_MEASURE |
+            NS_RENDERING_HINT_REORDER_SPACED_TEXT |
+            NS_RENDERING_HINT_TEXT_CLUSTERS);
+}
+
+/* static */
+nsresult
+nsFontMetricsPSPango::FamilyExists(nsIDeviceContext *aDevice,
+                                 const nsString &aName)
+{
+    // fontconfig family name is always in UTF-8
+    NS_ConvertUTF16toUTF8 name(aName);
+
+    nsresult rv = NS_ERROR_FAILURE;
+    PangoContext *context = get_context();
+    PangoFontFamily **familyList;
+    int n;
+
+    pango_context_list_families(context, &familyList, &n);
+
+    for (int i=0; i < n; i++) {
+        const char *tmpname = pango_font_family_get_name(familyList[i]);
+        if (!Compare(nsDependentCString(tmpname), name,
+                     nsCaseInsensitiveCStringComparator())) {
+            rv = NS_OK;
+            break;
+        }
+    }
+
+    g_free(familyList);
+    g_object_unref(context);
+
+    return rv;
+}
+
+// Private Methods
+
+nsresult
+nsFontMetricsPSPango::RealizeFont(void)
+{
+    nsCString familyList;
+    // Create and fill out the font description.
+    mPangoFontDesc = pango_font_description_new();
+
+    // Add CSS names - walk the list of fonts, adding the generic as
+    // the last font
+    for (int i=0; i < mFontList.Count(); ++i) {
+        // if this was a generic name, break out of the loop since we
+        // don't want to add it to the pattern yet
+        if (mFontIsGeneric[i])
+            break;;
+
+        nsCString *familyName = mFontList.CStringAt(i);
+        familyList.Append(familyName->get());
+        familyList.Append(',');
+    }
+
+    // If there's a generic add a pref for the generic if there's one
+    // set.
+    if (mGenericFont && !mFont.systemFont) {
+        nsCString name;
+        name += "font.name.";
+        name += mGenericFont->get();
+        name += ".";
+
+        nsString langGroup;
+        mLangGroup->ToString(langGroup);
+
+        name.AppendWithConversion(langGroup);
+
+        nsCOMPtr<nsIPref> pref;
+        pref = do_GetService(NS_PREF_CONTRACTID);
+        if (pref) {
+            nsresult rv;
+            nsXPIDLCString value;
+            rv = pref->GetCharPref(name.get(), getter_Copies(value));
+
+            // we ignore prefs that have three hypens since they are X
+            // style prefs.
+            if (NS_FFRECountHyphens(value) < 3) {
+                nsCString tmpstr;
+                tmpstr.Append(value);
+
+                familyList.Append(tmpstr);
+                familyList.Append(',');
+            }
+        }
+    }
+
+    // Add the generic if there is one.
+    if (mGenericFont && !mFont.systemFont) {
+        familyList.Append(mGenericFont->get());
+        familyList.Append(',');
+    }
+
+    // Set the family
+    pango_font_description_set_family(mPangoFontDesc,
+                                      familyList.get());
+
+    // Set the point size
+    pango_font_description_set_size(mPangoFontDesc,
+                                    (gint)(mPointSize * PANGO_SCALE));
+
+    // Set the style
+    pango_font_description_set_style(mPangoFontDesc,
+                                     CalculateStyle(mFont.style));
+
+    // Set the weight
+    pango_font_description_set_weight(mPangoFontDesc,
+                                      CalculateWeight(mFont.weight));
+
+    // Now that we have the font description set up, create the
+    // context.
+    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 pango language now that we have a context
+    pango_context_set_language(mPangoContext, GetPangoLanguage(mLangGroup));
+
+    // And attach the font description to this context
+    pango_context_set_font_description(mPangoContext, mPangoFontDesc);
+
+    return NS_OK;
+}
+
+/* static */
+PRBool
+nsFontMetricsPSPango::EnumFontCallback(const nsString &aFamily,
+                                     PRBool aIsGeneric, void *aData)
+{
+    NS_ConvertUTF16toUTF8 name(aFamily);
+
+    // The newest fontconfig does the full Unicode case folding so that 
+    // we're being lazy here by calling |ToLowerCase| after converting
+    // to UTF-8  assuming that in virtually all cases, we just have to
+    // fold [A-Z].  (bug 223653). 
+    ToLowerCase(name);
+    nsFontMetricsPSPango *metrics = (nsFontMetricsPSPango *)aData;
+    metrics->mFontList.AppendCString(name);
+    metrics->mFontIsGeneric.AppendElement((void *)aIsGeneric);
+    if (aIsGeneric) {
+        metrics->mGenericFont = 
+            metrics->mFontList.CStringAt(metrics->mFontList.Count() - 1);
+        return PR_FALSE; // stop processing
+    }
+
+    return PR_TRUE; // keep processing
+}
+
+/*
+ * 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!
+ */
+
+void
+nsFontMetricsPSPango::DrawStringSlowly(const gchar *aText,
+                                     const PRUnichar *aOrigString,
+                                     PRUint32 aLength,
+                                     gint aX, gint aY,
+                                     PangoLayoutLine *aLine,
+                                     const nscoord *aSpacing,
+                                     nsRenderingContextPS *aContext)
+{
+    float app2dev;
+    app2dev = mDeviceContext->AppUnitsToDevUnits();
+    gint offset = 0;
+
+    /*
+     * 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.
+     */
+    nscoord *utf8spacing = new nscoord[strlen(aText)];
+
+    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 (IS_HIGH_SURROGATE(aOrigString[curOffset]))
+                curOffset++;
+        }
+    }
+    else {
+        memcpy(utf8spacing, aSpacing, (sizeof(nscoord *) * aLength));
+    }
+
+    gint curRun = 0;
+
+    for (GSList *tmpList = aLine->runs; tmpList && tmpList->data;
+         tmpList = tmpList->next, curRun++) {
+        PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data;
+        gint tmpOffset = 0;
+
+        /*        printf("    Rendering run %d: \"%s\"\n", curRun,
+                  &aText[layoutRun->item->offset]); */
+
+        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;
+        }
+
+        /*        printf("    rendering at X coord %d\n", aX + offset); */
+        offset += tmpOffset;
+    }
+
+    draw_layout_line (aX, aY, aLine, this, aContext);
+
+    delete[] utf8spacing;
+}
+
+nsresult
+nsFontMetricsPSPango::GetTextDimensionsInternal(const gchar*        aString,
+                                              PRInt32             aLength,
+                                              PRInt32             aAvailWidth,
+                                              PRInt32*            aBreaks,
+                                              PRInt32             aNumBreaks,
+                                              nsTextDimensions&   aDimensions,
+                                              PRInt32&            aNumCharsFit,
+                                              nsTextDimensions&   aLastWordDimensions)
+{
+    NS_PRECONDITION(aBreaks[aNumBreaks - 1] == aLength, "invalid break array");
+
+    // If we need to back up this state represents the last place
+    // we could break. We can use this to avoid remeasuring text
+    PRInt32 prevBreakState_BreakIndex = -1; // not known
+                                            // (hasn't been computed)
+    nscoord prevBreakState_Width = 0; // accumulated width to this point
+
+    // Initialize OUT parameters
+    GetMaxAscent(aLastWordDimensions.ascent);
+    GetMaxDescent(aLastWordDimensions.descent);
+    aLastWordDimensions.width = -1;
+    aNumCharsFit = 0;
+
+    // Iterate each character in the string and determine which font to use
+    nscoord width = 0;
+    PRInt32 start = 0;
+    nscoord aveCharWidth;
+    GetAveCharWidth(aveCharWidth);
+
+    while (start < aLength) {
+        // Estimate how many characters will fit. Do that by
+        // diving the available space by the average character
+        // width. Make sure the estimated number of characters is
+        // at least 1
+        PRInt32 estimatedNumChars = 0;
+
+        if (aveCharWidth > 0)
+            estimatedNumChars = (aAvailWidth - width) / aveCharWidth;
+
+        if (estimatedNumChars < 1)
+            estimatedNumChars = 1;
+
+        // Find the nearest break offset
+        PRInt32 estimatedBreakOffset = start + estimatedNumChars;
+        PRInt32 breakIndex;
+        nscoord numChars;
+
+        // Find the nearest place to break that is less than or equal to
+        // the estimated break offset
+        if (aLength <= estimatedBreakOffset) {
+            // All the characters should fit
+            numChars = aLength - start;
+            breakIndex = aNumBreaks - 1;
+        } 
+        else {
+            breakIndex = prevBreakState_BreakIndex;
+            while (((breakIndex + 1) < aNumBreaks) &&
+                   (aBreaks[breakIndex + 1] <= estimatedBreakOffset)) {
+                ++breakIndex;
+            }
+
+            if (breakIndex == prevBreakState_BreakIndex) {
+                ++breakIndex; // make sure we advanced past the
+                // previous break index
+            }
+
+            numChars = aBreaks[breakIndex] - start;
+        }
+
+        // Measure the text
+        nscoord twWidth = 0;
+        if ((1 == numChars) && (aString[start] == ' '))
+            GetSpaceWidth(twWidth);
+        else if (numChars > 0)
+            GetWidth(&aString[start], numChars, twWidth);
+
+        // See if the text fits
+        PRBool  textFits = (twWidth + width) <= aAvailWidth;
+
+        // If the text fits then update the width and the number of
+        // characters that fit
+        if (textFits) {
+            aNumCharsFit += numChars;
+            width += twWidth;
+            start += numChars;
+
+            // This is a good spot to back up to if we need to so remember
+            // this state
+            prevBreakState_BreakIndex = breakIndex;
+            prevBreakState_Width = width;
+        }
+        else {
+            // See if we can just back up to the previous saved
+            // state and not have to measure any text
+            if (prevBreakState_BreakIndex > 0) {
+                // If the previous break index is just before the
+                // current break index then we can use it
+                if (prevBreakState_BreakIndex == (breakIndex - 1)) {
+                    aNumCharsFit = aBreaks[prevBreakState_BreakIndex];
+                    width = prevBreakState_Width;
+                    break;
+                }
+            }
+
+            // We can't just revert to the previous break state
+            if (0 == breakIndex) {
+                // There's no place to back up to, so even though
+                // the text doesn't fit return it anyway
+                aNumCharsFit += numChars;
+                width += twWidth;
+                break;
+            }
+
+            // Repeatedly back up until we get to where the text
+            // fits or we're all the way back to the first word
+            width += twWidth;
+            while ((breakIndex >= 1) && (width > aAvailWidth)) {
+                twWidth = 0;
+                start = aBreaks[breakIndex - 1];
+                numChars = aBreaks[breakIndex] - start;
+
+                if ((1 == numChars) && (aString[start] == ' '))
+                    GetSpaceWidth(twWidth);
+                else if (numChars > 0)
+                    GetWidth(&aString[start], numChars, twWidth);
+                width -= twWidth;
+                aNumCharsFit = start;
+                breakIndex--;
+            }
+            break;
+        }
+    }
+
+    aDimensions.width = width;
+    GetMaxAscent(aDimensions.ascent);
+    GetMaxDescent(aDimensions.descent);
+
+    /*    printf("aDimensions %d %d %d aLastWordDimensions %d %d %d aNumCharsFit %d\n",
+           aDimensions.width, aDimensions.ascent, aDimensions.descent,
+           aLastWordDimensions.width, aLastWordDimensions.ascent, aLastWordDimensions.descent,
+           aNumCharsFit); */
+
+    return NS_OK;
+}
+
+void
+nsFontMetricsPSPango::FixupSpaceWidths (PangoLayout *aLayout,
+                                      const char *aString)
+{
+    PangoLayoutLine *line = pango_layout_get_line(aLayout, 0);
+
+    gint curRun = 0;
+
+    for (GSList *tmpList = line->runs; tmpList && tmpList->data;
+         tmpList = tmpList->next, curRun++) {
+        PangoLayoutRun *layoutRun = (PangoLayoutRun *)tmpList->data;
+
+        for (gint i=0; i < layoutRun->glyphs->num_glyphs; i++) {
+            gint thisOffset = (gint)layoutRun->glyphs->log_clusters[i] + layoutRun->item->offset;
+            if (aString[thisOffset] == ' ')
+                layoutRun->glyphs->glyphs[i].geometry.width = mPangoSpaceWidth;
+        }
+    }
+}
+
+/* static */
+PangoLanguage *
+GetPangoLanguage(nsIAtom *aLangGroup)
+{
+    // Find the FC lang group for this lang group
+    nsCAutoString cname;
+    aLangGroup->ToUTF8String(cname);
+
+    // see if the lang group needs to be translated from mozilla's
+    // internal mapping into fontconfig's
+    const MozGtkLangGroup *langGroup;
+    langGroup = NS_FindFCLangGroup(cname);
+
+    // if there's no lang group, just use the lang group as it was
+    // passed to us
+    //
+    // we're casting away the const here for the strings - should be
+    // safe.
+    if (!langGroup)
+        return pango_language_from_string(cname.get());
+    else if (langGroup->Lang) 
+        return pango_language_from_string((char *) langGroup->Lang);
+
+    return pango_language_from_string("en");
+}
+
+/* static */
+void
+FreeGlobals(void)
+{
+}
+
+/* static */
+PangoStyle
+CalculateStyle(PRUint8 aStyle)
+{
+    switch(aStyle) {
+    case NS_FONT_STYLE_ITALIC:
+        return PANGO_STYLE_OBLIQUE;
+        break;
+    case NS_FONT_STYLE_OBLIQUE:
+        return PANGO_STYLE_OBLIQUE;
+        break;
+    }
+
+    return PANGO_STYLE_NORMAL;
+}
+
+/* static */
+PangoWeight
+CalculateWeight (PRUint16 aWeight)
+{
+    /*
+     * weights come in two parts crammed into one
+     * integer -- the "base" weight is weight / 100,
+     * the rest of the value is the "offset" from that
+     * weight -- the number of steps to move to adjust
+     * the weight in the list of supported font weights,
+     * this value can be negative or positive.
+     */
+    PRInt32 baseWeight = (aWeight + 50) / 100;
+    PRInt32 offset = aWeight - baseWeight * 100;
+
+    /* clip weights to range 0 to 9 */
+    if (baseWeight < 0)
+        baseWeight = 0;
+    if (baseWeight > 9)
+        baseWeight = 9;
+
+    /* Map from weight value to fcWeights index */
+    static int fcWeightLookup[10] = {
+        0, 0, 0, 0, 1, 1, 2, 3, 3, 4,
+    };
+
+    PRInt32 fcWeight = fcWeightLookup[baseWeight];
+
+    /*
+     * adjust by the offset value, make sure we stay inside the 
+     * fcWeights table
+     */
+    fcWeight += offset;
+
+    if (fcWeight < 0)
+        fcWeight = 0;
+    if (fcWeight > 4)
+        fcWeight = 4;
+
+    /* Map to final PANGO_WEIGHT value */
+    static int fcWeights[5] = {
+        349,
+        499,
+        649,
+        749,
+        999
+    };
+
+    return (PangoWeight)fcWeights[fcWeight];
+}
+
+/* static */
+nsresult
+EnumFontsPango(nsIAtom* aLangGroup, const char* aGeneric,
+               PRUint32* aCount, PRUnichar*** aResult)
+{
+    FcPattern   *pat = NULL;
+    FcObjectSet *os  = NULL;
+    FcFontSet   *fs  = NULL;
+    nsresult     rv  = NS_ERROR_FAILURE;
+
+    PRUnichar **array = NULL;
+    PRUint32    narray = 0;
+    PRInt32     serif = 0, sansSerif = 0, monospace = 0, nGenerics;
+
+    *aCount = 0;
+    *aResult = nsnull;
+
+    pat = FcPatternCreate();
+    if (!pat)
+        goto end;
+
+    os = FcObjectSetBuild(FC_FAMILY, FC_FOUNDRY, NULL);
+    if (!os)
+        goto end;
+
+    // take the pattern and add the lang group to it
+    if (aLangGroup)
+        NS_AddLangGroup(pat, aLangGroup);
+
+    // get the font list
+    fs = FcFontList(0, pat, os);
+
+    if (!fs)
+        goto end;
+
+    if (!fs->nfont) {
+        rv = NS_OK;
+        goto end;
+    }
+
+    // Fontconfig supports 3 generic fonts, "serif", "sans-serif", and
+    // "monospace", slightly different from CSS's 5.
+    if (!aGeneric)
+        serif = sansSerif = monospace = 1;
+    else if (!strcmp(aGeneric, "serif"))
+        serif = 1;
+    else if (!strcmp(aGeneric, "sans-serif"))
+        sansSerif = 1;
+    else if (!strcmp(aGeneric, "monospace"))
+        monospace = 1;
+    else if (!strcmp(aGeneric, "cursive") || !strcmp(aGeneric, "fantasy"))
+        serif = sansSerif =  1;
+    else
+        NS_NOTREACHED("unexpected generic family");
+    nGenerics = serif + sansSerif + monospace;
+
+    array = NS_STATIC_CAST(PRUnichar **,
+               nsMemory::Alloc((fs->nfont + nGenerics) * sizeof(PRUnichar *)));
+    if (!array)
+        goto end;
+
+    if (serif) {
+        PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("serif"));
+        if (!name)
+            goto end;
+        array[narray++] = name;
+    }
+
+    if (sansSerif) {
+        PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("sans-serif"));
+        if (!name)
+            goto end;
+        array[narray++] = name;
+    }
+
+    if (monospace) {
+        PRUnichar *name = ToNewUnicode(NS_LITERAL_STRING("monospace"));
+        if (!name)
+            goto end;
+        array[narray++] = name;
+    }
+
+    for (int i=0; i < fs->nfont; ++i) {
+        char *family;
+
+        // if there's no family, just move to the next iteration
+        if (FcPatternGetString (fs->fonts[i], FC_FAMILY, 0,
+                                (FcChar8 **) &family) != FcResultMatch) {
+            continue;
+        }
+
+        // fontconfig always returns family names in UTF-8
+        PRUnichar* name =  UTF8ToNewUnicode(nsDependentCString(family));
+
+        if (!name)
+            goto end;
+
+        array[narray++] = name;
+    }
+
+    NS_QuickSort(array + nGenerics, narray - nGenerics, sizeof (PRUnichar*),
+                 CompareFontNames, nsnull);
+
+    *aCount = narray;
+    if (narray)
+        *aResult = array;
+    else
+        nsMemory::Free(array);
+
+    rv = NS_OK;
+
+ end:
+    if (NS_FAILED(rv) && array) {
+        while (narray)
+            nsMemory::Free (array[--narray]);
+        nsMemory::Free (array);
+    }
+    if (pat)
+        FcPatternDestroy(pat);
+    if (os)
+        FcObjectSetDestroy(os);
+    if (fs)
+        FcFontSetDestroy(fs);
+
+    return rv;
+}
+
+/* static */
+int
+CompareFontNames (const void* aArg1, const void* aArg2, void* aClosure)
+{
+    const PRUnichar* str1 = *((const PRUnichar**) aArg1);
+    const PRUnichar* str2 = *((const PRUnichar**) aArg2);
+
+    return nsCRT::strcmp(str1, str2);
+}
+
+
+// nsFontEnumeratorPango class
+
+nsFontEnumeratorPango::nsFontEnumeratorPango()
+{
+}
+
+NS_IMPL_ISUPPORTS1(nsFontEnumeratorPango, nsIFontEnumerator)
+
+NS_IMETHODIMP
+nsFontEnumeratorPango::EnumerateAllFonts(PRUint32 *aCount,
+                                         PRUnichar ***aResult)
+{
+    NS_ENSURE_ARG_POINTER(aResult);
+    *aResult = nsnull;
+    NS_ENSURE_ARG_POINTER(aCount);
+    *aCount = 0;
+
+    return EnumFontsPango(nsnull, nsnull, aCount, aResult);
+}
+
+NS_IMETHODIMP
+nsFontEnumeratorPango::EnumerateFonts(const char *aLangGroup,
+                                      const char *aGeneric,
+                                      PRUint32 *aCount,
+                                      PRUnichar ***aResult)
+{
+    NS_ENSURE_ARG_POINTER(aResult);
+    *aResult = nsnull;
+    NS_ENSURE_ARG_POINTER(aCount);
+    *aCount = 0;
+
+    // aLangGroup=null or ""  means any (i.e., don't care)
+    // aGeneric=null or ""  means any (i.e, don't care)
+    nsCOMPtr<nsIAtom> langGroup;
+    if (aLangGroup && *aLangGroup)
+        langGroup = do_GetAtom(aLangGroup);
+    const char* generic = nsnull;
+    if (aGeneric && *aGeneric)
+        generic = aGeneric;
+
+    return EnumFontsPango(langGroup, generic, aCount, aResult);
+}
+
+NS_IMETHODIMP
+nsFontEnumeratorPango::HaveFontFor(const char *aLangGroup,
+                                   PRBool *aResult)
+{
+    NS_ENSURE_ARG_POINTER(aResult);
+    *aResult = PR_FALSE;
+    NS_ENSURE_ARG_POINTER(aLangGroup);
+
+    *aResult = PR_TRUE; // always return true for now.
+    // Finish me - ftang
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontEnumeratorPango::GetDefaultFont(const char *aLangGroup,
+                                      const char *aGeneric,
+                                      PRUnichar **aResult)
+{
+    NS_ENSURE_ARG_POINTER(aResult);
+    *aResult = nsnull;
+
+    // Have a look at nsFontEnumeratorXft::GetDefaultFont for some
+    // possible code for this function.
+
+    return NS_OK;
+}
+
+NS_IMETHODIMP
+nsFontEnumeratorPango::UpdateFontList(PRBool *_retval)
+{
+    *_retval = PR_FALSE; // always return false for now
+    return NS_OK;
+}
Index: gfx/src/ps/nsFontMetricsPSPango.h
===================================================================
RCS file: gfx/src/ps/nsFontMetricsPSPango.h
diff -N gfx/src/ps/nsFontMetricsPSPango.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ gfx/src/ps/nsFontMetricsPSPango.h	23 Oct 2006 17:37:13 -0000
@@ -0,0 +1,305 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:expandtab:shiftwidth=4:tabstop=4:
+ */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is
+ * Christopher Blizzard <blizzard@mozilla.org>.  
+ * Portions created by the Initial Developer are Copyright (C) 2002
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *
+ * 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
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsFontMetricsPSPango_h__
+#define nsFontMetricsPSPango_h__
+
+#include "nsIFontMetrics.h"
+#include "nsIFontEnumerator.h"
+#include "nsCRT.h"
+#include "nsIAtom.h"
+#include "nsString.h"
+#include "nsVoidArray.h"
+#include "nsFontMetricsPS.h"
+
+#include <pango/pango.h>
+
+class nsRenderingContextPS;
+class nsIDrawingSurface;
+
+class nsFontMetricsPSPango : public nsFontMetricsPS
+{
+public:
+    nsFontMetricsPSPango();
+    virtual ~nsFontMetricsPSPango();
+
+    NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
+
+    // nsISupports
+    NS_DECL_ISUPPORTS
+
+    // nsIFontMetrics
+    NS_IMETHOD  Init                 (const nsFont& aFont, nsIAtom* aLangGroup,
+                                      nsIDeviceContext *aContext);
+    NS_IMETHOD  Destroy();
+    NS_IMETHOD  GetLangGroup         (nsIAtom** aLangGroup);
+    NS_IMETHOD  GetFontHandle        (nsFontHandle &aHandle);
+
+    NS_IMETHOD  GetXHeight           (nscoord& aResult)
+                                     { aResult = mXHeight; return NS_OK; };
+
+    NS_IMETHOD GetSuperscriptOffset  (nscoord& aResult)
+                                     { aResult = mSuperscriptOffset;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetSubscriptOffset    (nscoord& aResult)
+                                     { aResult = mSubscriptOffset;
+                                       return NS_OK; };
+                              
+    NS_IMETHOD GetStrikeout          (nscoord& aOffset, nscoord& aSize)
+                                     { aOffset = mStrikeoutOffset;
+                                       aSize = mStrikeoutSize; 
+                                       return NS_OK; };
+
+    NS_IMETHOD GetUnderline          (nscoord& aOffset, nscoord& aSize)
+                                     { aOffset = mUnderlineOffset;
+                                       aSize = mUnderlineSize; 
+                                       return NS_OK; };
+
+    NS_IMETHOD GetHeight             (nscoord &aHeight)
+                                     { aHeight = mMaxHeight; 
+                                       return NS_OK; };
+
+    NS_IMETHOD GetNormalLineHeight   (nscoord &aHeight)
+                                     { aHeight = mEmHeight + mLeading;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetLeading            (nscoord &aLeading)
+                                     { aLeading = mLeading; 
+                                       return NS_OK; };
+
+    NS_IMETHOD GetEmHeight           (nscoord &aHeight)
+                                     { aHeight = mEmHeight; 
+                                       return NS_OK; };
+
+    NS_IMETHOD GetEmAscent           (nscoord &aAscent)
+                                     { aAscent = mEmAscent;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetEmDescent          (nscoord &aDescent)
+                                     { aDescent = mEmDescent;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetMaxHeight          (nscoord &aHeight)
+                                     { aHeight = mMaxHeight;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetMaxAscent          (nscoord &aAscent)
+                                     { aAscent = mMaxAscent;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetMaxDescent         (nscoord &aDescent)
+                                     { aDescent = mMaxDescent;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetMaxAdvance         (nscoord &aAdvance)
+                                     { aAdvance = mMaxAdvance;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetSpaceWidth         (nscoord &aSpaceCharWidth)
+                                     { aSpaceCharWidth = mSpaceWidth;
+                                       return NS_OK; };
+
+    NS_IMETHOD GetAveCharWidth       (nscoord &aAveCharWidth)
+                                     { aAveCharWidth = mAveCharWidth;
+                                       return NS_OK; };
+
+    // nsIFontMetricsPS (calls from the font rendering layer)
+    NS_IMETHOD  GetStringWidth(const char *String,nscoord &aWidth,nscoord aLength);
+    NS_IMETHOD  GetStringWidth(const PRUnichar *aString,nscoord &aWidth,nscoord aLength);
+
+    NS_IMETHOD       GetWidth(const char* aString, PRUint32 aLength,
+                              nscoord& aWidth);
+    NS_IMETHOD       GetWidth(const PRUnichar* aString, PRUint32 aLength,
+                              nscoord& aWidth);
+
+    NS_IMETHOD       GetTextDimensions(const char* aString,
+                                       PRUint32 aLength,
+                                       nsTextDimensions& aDimensions);
+    NS_IMETHOD       GetTextDimensions(const PRUnichar* aString,
+                                       PRUint32 aLength,
+                                       nsTextDimensions& aDimensions, 
+                                       PRInt32* aFontID);
+    NS_IMETHOD       GetTextDimensions(const char*         aString,
+                                       PRInt32             aLength,
+                                       PRInt32             aAvailWidth,
+                                       PRInt32*            aBreaks,
+                                       PRInt32             aNumBreaks,
+                                       nsTextDimensions&   aDimensions,
+                                       PRInt32&            aNumCharsFit,
+                                       nsTextDimensions&   aLastWordDimensions,
+                                       PRInt32*            aFontID);
+    NS_IMETHOD       GetTextDimensions(const PRUnichar*    aString,
+                                       PRInt32             aLength,
+                                       PRInt32             aAvailWidth,
+                                       PRInt32*            aBreaks,
+                                       PRInt32             aNumBreaks,
+                                       nsTextDimensions&   aDimensions,
+                                       PRInt32&            aNumCharsFit,
+                                       nsTextDimensions&   aLastWordDimensions,
+                                       PRInt32*            aFontID);
+
+    NS_IMETHOD       DrawString(const char *aString, PRUint32 aLength,
+                                nscoord aX, nscoord aY,
+                                const nscoord* aSpacing,
+                                nsRenderingContextPS *aContext);
+    NS_IMETHOD       DrawString(const PRUnichar* aString, PRUint32 aLength,
+                                nscoord aX, nscoord aY,
+                                PRInt32 aFontID,
+                                const nscoord* aSpacing,
+                                nsRenderingContextPS *aContext);
+
+#ifdef MOZ_MATHML
+    NS_IMETHOD       GetBoundingMetrics(const char *aString, PRUint32 aLength,
+                                        nsBoundingMetrics &aBoundingMetrics);
+    NS_IMETHOD       GetBoundingMetrics(const PRUnichar *aString,
+                                        PRUint32 aLength,
+                                        nsBoundingMetrics &aBoundingMetrics,
+                                        PRInt32 *aFontID);
+#endif /* MOZ_MATHML */
+
+    NS_IMETHOD       SetRightToLeftText(PRBool aIsRTL);
+
+    NS_IMETHOD       GetClusterInfo(const PRUnichar *aText,
+                                    PRUint32 aLength,
+                                    PRUint8 *aClusterStarts);
+
+    virtual PRInt32 GetPosition(const PRUnichar *aText,
+                                PRUint32 aLength,
+                                nsPoint aPt);
+
+    NS_IMETHOD       GetRangeWidth(const PRUnichar *aText,
+                                   PRUint32 aLength,
+                                   PRUint32 aStart,
+                                   PRUint32 aEnd,
+                                   PRUint32 &aWidth);
+
+    NS_IMETHOD       GetRangeWidth(const char *aText,
+                                   PRUint32 aLength,
+                                   PRUint32 aStart,
+                                   PRUint32 aEnd,
+                                   PRUint32 &aWidth);
+
+    // get hints for the font
+    virtual PRUint32    GetHints     (void);
+
+    // drawing surface methods
+    static nsresult FamilyExists    (nsIDeviceContext *aDevice,
+                                     const nsString &aName);
+
+    inline nsIDeviceContext *GetDeviceContext() { return mDeviceContext; }
+
+private:
+
+    // generic font metrics class bits
+    nsCStringArray       mFontList;
+    nsAutoVoidArray      mFontIsGeneric;
+
+    nsIDeviceContext    *mDeviceContext;
+    nsCOMPtr<nsIAtom>    mLangGroup;
+    nsCString           *mGenericFont;
+    float                mPointSize;
+
+    nsCAutoString        mDefaultFont;
+
+    // Pango-related items
+    PangoFontDescription *mPangoFontDesc;
+    PangoContext         *mPangoContext;
+    PangoContext         *mLTRPangoContext;
+    PangoContext         *mRTLPangoContext;
+    PangoAttrList        *mPangoAttrList;
+    PRBool                mIsRTL;
+
+    // Cached font metrics
+    nscoord                  mXHeight;
+    nscoord                  mSuperscriptOffset;
+    nscoord                  mSubscriptOffset;
+    nscoord                  mStrikeoutOffset;
+    nscoord                  mStrikeoutSize;
+    nscoord                  mUnderlineOffset;
+    nscoord                  mUnderlineSize;
+    nscoord                  mMaxHeight;
+    nscoord                  mLeading;
+    nscoord                  mEmHeight;
+    nscoord                  mEmAscent;
+    nscoord                  mEmDescent;
+    nscoord                  mMaxAscent;
+    nscoord                  mMaxDescent;
+    nscoord                  mMaxAdvance;
+    nscoord                  mSpaceWidth;
+    nscoord                  mPangoSpaceWidth;
+    nscoord                  mAveCharWidth;
+
+    // Private methods
+    nsresult RealizeFont(void);
+    nsresult CacheFontMetrics(void);
+
+    static PRBool EnumFontCallback(const nsString &aFamily,
+                                   PRBool aIsGeneric, void *aData);
+
+    void     DrawStringSlowly(const gchar *aText,
+                              const PRUnichar *aOrigString,
+                              PRUint32 aLength,
+                              gint aX, gint aY,
+                              PangoLayoutLine *aLine,
+                              const nscoord *aSpacing,
+                              nsRenderingContextPS *aContext);
+
+    nsresult GetTextDimensionsInternal(const gchar*        aString,
+                                       PRInt32             aLength,
+                                       PRInt32             aAvailWidth,
+                                       PRInt32*            aBreaks,
+                                       PRInt32             aNumBreaks,
+                                       nsTextDimensions&   aDimensions,
+                                       PRInt32&            aNumCharsFit,
+                                       nsTextDimensions&   aLastWordDimensions);
+
+    void FixupSpaceWidths (PangoLayout *aLayout, const char *aString);
+};
+
+class nsFontEnumeratorPango : public nsIFontEnumerator
+{
+public:
+    nsFontEnumeratorPango();
+    NS_DECL_ISUPPORTS
+    NS_DECL_NSIFONTENUMERATOR
+};
+
+#endif
+
Index: gfx/src/ps/nsPostScriptObj.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsPostScriptObj.cpp,v
retrieving revision 1.124
diff -u -p -d -r1.124 nsPostScriptObj.cpp
--- gfx/src/ps/nsPostScriptObj.cpp	26 Jul 2005 15:54:18 -0000	1.124
+++ gfx/src/ps/nsPostScriptObj.cpp	23 Oct 2006 17:37:29 -0000
@@ -2061,31 +2061,74 @@ nsPostScriptObj::show(const PRUnichar* t
 
 #if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
 void 
-nsPostScriptObj::show(const PRUnichar* aTxt, int aLen,
-                      const nsAFlatString& aCharList, PRUint16 aSubFontIdx)
+/*nsPostScriptObj::show(const PRUnichar* aTxt, int aLen,
+  const nsAFlatString& aCharList, PRUint16 aSubFontIdx) */
+nsPostScriptObj::show(const nsValueArray *aGlyphs, nsPSFontGenerator *aSubset,
+                      PRUint16 aSubFontIdx)
 {
-  int i;
+  PRUint32 i;
   fputc('<', mScriptFP);
 
-  const PRUint16 subFontSize = nsPSFontGenerator::kSubFontSize;
+  for (i = 0; i < aGlyphs->Count(); i++) {
+    PRUint32 glyph = aGlyphs->ValueAt(i);
+    fprintf(mScriptFP, "%02x", aSubset->InSubsetIndexOf(glyph));
+  }
 
-  // the character repertoire of a subfont (255 characters max)
-  const nsAString& repertoire = 
-        Substring(aCharList, aSubFontIdx * subFontSize,
-                  PR_MIN(subFontSize, 
-                  aCharList.Length() - aSubFontIdx * subFontSize));
+  fputs("> show\n", mScriptFP);
+}
+#endif
 
-  for (i = 0; i < aLen; i++) {
-    // XXX This is a little inefficient, but printing is not perf. critical. 
-    NS_ASSERTION(repertoire.FindChar(aTxt[i]) != kNotFound,
-        "character is not covered by this subfont");
-      
-    // Type 1 encoding vector has 256 slots, but the 0-th slot is 
-    // reserved for /.notdef so that we use the 1st through 255th slots
-    // for actual characters (hence '+ 1')
-    fprintf(mScriptFP, "%02x", repertoire.FindChar(aTxt[i]) + 1); 
+#ifdef MOZ_ENABLE_PANGO
+void
+nsPostScriptObj::show(const PangoGlyphString *glyphs, float zoom, 
+                      nsPSFontGenerator *aSubset, PRUint16 aSubFontIdx)
+{
+  PRUint32 i;
+  int horiz = 1;
+
+  if (glyphs->glyphs[0].geometry.x_offset || glyphs->glyphs[0].geometry.y_offset)
+    rmoveto (NSToCoordRound (zoom * glyphs->glyphs[0].geometry.x_offset / PANGO_SCALE),
+             NSToCoordRound (zoom * glyphs->glyphs[0].geometry.y_offset / PANGO_SCALE));
+
+  fputc('<', mScriptFP);
+
+  for (i = 0; i < glyphs->num_glyphs; i++) {
+    PRUint32 glyph = glyphs->glyphs[i].glyph;
+    fprintf(mScriptFP, "%02x", aSubset->InSubsetIndexOf(glyph));
+    if (glyphs->glyphs[i].geometry.y_offset)
+      horiz = 0;
+  }
+
+  if (horiz) {
+    fputs(">\n[", mScriptFP);
+    for (i = 1; i < glyphs->num_glyphs; i++) {
+      fprintf(mScriptFP, "%d ",
+              NSToCoordRound (zoom * (+ glyphs->glyphs[i  ].geometry.x_offset
+                                      + glyphs->glyphs[i-1].geometry.width
+                                      - glyphs->glyphs[i-1].geometry.x_offset) / PANGO_SCALE));
+    }
+    fprintf(mScriptFP, "%d",
+              NSToCoordRound (zoom * (+ glyphs->glyphs[i-1].geometry.width
+                                      - glyphs->glyphs[i-1].geometry.x_offset
+                                      - glyphs->glyphs[  0].geometry.x_offset) / PANGO_SCALE));
+    fputs("] xshow\n", mScriptFP);
+  } else {
+    fputs(">\n[", mScriptFP);
+    for (i = 1; i < glyphs->num_glyphs; i++) {
+      fprintf(mScriptFP, "%d %d ",
+              NSToCoordRound (zoom * (+ glyphs->glyphs[i  ].geometry.x_offset
+                                      + glyphs->glyphs[i-1].geometry.width
+                                      - glyphs->glyphs[i-1].geometry.x_offset) / PANGO_SCALE),
+              NSToCoordRound (zoom * (+ glyphs->glyphs[i  ].geometry.y_offset
+                                      - glyphs->glyphs[i-1].geometry.y_offset) / PANGO_SCALE));
+    }
+    fprintf(mScriptFP, "%d %d",
+              NSToCoordRound (zoom * (+ glyphs->glyphs[i-1].geometry.width
+                                      - glyphs->glyphs[i-1].geometry.x_offset
+                                      - glyphs->glyphs[  0].geometry.x_offset) / PANGO_SCALE),
+              NSToCoordRound (zoom * (- glyphs->glyphs[i-1].geometry.y_offset) / PANGO_SCALE));
+    fputs("] xyshow\n", mScriptFP);
   }
-  fputs("> show\n", mScriptFP);
 }
 #endif
 
@@ -2101,6 +2144,16 @@ nsPostScriptObj::moveto(nscoord x, nscoo
 
 /** ---------------------------------------------------
  *  See documentation in nsPostScriptObj.h
+ *	@update 10/20/06 behdad
+ */
+void 
+nsPostScriptObj::rmoveto(nscoord x, nscoord y)
+{
+  fprintf(mScriptFP, "%d %d rmoveto\n", x, y);
+}
+
+/** ---------------------------------------------------
+ *  See documentation in nsPostScriptObj.h
  *	@update 2/1/99 dwc
  */
 void 
Index: gfx/src/ps/nsPostScriptObj.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsPostScriptObj.h,v
retrieving revision 1.47
diff -u -p -d -r1.47 nsPostScriptObj.h
--- gfx/src/ps/nsPostScriptObj.h	8 May 2005 15:01:20 -0000	1.47
+++ gfx/src/ps/nsPostScriptObj.h	23 Oct 2006 17:37:30 -0000
@@ -57,9 +57,15 @@
 #include "nsIPersistentProperties2.h"
 #include "nsTempfilePS.h"
 #include "nsEPSObjectPS.h"
+#ifdef MOZ_ENABLE_PANGO
+#include <pango/pango.h>
+#endif
+
 
+class nsPSFontGenerator;
 class nsIImage;
 class nsIAtom;
+class nsValueArray;
 #endif
 
 #include <stdio.h>
@@ -217,6 +223,14 @@ public:
    */
   void moveto(nscoord aX, nscoord aY);
   /** ---------------------------------------------------
+   *  Move relative to the current point
+   *	@update 10/20/2006 behdad
+   *	@param  aX   X coordinate
+   *	        aY   Y coordinate
+   *    @return VOID
+   */
+  void rmoveto(nscoord aX, nscoord aY);
+  /** ---------------------------------------------------
    *  Add a line to the current path, from the current point
    *  to the specified point.
    *	@update 9/30/2003
@@ -346,12 +360,24 @@ public:
    */
   void show(const PRUnichar* aText, int aLen, const char *aAlign, int aType);
   /** ---------------------------------------------------
-   *  This version takes a PRUnichar string, a font subset string
-   *  for freetype printing and a subfont index
+   *  This version of show takes an array of glyphs, subfont and subfont index
+   *  to render and is used for freetype and xft printing.
    *	@update 2/15/2005 jshin@mailaps.org
+   *    @update 6/7/2005 blizzard@mozilla.org
    */
-  void show(const PRUnichar* aText, int aLen, const nsAFlatString& aCharList,
+  void show(const nsValueArray *aGlyphs, nsPSFontGenerator *aSubset,
             PRUint16 aSubFontIdx);
+  /*void show(const PRUnichar* aText, int aLen, const nsAFlatString& aCharList,
+    PRUint16 aSubFontIdx); */
+#ifdef MOZ_ENABLE_PANGO
+  /** ---------------------------------------------------
+   *  This version of show takes a pango glyph string, subfont and subfont index
+   *  to render and is used for pango printing.
+   *	@update 10/20/2006 behdad@behdad.org
+   */
+  void show(const PangoGlyphString *glyphs, float zoom,
+            nsPSFontGenerator *aSubset, PRUint16 aSubFontIdx);
+#endif
   /** ---------------------------------------------------
    *  set the clipping path to the current path using the winding rule
    *	@update 2/1/99 dwc
Index: gfx/src/ps/nsRenderingContextPS.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsRenderingContextPS.cpp,v
retrieving revision 1.83
diff -u -p -d -r1.83 nsRenderingContextPS.cpp
--- gfx/src/ps/nsRenderingContextPS.cpp	4 Mar 2005 07:39:27 -0000	1.83
+++ gfx/src/ps/nsRenderingContextPS.cpp	23 Oct 2006 17:37:31 -0000
@@ -251,6 +251,8 @@ nsRenderingContextPS :: GetDrawingSurfac
 NS_IMETHODIMP
 nsRenderingContextPS :: GetHints(PRUint32& aResult)
 {
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  aResult = metrics->GetHints ();
   return NS_OK;
 }
 
@@ -1006,8 +1008,11 @@ nsRenderingContextPS::GetTextDimensions(
                                         nsTextDimensions& aLastWordDimensions,
                                         PRInt32*          aFontID)
 {
-  NS_NOTYETIMPLEMENTED("nsRenderingContextPS::GetTextDimensions");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetTextDimensions (aString, aLength, aAvailWidth, aBreaks, aNumBreaks,
+				     aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
 }
 
 NS_IMETHODIMP
@@ -1021,43 +1026,31 @@ nsRenderingContextPS::GetTextDimensions(
                                         nsTextDimensions& aLastWordDimensions,
                                         PRInt32*          aFontID)
 {
-  NS_NOTYETIMPLEMENTED("nsRenderingContextPS::GetTextDimensions");
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetTextDimensions (aString, aLength, aAvailWidth, aBreaks, aNumBreaks,
+				     aDimensions, aNumCharsFit, aLastWordDimensions, aFontID);
 }
 
 NS_IMETHODIMP
 nsRenderingContextPS :: GetTextDimensions(const char* aString, PRUint32 aLength,
                                           nsTextDimensions& aDimensions)
 {
-  nsresult rv = NS_ERROR_FAILURE;
-
-  if (mFontMetrics) {
-    nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
-    metrics->GetStringWidth(aString, aDimensions.width, aLength);
-    metrics->GetMaxAscent(aDimensions.ascent);
-    metrics->GetMaxDescent(aDimensions.descent);
-    rv = NS_OK;
-  }
-  
-  return rv;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetTextDimensions (aString, aLength, aDimensions);
 }
 
 NS_IMETHODIMP
 nsRenderingContextPS :: GetTextDimensions(const PRUnichar* aString, PRUint32 aLength,
                                           nsTextDimensions& aDimensions, PRInt32* aFontID)
 {
-  nsresult rv = NS_ERROR_FAILURE;
-
-  if (mFontMetrics) {
-    nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
-    metrics->GetStringWidth(aString, aDimensions.width, aLength);
-     //XXX temporary - bug 96609
-    metrics->GetMaxAscent(aDimensions.ascent);
-    metrics->GetMaxDescent(aDimensions.descent);
-    rv = NS_OK;
-  }
-
-  return rv;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetTextDimensions (aString, aLength, aDimensions, aFontID);
 }
 
 /** ---------------------------------------------------
@@ -1073,47 +1066,7 @@ nsRenderingContextPS :: DrawString(const
 
   nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
   NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
-
-  // When FT2 printing is enabled, we don't need to set langgroup
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
-  if (!NS_REINTERPRET_CAST(nsDeviceContextPS *, mContext.get())->mFTPEnable) {
-#endif
-    nsCOMPtr<nsIAtom> langGroup;
-    mFontMetrics->GetLangGroup(getter_AddRefs(langGroup));
-    mPSObj->setlanggroup(langGroup);
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
-  }
-#endif
-
-  if (aLength == 0)
-    return NS_OK;
-  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], metrics->Font(), metrics);
-  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
-  fontPS->SetupFont(this);
-
-  PRUint32 i, start = 0;
-  for (i=0; i<aLength; i++) {
-    nsFontPS* fontThisChar;
-    fontThisChar = nsFontPS::FindFont(aString[i], metrics->Font(), metrics);
-    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
-    if (fontThisChar != fontPS) {
-      // draw text up to this point
-      aX += DrawString(aString+start, i-start, aX, aY, fontPS, 
-                       aSpacing?aSpacing+start:nsnull);
-      start = i;
-
-      // setup for following text
-      fontPS = fontThisChar;
-      fontPS->SetupFont(this);
-    }
-  }
-
-  // draw the last part
-  if (aLength-start)
-    DrawString(aString+start, aLength-start, aX, aY, fontPS, 
-               aSpacing?aSpacing+start:nsnull);
-
-  return NS_OK;
+  return metrics->DrawString (aString, aLength, aX, aY, aSpacing, this);
 }
 
 /** ---------------------------------------------------
@@ -1129,110 +1082,7 @@ nsRenderingContextPS :: DrawString(const
   
   nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
   NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
-
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
-  // When FT2 printing is enabled, we don't need to set langgroup
-  if (!NS_REINTERPRET_CAST(nsDeviceContextPS *, mContext.get())->mFTPEnable) {
-#endif
-    nsCOMPtr<nsIAtom> langGroup = nsnull;
-    mFontMetrics->GetLangGroup(getter_AddRefs(langGroup));
-    mPSObj->setlanggroup(langGroup);
-#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
-  }
-#endif
-
-  /* build up conversion table */
-  mPSObj->preshow(aString, aLength);
-
-  if (aLength == 0)
-    return NS_OK;
-  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], metrics->Font(), metrics);
-  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);
-  fontPS->SetupFont(this);
-
-  PRUint32 i, start = 0;
-  for (i=0; i<aLength; i++) {
-    nsFontPS* fontThisChar;
-    fontThisChar = nsFontPS::FindFont(aString[i], metrics->Font(), metrics);
-    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
-    if (fontThisChar != fontPS) {
-      // draw text up to this point
-      aX += DrawString(aString+start, i-start, aX, aY, fontPS, 
-                       aSpacing?aSpacing+start:nsnull);
-      start = i;
-
-      // setup for following text
-      fontPS = fontThisChar;
-      fontPS->SetupFont(this);
-    }
-  }
-
-  // draw the last part
-  if (aLength-start)
-    DrawString(aString+start, aLength-start, aX, aY, fontPS, 
-               aSpacing?aSpacing+start:nsnull);
-
-  return NS_OK;
-}
-
-PRInt32 
-nsRenderingContextPS::DrawString(const char *aString, PRUint32 aLength,
-                                 nscoord &aX, nscoord &aY, nsFontPS* aFontPS,
-                                 const nscoord* aSpacing)
-{
-  nscoord width = 0;
-  PRInt32 x = aX;
-  PRInt32 y = aY;
-
-  PRInt32 dxMem[500];
-  PRInt32* dx0 = 0;
-  if (aSpacing) {
-    dx0 = dxMem;
-    if (aLength > 500) {
-      dx0 = new PRInt32[aLength];
-      NS_ENSURE_TRUE(dx0, NS_ERROR_OUT_OF_MEMORY);
-    }
-    mTranMatrix->ScaleXCoords(aSpacing, aLength, dx0);
-  }
-
-  mTranMatrix->TransformCoord(&x, &y);
-  width = aFontPS->DrawString(this, x, y, aString, aLength);
-
-  if ((aSpacing) && (dx0 != dxMem)) {
-    delete [] dx0;
-  }
-
-  return width;
-}
-
-
-PRInt32 
-nsRenderingContextPS::DrawString(const PRUnichar *aString, PRUint32 aLength,
-                                 nscoord aX, nscoord aY, nsFontPS* aFontPS,
-                                 const nscoord* aSpacing)
-{
-  nscoord width = 0;
-  PRInt32 x = aX;
-  PRInt32 y = aY;
-
-  if (aSpacing) {
-    // Slow, but accurate rendering
-    const PRUnichar* end = aString + aLength;
-    while (aString < end){
-      x = aX;
-      y = aY;
-      mTranMatrix->TransformCoord(&x, &y);
-      aFontPS->DrawString(this, x, y, aString, 1);
-      aX += *aSpacing++;
-      aString++;
-    }
-    width = aX;
-  } else {
-    mTranMatrix->TransformCoord(&x, &y);
-    width = aFontPS->DrawString(this, x, y, aString, aLength);
-  }
-
-  return width;
+  return metrics->DrawString (aString, aLength, aX, aY, aFontID, aSpacing, this);
 }
 
 /** ---------------------------------------------------
@@ -1346,8 +1196,10 @@ nsRenderingContextPS::GetBoundingMetrics
                                          PRUint32           aLength,
                                          nsBoundingMetrics& aBoundingMetrics)
 {
-  // Fill me up 
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetBoundingMetrics (aString, aLength, aBoundingMetrics);
 }
 
   /**
@@ -1359,8 +1211,10 @@ nsRenderingContextPS::GetBoundingMetrics
                                          nsBoundingMetrics& aBoundingMetrics,
                                          PRInt32*           aFontID)
 {
-  // Fill me up 
-  return NS_ERROR_NOT_IMPLEMENTED;
+  NS_ENSURE_TRUE(mFontMetrics, NS_ERROR_NULL_POINTER);
+  nsFontMetricsPS *metrics = NS_REINTERPRET_CAST(nsFontMetricsPS *, mFontMetrics.get());
+  NS_ENSURE_TRUE(metrics, NS_ERROR_FAILURE);
+  return metrics->GetBoundingMetrics (aString, aLength, aBoundingMetrics, aFontID);
 }
 #endif /* MOZ_MATHML */
 
Index: gfx/src/ps/nsRenderingContextPS.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsRenderingContextPS.h,v
retrieving revision 1.49
diff -u -p -d -r1.49 nsRenderingContextPS.h
--- gfx/src/ps/nsRenderingContextPS.h	20 Sep 2004 06:46:16 -0000	1.49
+++ gfx/src/ps/nsRenderingContextPS.h	23 Oct 2006 17:37:35 -0000
@@ -154,6 +154,10 @@ public:
   NS_IMETHOD GetWidth(const PRUnichar* aString, PRUint32 aLength,
                       nscoord& aWidth, PRInt32 *aFontID);
 
+  nsTransform2D *GetTranMatrix() {
+    return mTranMatrix;
+  }
+
   NS_IMETHOD DrawString(const char *aString, PRUint32 aLength,
                         nscoord aX, nscoord aY,
                         const nscoord* aSpacing);
@@ -164,13 +168,6 @@ public:
   NS_IMETHOD DrawString(const nsString& aString, nscoord aX, nscoord aY,
                         PRInt32 aFontID,
                         const nscoord* aSpacing);
-protected:
-  PRInt32 DrawString(const PRUnichar *aString, PRUint32 aLength,
-                        nscoord aX, nscoord aY, nsFontPS* aFontPS,
-                        const nscoord* aSpacing);
-  PRInt32 DrawString(const char *aString, PRUint32 aLength,
-                        nscoord &aX, nscoord &aY, nsFontPS* aFontPS,
-                        const nscoord* aSpacing);
 public:
 
   NS_IMETHOD GetTextDimensions(const char* aString, PRUint32 aLength,
Index: gfx/src/ps/nsType1.cpp
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsType1.cpp,v
retrieving revision 1.5.8.1
diff -u -p -d -r1.5.8.1 nsType1.cpp
--- gfx/src/ps/nsType1.cpp	19 Oct 2005 08:16:22 -0000	1.5.8.1
+++ gfx/src/ps/nsType1.cpp	23 Oct 2006 17:37:39 -0000
@@ -73,8 +73,13 @@
 #include "nsIFreeType2.h"
 #include "nsServiceManagerUtils.h"
 #endif
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
+#include FT_TYPE1_TABLES_H
+#endif
 #include "nsPrintfCString.h"
 #include "nsAutoBuffer.h"
+#include "nsValueArray.h"
+#include "nsVoidArray.h"
 
 #define HEXASCII_LINE_LEN 64
 
@@ -113,7 +118,7 @@ static void encryptAndHexOut(FILE *aFile
                              const char *aBuf, PRInt32 aLen = -1);
 static void charStringOut(FILE* aFile, PRUint32* aPos, PRUint16* aKey,
                           const char *aStr, PRUint32 aLen, 
-                          PRUnichar aId);
+                          const char *aGlyphName);
 static void flattenName(nsCString& aString);
 
 /* thunk a short name for this function */
@@ -202,19 +207,30 @@ Type1EncryptString(unsigned char *aInBuf
     aOutBuf[i] = Type1Encrypt(aInBuf[i], &key);
 }
 
+static FT_UShort
+get_upm (FT_Face face)
+{
+  FT_UShort upm = face->units_per_EM;
+
+  if (!upm)
+    upm = 1000; // bitmap font or something
+
+  return upm;
+}
+
 static PRBool
 sideWidthAndBearing(const FT_Vector *aEndPt, FT2PT1_info *aFti)
 {
   int aw = 0;
   int ah = 0;
-  FT_UShort upm = aFti->face->units_per_EM;
+  FT_UShort upm = get_upm (aFti->face);
   FT_GlyphSlot slot;
   FT_Glyph glyph;
   FT_BBox bbox;
 
   slot = aFti->face->glyph;
 
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
   FT_Error error = FT_Get_Glyph(slot, &glyph);
   if (error) {
     NS_ERROR("sideWidthAndBearing failed to get glyph");
@@ -256,7 +272,7 @@ static int
 moveto(nsFT_CONST FT_Vector *aEndPt, void *aClosure)
 {
   FT2PT1_info *fti = (FT2PT1_info *)aClosure;
-  FT_UShort upm = fti->face->units_per_EM;
+  FT_UShort upm = get_upm (fti->face);
   PRBool rslt;
 
   if (fti->elm_cnt == 0) {
@@ -293,7 +309,7 @@ static int
 lineto(nsFT_CONST FT_Vector *aEndPt, void *aClosure)
 {
   FT2PT1_info *fti = (FT2PT1_info *)aClosure;
-  FT_UShort upm = fti->face->units_per_EM;
+  FT_UShort upm = get_upm (fti->face);
 
   if (toCS(upm, aEndPt->x) == fti->cur_x) {
     fti->len += ecsi(&fti->buf, toCS(upm, aEndPt->y) - (int)fti->cur_y);
@@ -320,7 +336,7 @@ conicto(nsFT_CONST FT_Vector *aControlPt
         void *aClosure)
 {
   FT2PT1_info *ftinfo = (FT2PT1_info *)aClosure;
-  FT_UShort upm = ftinfo->face->units_per_EM;
+  FT_UShort upm = get_upm (ftinfo->face);
   double ctl_x, ctl_y;
   double cur_x, cur_y, x3, y3;
   FT_Vector aControlPt1, aControlPt2;
@@ -353,7 +369,7 @@ cubicto(nsFT_CONST FT_Vector *aControlPt
         nsFT_CONST FT_Vector *aEndPt, void *aClosure)
 {
   FT2PT1_info *ftinfo = (FT2PT1_info *)aClosure;
-  FT_UShort upm = ftinfo->face->units_per_EM;
+  FT_UShort upm = get_upm (ftinfo->face);
   double cur_x, cur_y, x1, y1, x2, y2, x3, y3;
 
   cur_x = ftinfo->cur_x;
@@ -408,8 +424,55 @@ static FT_Outline_Funcs ft_outline_funcs
   0
 };
 
+
+static int
+trace_bitmap_glyph (FT_GlyphSlot slot, FT2PT1_info *fti)
+{
+  unsigned char *row, *byte_ptr, byte;
+  int rows, cols;
+  int x, y, bit_mask;
+  int upm, x_off, y_off, x_mult, y_mult;
+
+  upm = get_upm (slot->face);
+  x_off = slot->bitmap_left;
+  y_off = slot->bitmap_top;
+  x_mult = upm / slot->face->size->metrics.x_ppem;
+  y_mult = upm / slot->face->size->metrics.y_ppem;
+
+  switch (slot->bitmap.pixel_mode) {
+  case FT_PIXEL_MODE_MONO:
+
+    for (y = 0, row = slot->bitmap.buffer, rows = slot->bitmap.rows; rows; row += slot->bitmap.pitch, rows--, y++) {
+	for (x = 0, byte_ptr = row, cols = (slot->bitmap.width + 7) / 8; cols; byte_ptr++, cols--) {
+	    byte = *byte_ptr;
+	    for (bit_mask = 128; bit_mask && x < slot->bitmap.width; bit_mask >>= 1, x++) {
+		if (byte & bit_mask) {
+		    FT_Vector p;
+		    p.x = x_mult * (x_off + x);
+		    p.y = y_mult * (y_off - y);
+		    moveto(&p, (void *) fti);
+		    p.x += x_mult;
+		    lineto(&p, (void *) fti);
+		    p.y += y_mult;
+		    lineto(&p, (void *) fti);
+		    p.x -= x_mult;
+		    lineto(&p, (void *) fti);
+		}
+	    }
+	}
+    }
+    break;
+
+  default:
+    return 1;
+  }
+
+  return 0;
+}
+
+
 FT_Error
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
 FT2GlyphToType1CharString(FT_Face aFace, PRUint32 aGlyphID,
                           int aWmode, int aLenIV, unsigned char *aBuf)
 #else
@@ -423,7 +486,7 @@ FT2GlyphToType1CharString(nsIFreeType2 *
   unsigned char *start = aBuf;
   FT2PT1_info fti;
 
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
   FT_Error error = FT_Load_Glyph(aFace, aGlyphID, flags);
   if (error) {
     NS_ERROR("failed to load aGlyphID");
@@ -438,11 +501,6 @@ FT2GlyphToType1CharString(nsIFreeType2 *
 #endif
   slot = aFace->glyph;
 
-  if (slot->format != ft_glyph_format_outline) {
-    NS_ERROR("aGlyphID is not an outline glyph");
-    return 1;
-  }
-
 #ifdef MOZ_ENABLE_FREETYPE2
   fti.ft2     = aFt2;
 #endif
@@ -456,18 +514,27 @@ FT2GlyphToType1CharString(nsIFreeType2 *
   for (j=0; j< aLenIV; j++) {
     fti.len += ecsi(&fti.buf, 0);
   }
-#ifdef MOZ_ENABLE_XFT
-  if (FT_Outline_Decompose(&slot->outline, &ft_outline_funcs, &fti))  {
-    NS_ERROR("error decomposing aGlyphID");
-    return 1;
-  }
+
+  if (slot->format == ft_glyph_format_outline) {
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
+    if (FT_Outline_Decompose(&slot->outline, &ft_outline_funcs, &fti))  {
+      NS_ERROR("error decomposing aGlyphID");
+      return 1;
+    }
 #else
-  rv = aFt2->OutlineDecompose(&slot->outline, &ft_outline_funcs, &fti);
-  if (NS_FAILED(rv)) {
-    NS_ERROR("error decomposing aGlyphID");
-    return 1;
-  }
+    rv = aFt2->OutlineDecompose(&slot->outline, &ft_outline_funcs, &fti);
+    if (NS_FAILED(rv)) {
+      NS_ERROR("error decomposing aGlyphID");
+    }
 #endif
+  } else if (slot->format == ft_glyph_format_bitmap) {
+    /* ok, it's a bitmap glyph.  trace it! */
+    if (trace_bitmap_glyph (slot, &fti)) {
+      NS_ERROR("error tracing bitmap glyph");
+    }
+  } else {
+      NS_ERROR("aGlyphID has unhandled format");
+  }
 
   if (fti.elm_cnt) {
     fti.len += csc(&fti.buf, T1_CLOSEPATH);
@@ -491,28 +558,52 @@ FT2GlyphToType1CharString(nsIFreeType2 *
 }
 
 static PRBool
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
 outputType1SubFont(FT_Face aFace,
 #else
 outputType1SubFont(nsIFreeType2 *aFt2, FT_Face aFace,
 #endif
-                 const nsAString &aCharIDs, const char *aFontName,
-                 int aWmode, int aLenIV, FILE *aFile);
+                   nsValueArray *aGlyphs,
+                   PRUint32 aOffset, PRUint32 aLen,
+                   const char *aFontName,
+                   int aWmode, int aLenIV, FILE *aFile);
 
 nsresult
 FT2ToType1FontName(FT_Face aFace, int aWmode, nsCString& aFontName)
 {
+  // only hash the first 10 000 bytes of the font
+  int size = aFace->stream->size;
+  size = size > 10000 ? 10000 : size;
+
+  unsigned char *data;
+  if (aFace->stream->read) {
+    data = (unsigned char *) malloc (size);
+    aFace->stream->read (aFace->stream, 0, data, size);
+  } else {
+    data = aFace->stream->base;
+  }
+
+  unsigned int data_hash = 0;
+  int i;
+  for (i = 0; i < size; i++)
+    data_hash = (data_hash << 5) - data_hash + data[size];
+
+  if (aFace->stream->read)
+    free (data);
+
   aFontName = aFace->family_name;
   aFontName.AppendLiteral(".");
   aFontName += aFace->style_name;
-  aFontName += nsPrintfCString(".%ld.%d", aFace->face_index, aWmode ? 1 : 0);
+  aFontName += nsPrintfCString(".%ld.%d.%lx.%x", aFace->face_index, aWmode ? 1 : 0,
+                               (long) aFace->stream->size, data_hash);
   flattenName(aFontName);
+
   return NS_OK;
 }
 
 // output a subsetted truetype font converted to multiple type 1 fonts
 PRBool
-FT2SubsetToType1FontSet(FT_Face aFace, const nsString& aSubset,
+FT2SubsetToType1FontSet(FT_Face aFace, nsValueArray *aGlyphSubset,
                         int aWmode,  FILE *aFile)
 {
 #ifdef MOZ_ENABLE_FREETYPE2
@@ -527,32 +618,35 @@ FT2SubsetToType1FontSet(FT_Face aFace, c
   nsCAutoString fontNameBase;
   FT2ToType1FontName(aFace, aWmode, fontNameBase);
   PRUint32 i = 0;
-  for (; i <= aSubset.Length() / 255 ; i++) {
+  for (; i <= aGlyphSubset->Count() / 255 ; i++) {
     nsCAutoString fontName(fontNameBase);
     fontName.AppendLiteral(".Set");
     fontName.AppendInt(i);
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
     outputType1SubFont(aFace,
 #else
     outputType1SubFont(ft2, aFace, 
 #endif
-      Substring(aSubset, i * 255, PR_MIN(255, aSubset.Length() - i * 255)),
-      fontName.get(), aWmode, 4, aFile);
+                       aGlyphSubset,
+                       (i * 255), PR_MIN(255, aGlyphSubset->Count() - i * 255),
+                       fontName.get(), aWmode, 4, aFile);
   }
   return PR_TRUE;
 }
 
 // output a type 1 font (with 255 characters or fewer) 
 static PRBool
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
 outputType1SubFont(FT_Face aFace,
 #else
 outputType1SubFont(nsIFreeType2 *aFt2, FT_Face aFace,
 #endif
-                 const nsAString& aCharIDs, const char *aFontName,
-                 int aWmode, int aLenIV, FILE *aFile)
+                   nsValueArray *aGlyphs,
+                   PRUint32 aOffset, PRUint32 aLen,
+                   const char *aFontName,
+                   int aWmode, int aLenIV, FILE *aFile)
 {
-  FT_UShort upm = aFace->units_per_EM;
+  FT_UShort upm = get_upm (aFace);
 
   fprintf(aFile, "%%%%BeginResource: font %s\n"
                  "%%!PS-AdobeFont-1.0-3.0 %s 1.0\n"
@@ -573,9 +667,13 @@ outputType1SubFont(nsIFreeType2 *aFt2, F
                  toCS(upm, aFace->bbox.xMax),
                  toCS(upm, aFace->bbox.yMax));
 
-  nsString charIDstr(aCharIDs);
-  PRUint32 len = aCharIDs.Length();
-  
+  nsValueArray glyphs(PR_UINT16_MAX);
+  nsCStringArray glyphnames(PR_UINT16_MAX);
+  glyphs = *aGlyphs;
+
+  PRUint32 len = aLen;
+  PRUint32 i;
+
   if (len < 10) { 
     // Add a small set of characters to the subset of the user
     // defined font to produce to make sure the font ends up
@@ -584,25 +682,47 @@ outputType1SubFont(nsIFreeType2 *aFt2, F
     // XXX : need to check if this is true of type 1 fonts as well.
     // I suspect it's only the case of CID-keyed fonts (type 9) we used to
     // generate. 
-    charIDstr.AppendLiteral("1234567890"); 
+    for (i = 1; i <= 10; i++) {
+      glyphs.AppendValue(i);
+    }
     len += 10;
   }
   
-  const PRUnichar *charIDs = charIDstr.get();
-
-  PRUint32 i;
+  FT_Int has_glyph_name;
+#if defined (MOZ_ENABLE_XFT) || defined (MOZ_ENABLE_PANGO)
+  has_glyph_name = FT_Has_PS_Glyph_Names(aFace);
+#else
+  has_glyph_name = aFt2->hasPSGlyphNames(aFace);
+#endif
 
   // construct an Encoding vector : the 0th element
   // is /.notdef
-  fputs("/Encoding [\n/.notdef\n", aFile); 
-  for (i = 0; i < len; ++i) {
-      fprintf(aFile, "/uni%04X", charIDs[i]); 
-      if (i % 8 == 7) fputc('\n', aFile);
+  fputs("/Encoding [\n/.notdef", aFile); 
+  for (i = aOffset; i < aOffset + aLen; ++i) {
+      nsCString name;
+      char buffer[256];
+
+      if (glyphs.ValueAt(i) == 0) {
+        name = "/.notdef";
+      } else if (!has_glyph_name ||
+#if defined (MOZ_ENABLE_XFT) || defined (MOZ_ENABLE_PANGO)
+                 FT_Get_Glyph_Name(aFace, glyphs.ValueAt(i), buffer, 255) != FT_Err_Ok
+#else
+                 NS_FAILED(aFt2->getGlyphName(aFace, glyphs.ValueAt(i), buffer, 255))
+#endif
+      ) {
+        name = nsPrintfCString(256, "/idx%04X", glyphs.ValueAt(i));
+      } else {
+        name = nsPrintfCString(256, "/%s", buffer);
+      }
+      glyphnames.AppendCString(name);
+      fprintf(aFile, name.get());
+      if ((i-aOffset) % 8 == 6) fputc('\n', aFile);
   }
 
-  for (i = len; i < 255; ++i) {
+  for (i = PR_MAX (0, 255 - int(aLen)); i; --i) {
       fputs("/.notdef", aFile);
-      if (i % 8 == 7) fputc('\n', aFile);
+      if (i % 8 == 1) fputc('\n', aFile);
   }
   fputs("] def\n", aFile); 
 
@@ -630,23 +750,21 @@ outputType1SubFont(nsIFreeType2 *aFt2, F
   // get the maximum charstring length without actually filling up the buffer
   PRInt32 charStringLen;
   PRInt32 maxCharStringLen =
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
     FT2GlyphToType1CharString(aFace, 0, aWmode, aLenIV, nsnull);
 #else
     FT2GlyphToType1CharString(aFt2, aFace, 0, aWmode, aLenIV, nsnull);
 #endif
 
-  PRUint32 glyphID;
-
-  for (i = 0; i < len; i++) {
-#ifdef MOZ_ENABLE_XFT
-    glyphID = FT_Get_Char_Index(aFace, charIDs[i]);
+  for (i = aOffset; i < aOffset + aLen; i++) {
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
     charStringLen =
-      FT2GlyphToType1CharString(aFace, glyphID, aWmode, aLenIV, nsnull);
+      FT2GlyphToType1CharString(aFace, glyphs.ValueAt(i), aWmode, aLenIV,
+                                nsnull);
 #else
-    aFt2->GetCharIndex(aFace, charIDs[i], &glyphID);
     charStringLen =
-      FT2GlyphToType1CharString(aFt2, aFace, glyphID, aWmode, aLenIV, nsnull);
+      FT2GlyphToType1CharString(aFt2, aFace, glyphs.ValueAt(i), aWmode, aLenIV,
+                                nsnull);
 #endif
 
     if (charStringLen > maxCharStringLen)
@@ -666,7 +784,7 @@ outputType1SubFont(nsIFreeType2 *aFt2, F
                                    len + 1).get()); 
 
   // output the notdef glyph
-#ifdef MOZ_ENABLE_XFT
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
   charStringLen = FT2GlyphToType1CharString(aFace, 0, aWmode, aLenIV,
                                             charString.get());
 #else
@@ -676,22 +794,20 @@ outputType1SubFont(nsIFreeType2 *aFt2, F
 
   // enclose charString with  "/.notdef RD .....  ND" 
   charStringOut(aFile, &pos, &key, NS_REINTERPRET_CAST(const char*, charString.get()),
-                charStringLen, 0); 
+                charStringLen, "/.notdef");
 
 
   // output the charstrings for each glyph in this sub font
-  for (i = 0; i < len; i++) {
-#ifdef MOZ_ENABLE_XFT
-    glyphID = FT_Get_Char_Index(aFace, charIDs[i]);
-    charStringLen = FT2GlyphToType1CharString(aFace, glyphID, aWmode,
+  for (i = aOffset; i < aOffset + aLen; i++) {
+#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_PANGO)
+    charStringLen = FT2GlyphToType1CharString(aFace, glyphs.ValueAt(i), aWmode,
                                               aLenIV, charString.get());
 #else
-    aFt2->GetCharIndex(aFace, charIDs[i], &glyphID);
-    charStringLen = FT2GlyphToType1CharString(aFt2, aFace, glyphID, aWmode,
-                                              aLenIV, charString.get());
+    charStringLen = FT2GlyphToType1CharString(aFt2, aFace, glyphs.ValueAt(i),
+                                              aWmode, aLenIV, charString.get());
 #endif
     charStringOut(aFile, &pos, &key, NS_REINTERPRET_CAST(const char*, charString.get()),
-                  charStringLen, charIDs[i]);
+                  charStringLen, glyphnames.CStringAt(i - aOffset)->get());
   }
 
   // wrap up the encrypted part of the font definition
@@ -753,15 +869,12 @@ void encryptAndHexOut(FILE *aFile, PRUin
 
 /* static */ 
 void charStringOut(FILE* aFile,  PRUint32* aPos, PRUint16* aKey, 
-                   const char *aStr, PRUint32 aLen, PRUnichar aId)
+                   const char *aStr, PRUint32 aLen, const char *aGlyphName)
 {
     // use a local buffer instead of nsPrintfCString to avoid alloc.
     char buf[30];
     int oLen;
-    if (aId == 0)
-      oLen = PR_snprintf(buf, 30, "/.notdef %d RD ", aLen); 
-    else 
-      oLen = PR_snprintf(buf, 30, "/uni%04X %d RD ", aId, aLen); 
+    oLen = PR_snprintf(buf, 30, "%s %d RD ", aGlyphName, aLen);
 
     if (oLen >= 30) {
       NS_WARNING("buffer size exceeded. charstring will be truncated");
Index: gfx/src/ps/nsType1.h
===================================================================
RCS file: /cvsroot/mozilla/gfx/src/ps/nsType1.h,v
retrieving revision 1.5
diff -u -p -d -r1.5 nsType1.h
--- gfx/src/ps/nsType1.h	4 Mar 2005 07:39:27 -0000	1.5
+++ gfx/src/ps/nsType1.h	23 Oct 2006 17:37:39 -0000
@@ -122,8 +122,9 @@ FT_Error FT2GlyphToType1CharString(nsIFr
 
 class nsString;
 class nsCString;
+class nsValueArray;
 
-PRBool FT2SubsetToType1FontSet(FT_Face aFace, const nsString& aSubset,
+PRBool FT2SubsetToType1FontSet(FT_Face aFace, nsValueArray *aGlyphSubset,
                                int aWmode,  FILE *aFile);
 nsresult FT2ToType1FontName(FT_Face aFace, int aWmode,
                             nsCString& aFontName);
Index: config/system-headers
===================================================================
--- config/system-headers	2006-10-26 12:21:39.000000000 -0400
+++ config/system-headers	2006-10-26 12:23:29.000000000 -0400
@@ -199,6 +199,7 @@
 freetype/ftoutln.h
 freetype/ttnameid.h
 freetype/tttables.h
+freetype/t1tables.h
 fribidi/fribidi.h
 FSp_fopen.h
 fstream.h
@@ -208,6 +209,7 @@
 gdk/gdkevents.h
 gdk/gdk.h
 gdk/gdkkeysyms.h
+gdk/gdkpango.h
 gdk/gdkprivate.h
 gdk/gdkregion.h
 gdk/gdkwindow.h
@@ -501,6 +503,7 @@
 pango/pangofc-fontmap.h
 pango/pango-fontmap.h
 pango/pango.h
+pango/pangoft2.h
 pango/pangoxft.h
 pango/pangox.h
 pango-types.h