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 + * . 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 +#include + +#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 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 props; + nsCOMPtr 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 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 encoder; + nsCOMPtr 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 + * . 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 + +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 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 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 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; iSetupFont(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 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; iSetupFont(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 + * . Portions created by the Initial Developer + * are Copyright (C) 2004 the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Christopher Blizzard + * Behdad Esfahbod + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * 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 +#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 +#include +#include +#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> 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 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 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 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 . + * 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 + +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 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 +#endif + +class nsPSFontGenerator; class nsIImage; class nsIAtom; +class nsValueArray; #endif #include @@ -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 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; iFont(), 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 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; iFont(), 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