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
@@ -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