/* ***** 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
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Roland Mainz <roland.mainz@informatik.med.uni-giessen.de>
 *   Ervin Yan <Ervin.Yan@sun.com>
 *   Christopher Blizzard <blizzard@mozilla.org>
 *   Jungshik Shin <jshin@mailaps.org>
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either of 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 "gfx-config.h"
#include "nsFontMetricsPS.h"
#include "nsDeviceContextPS.h"
#include "nsRenderingContextPS.h"
#include "nsIServiceManager.h"
#include "nsGfxCIID.h"

#include "nsIPref.h"
#include "nsVoidArray.h"
#include "nsReadableUtils.h"
#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
#include "nsType1.h"
#endif
#include "prlog.h"

#ifdef MOZ_ENABLE_FREETYPE2
#include "nsArray.h"
#endif

const PRUint16 nsPSFontGenerator::kSubFontSize = 255; 

extern nsIAtom *gUsersLocale;
#define NS_IS_BOLD(weight) ((weight) > 400 ? 1 : 0)

#ifdef MOZ_ENABLE_XFT

#include "nsFontConfigUtils.h"

static nsFontPS* CreateFontPS(nsXftEntry*, const nsFont&,
                              nsFontMetricsPS*);

#else
#ifdef MOZ_ENABLE_FREETYPE2
static nsFontPS* CreateFontPS(nsITrueTypeFontCatalogEntry*, const nsFont&,
                              nsFontMetricsPS*);

static NS_DEFINE_CID(kFCSCID, NS_FONTCATALOGSERVICE_CID);
#endif
#endif

#ifdef PR_LOGGING
static PRLogModuleInfo *gFontMetricsPSM = PR_NewLogModule("FontMetricsPS");
#endif

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
nsFontMetricsPS :: nsFontMetricsPS()
{
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
nsFontMetricsPS :: ~nsFontMetricsPS()
{
  if (mFontsPS) {
    int i;
    for (i=0; i<mFontsPS->Count(); i++) {
      fontps *fontPS = (fontps *)mFontsPS->ElementAt(i);
      if (!fontPS)
        continue;
      if (fontPS->fontps)
        delete fontPS->fontps;
#ifdef MOZ_ENABLE_XFT
      if (fontPS->entry)
        delete fontPS->entry;
      if (fontPS->charset)
        FcCharSetDestroy(fontPS->charset);
#else
#ifdef MOZ_ENABLE_FREETYPE2
      NS_IF_RELEASE(fontPS->entry);
      if (fontPS->ccmap)
        FreeCCMap(fontPS->ccmap);
#endif
#endif
      delete fontPS;
    }
    delete mFontsPS;
  }

  if (mFontsAlreadyLoaded) {
    delete mFontsAlreadyLoaded;
  }

  if (mDeviceContext) {
    // Notify our device context that owns us so that it can update its font cache
    mDeviceContext->FontMetricsDeleted(this);
    mDeviceContext = nsnull;
  }
}

NS_IMPL_ISUPPORTS1(nsFontMetricsPS, nsIFontMetrics)

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: Init(const nsFont& aFont, nsIAtom* aLangGroup,
  nsIDeviceContext *aContext)
{
  mLangGroup = aLangGroup;

  mFont = aFont;

  //don't addref this to avoid circular refs
  mDeviceContext = (nsDeviceContextPS *)aContext;

  mFontsPS = new nsVoidArray();
  NS_ENSURE_TRUE(mFontsPS, NS_ERROR_OUT_OF_MEMORY);
  mFontsAlreadyLoaded = new nsHashtable();
  NS_ENSURE_TRUE(mFontsAlreadyLoaded, NS_ERROR_OUT_OF_MEMORY);

  // make sure we have at least one font
  nsFontPS *fontPS = nsFontPS::FindFont('a', aFont, this);
  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);

  RealizeFont();
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsPS :: Destroy()
{
  mDeviceContext = nsnull;
  return NS_OK;
}


/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
void
nsFontMetricsPS::RealizeFont()
{
  if (mDeviceContext) {
    float dev2app;
    dev2app = mDeviceContext->DevUnitsToAppUnits();
    fontps *font = (fontps*)mFontsPS->ElementAt(0);
#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
    NS_ASSERTION(font && font->entry, "no font available");
    if (font && !font->fontps && font->entry)
      font->fontps = CreateFontPS(font->entry, mFont, this);
#endif
    NS_ASSERTION(font && font->fontps, "no font available");
    if (font && font->fontps)
      font->fontps->RealizeFont(this, dev2app);
  }
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetXHeight(nscoord& aResult)
{
  aResult = mXHeight;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetSuperscriptOffset(nscoord& aResult)
{
  aResult = mSuperscriptOffset;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetSubscriptOffset(nscoord& aResult)
{
  aResult = mSubscriptOffset;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetStrikeout(nscoord& aOffset, nscoord& aSize)
{
  aOffset = mStrikeoutOffset;
  aSize = mStrikeoutSize;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetUnderline(nscoord& aOffset, nscoord& aSize)
{
  aOffset = mUnderlineOffset;
  aSize = mUnderlineSize;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetHeight(nscoord &aHeight)
{
  aHeight = mHeight;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetNormalLineHeight(nscoord &aHeight)
{
  aHeight = mEmHeight + mLeading;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetLeading(nscoord &aLeading)
{
  aLeading = mLeading;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetEmHeight(nscoord &aHeight)
{
  aHeight = mEmHeight;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetEmAscent(nscoord &aAscent)
{
  aAscent = mEmAscent;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetEmDescent(nscoord &aDescent)
{
  aDescent = mEmDescent;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetMaxHeight(nscoord &aHeight)
{
  aHeight = mMaxHeight;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetMaxAscent(nscoord &aAscent)
{
  aAscent = mMaxAscent;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetMaxDescent(nscoord &aDescent)
{
  aDescent = mMaxDescent;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetMaxAdvance(nscoord &aAdvance)
{
  aAdvance = mMaxAdvance;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsPS :: GetAveCharWidth(nscoord &aAveCharWidth)
{
  aAveCharWidth = mAveCharWidth;
  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetSpaceWidth(nscoord &aSpaceWidth)
{
  aSpaceWidth = mSpaceWidth;
  return NS_OK;
}

NS_IMETHODIMP
nsFontMetricsPS :: GetLangGroup(nsIAtom** aLangGroup)
{
  if (!aLangGroup) {
    return NS_ERROR_NULL_POINTER;
  }

  *aLangGroup = mLangGroup;
  NS_IF_ADDREF(*aLangGroup);

  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS::GetFontHandle(nsFontHandle &aHandle)
{

  return NS_OK;
}

/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetStringWidth(const char *aString,nscoord& aWidth,nscoord aLength)
{
  aWidth = 0;
  if (aLength == 0)
    return NS_OK;
  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], mFont, this);
  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);

  nscoord i, start = 0;
  for (i=0; i<aLength; i++) {
    nsFontPS* fontThisChar = nsFontPS::FindFont(aString[i], mFont, this);
    NS_ASSERTION(fontThisChar,"failed to find a font");
    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
    if (fontThisChar != fontPS) {
      // measure text up to this point
      aWidth += fontPS->GetWidth(aString+start, i-start);
      start = i;
      fontPS = fontThisChar;
    }
  }
  // measure the last part
  if (aLength-start)
      aWidth += fontPS->GetWidth(aString+start, aLength-start);

  return NS_OK;
}


/** ---------------------------------------------------
 *  See documentation in nsFontMetricsPS.h
 *	@update 2/26/99 dwc
 */
NS_IMETHODIMP
nsFontMetricsPS :: GetStringWidth(const PRUnichar *aString,nscoord& aWidth,nscoord aLength)
{
  aWidth = 0;
  if (aLength == 0)
    return NS_OK;
  nsFontPS* fontPS = nsFontPS::FindFont(aString[0], mFont, this);
  NS_ENSURE_TRUE(fontPS, NS_ERROR_FAILURE);

  nscoord i, start = 0;
  for (i=0; i<aLength; i++) {
    nsFontPS* fontThisChar = nsFontPS::FindFont(aString[i], mFont, this);
    NS_ASSERTION(fontThisChar,"failed to find a font");
    NS_ENSURE_TRUE(fontThisChar, NS_ERROR_FAILURE);
    if (fontThisChar != fontPS) {
      // measure text up to this point
      aWidth += fontPS->GetWidth(aString+start, i-start);
      start = i;
      fontPS = fontThisChar;
    }
  }
  // measure the last part
  if (aLength-start)
      aWidth += fontPS->GetWidth(aString+start, aLength-start);

  return NS_OK;
}

nsFontPS*
nsFontPS::FindFont(char aChar, const nsFont& aFont, 
                   nsFontMetricsPS* aFontMetrics)
{
  PRUnichar uc = (unsigned char)aChar;
  return FindFont(uc, aFont, aFontMetrics);
}

// nsFontPS
nsFontPS*
nsFontPS::FindFont(PRUnichar aChar, const nsFont& aFont, 
                   nsFontMetricsPS* aFontMetrics)
{
  nsFontPS* fontPS;

#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)
  nsDeviceContextPS* dc = aFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, nsnull);
  if (dc->mFTPEnable) {
#ifdef MOZ_ENABLE_XFT
    fontPS = nsFontPSXft::FindFont(aChar, aFont, aFontMetrics);
#else
    fontPS = nsFontPSFreeType::FindFont(aChar, aFont, aFontMetrics);
#endif
    if (fontPS)
      return fontPS;
  }
#endif

  /* Find in afm font */
  nsVoidArray *fonts = aFontMetrics->GetFontsPS();
  if (!fonts)
    return nsnull;

  if (fonts->Count() > 0) {
    fontps *fps = (fontps*)fonts->ElementAt(0);
    NS_ENSURE_TRUE(fps, nsnull);
    fontPS = fps->fontps;
  }
  else {
    fontPS = nsFontPSAFM::FindFont(aFont, aFontMetrics);
    fontps *fps = new fontps;
    NS_ENSURE_TRUE(fps, nsnull);
    fps->fontps = fontPS;
#if defined(MOZ_ENABLE_XFT) || defined(MOZ_ENABLE_FREETYPE2) 
    fps->entry  = nsnull;
#ifdef MOZ_ENABLE_XFT
    fps->charset = nsnull;
#else
    fps->ccmap  = nsnull;
#endif
#endif
    fonts->AppendElement(fps);
  }
  return fontPS;
}

nsFontPS::nsFontPS(const nsFont& aFont, nsFontMetricsPS* aFontMetrics)
{
  mFont = new nsFont(aFont);
  if (!mFont) return;
  mFontMetrics = aFontMetrics;
}

nsFontPS::~nsFontPS()
{
  if (mFont) {
    delete mFont;
    mFont = nsnull;
  }

  if (mCCMap) {
    FreeCCMap(mCCMap);
  }

  mFontMetrics = nsnull;
}

// nsFontPSAFM
nsFontPS*
nsFontPSAFM::FindFont(const nsFont& aFont, nsFontMetricsPS* aFontMetrics)
{
  nsAFMObject* afmInfo = new nsAFMObject();
  if (!afmInfo) return nsnull;
  afmInfo->Init(aFont.size);

  PRInt16 fontIndex = afmInfo->CheckBasicFonts(aFont, PR_TRUE);
  if (fontIndex < 0) {
    if (!(afmInfo->AFM_ReadFile(aFont))) {
      fontIndex = afmInfo->CheckBasicFonts(aFont, PR_FALSE);
      if (fontIndex < 0) {
        fontIndex = afmInfo->CreateSubstituteFont(aFont);
      }
    }
  }

  nsFontPSAFM* fontPSAFM = nsnull;
  if (fontIndex < 0)
    delete afmInfo;
  else
    fontPSAFM = new nsFontPSAFM(aFont, afmInfo, fontIndex, aFontMetrics);
  return fontPSAFM;
}

nsFontPSAFM::nsFontPSAFM(const nsFont& aFont, nsAFMObject* aAFMInfo,
                         PRInt16 fontIndex, nsFontMetricsPS* aFontMetrics) :
nsFontPS(aFont, aFontMetrics), mAFMInfo(aAFMInfo), mFontIndex(fontIndex)
{
  if (!(mFont && mAFMInfo)) return;
  AppendASCIItoUTF16(mAFMInfo->mPSFontInfo->mFamilyName, mFamilyName);
}

nsFontPSAFM::~nsFontPSAFM()
{
  if (mAFMInfo) {
    delete mAFMInfo;
    mAFMInfo = nsnull;
  }
}

nscoord
nsFontPSAFM::GetWidth(const char* aString, PRUint32 aLength)
{
  nscoord width = 0;
  if (mAFMInfo) {
    mAFMInfo->GetStringWidth(aString, width, aLength);
  }
  return width;
}

nscoord
nsFontPSAFM::GetWidth(const PRUnichar* aString, PRUint32 aLength)
{
  nscoord width = 0;
  if (mAFMInfo) {
    mAFMInfo->GetStringWidth(aString, width, aLength);
  }
  return width;
}

nscoord
nsFontPSAFM::DrawString(nsRenderingContextPS* aContext,
                        nscoord aX, nscoord aY,
                        const char* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext, 0);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, 0);

  psObj->moveto(aX, aY);
  psObj->show(aString, aLength, "");
  return GetWidth(aString, aLength);
}

nscoord
nsFontPSAFM::DrawString(nsRenderingContextPS* aContext,
                        nscoord aX, nscoord aY,
                        const PRUnichar* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext, 0);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, 0);

  psObj->moveto(aX, aY);
  psObj->show(aString, aLength, "", 0);
  return GetWidth(aString, aLength);
}

nsresult
nsFontPSAFM::RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app)
{
  NS_ENSURE_ARG_POINTER(aFontMetrics);

  float fontSize;
  float offset;

  nscoord onePixel = NSToCoordRound(1 * dev2app);

  // convert the font size which is in twips to points
  fontSize = mFont->size / TWIPS_PER_POINT_FLOAT;

  offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mXHeight) / 1000.0f;
  nscoord xHeight = NSToCoordRound(offset);
  aFontMetrics->SetXHeight(xHeight);
  aFontMetrics->SetSuperscriptOffset(xHeight);
  aFontMetrics->SetSubscriptOffset(xHeight);
  aFontMetrics->SetStrikeout((nscoord)(xHeight / 2), onePixel);

  offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mUnderlinePosition) / 1000.0f;
  aFontMetrics->SetUnderline(NSToCoordRound(offset), onePixel);

  nscoord size = NSToCoordRound(fontSize * dev2app);
  aFontMetrics->SetHeight(size);
  aFontMetrics->SetEmHeight(size);
  aFontMetrics->SetMaxAdvance(size);
  aFontMetrics->SetMaxHeight(size);

  offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mAscender) / 1000.0f;
  nscoord ascent = NSToCoordRound(offset);
  aFontMetrics->SetAscent(ascent);
  aFontMetrics->SetEmAscent(ascent);
  aFontMetrics->SetMaxAscent(ascent);

  offset = NSFloatPointsToTwips(fontSize * mAFMInfo->mPSFontInfo->mDescender) / 1000.0f;
  nscoord descent = -(NSToCoordRound(offset));
  aFontMetrics->SetDescent(descent);
  aFontMetrics->SetEmDescent(descent);
  aFontMetrics->SetMaxDescent(descent);

  aFontMetrics->SetLeading(0);

  nscoord spaceWidth = GetWidth(" ", 1);
  aFontMetrics->SetSpaceWidth(spaceWidth);

  nscoord aveCharWidth = GetWidth("x", 1);
  aFontMetrics->SetAveCharWidth(aveCharWidth);

  return NS_OK;
}

nsresult
nsFontPSAFM::SetupFont(nsRenderingContextPS* aContext)
{
  NS_ENSURE_TRUE(aContext && mFontMetrics, 0);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, 0);

  nscoord fontHeight = 0;
  mFontMetrics->GetHeight(fontHeight);

  psObj->setscriptfont(mFontIndex, mFamilyName,
                       fontHeight, mFont->style, mFont->variant,
                       mFont->weight, mFont->decorations);
  return NS_OK;
}

#ifdef MOZ_MATHML
nsresult
nsFontPSAFM::GetBoundingMetrics(const char*        aString,
                                PRUint32           aLength,
                                nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult
nsFontPSAFM::GetBoundingMetrics(const PRUnichar*   aString,
                                PRUint32           aLength,
                                nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}
#endif

#if defined(MOZ_ENABLE_FREETYPE2) || defined(MOZ_ENABLE_XFT)

#define WIDEN_8_TO_16_BUF_SIZE 1024

#ifdef MOZ_ENABLE_XFT

nsXftEntry::nsXftEntry(FcPattern *aFontPattern)
{
  mFace = nsnull;
  mFaceIndex = 0;

  char *fcResult;

  if (FcPatternGetString(aFontPattern, FC_FILE, 0, (FcChar8 **) &fcResult)
      == FcResultMatch)     
    mFontFileName = fcResult;

  if (FcPatternGetString(aFontPattern, FC_FAMILY, 0, (FcChar8 **) &fcResult)
      == FcResultMatch)     
    mFamilyName = fcResult;

  if (FcPatternGetString(aFontPattern, FC_STYLE, 0, (FcChar8 **) &fcResult)
      == FcResultMatch)
    mStyleName = fcResult;
}


static nsFontPS*
CreateFontPS(nsXftEntry *aEntry, const nsFont& aFont,
             nsFontMetricsPS* aFontMetrics)
{
  nsresult rv;
  nsDeviceContextPS* dc = aFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, nsnull);

  nsCAutoString fileName(aEntry->mFontFileName);
  nsCAutoString familyName(aEntry->mFamilyName);
  nsCAutoString styleName(aEntry->mStyleName);
  ToLowerCase(familyName);
  ToLowerCase(styleName);
  
  nsCAutoString fontName;
  fontName.Append(familyName);
  fontName.Append("-");
  fontName.Append(styleName);
  nsCStringKey key(fontName);

  nsHashtable *psFGList = dc->GetPSFontGeneratorList();
  NS_ENSURE_TRUE(psFGList, nsnull);
  
  nsPSFontGenerator* psFontGen = (nsPSFontGenerator*) psFGList->Get(&key);
  if (!psFontGen) {
    psFontGen = new nsXftType1Generator;
    NS_ENSURE_TRUE(psFontGen, nsnull);
    rv = ((nsXftType1Generator*)psFontGen)->Init(aEntry);
    if (NS_FAILED(rv)) {
      delete psFontGen;
      return nsnull;
    }
    psFGList->Put(&key, (void *) psFontGen);
  }
  nsFontPSXft* font = new nsFontPSXft(aFont, aFontMetrics);
  NS_ENSURE_TRUE(font, nsnull);
  rv = font->Init(aEntry, psFontGen);
  if (NS_FAILED(rv)) {
    delete font;
    return nsnull;
  }
  return font;
}

/* static */
PRBool
nsFontPSXft::CSSFontEnumCallback(const nsString& aFamily, PRBool aIsGeneric,
                                      void* aFpi)
{
    fontPSInfo *fpi = (fontPSInfo *)aFpi;


    // fontconfig always returns family names in UTF-8 so that we
    // should do the same here.
    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);
    fpi->mFontList.AppendCString(name);
    fpi->mFontIsGeneric.AppendElement((void *)aIsGeneric);
    if (aIsGeneric) {
        fpi->mGenericFont =
            fpi->mFontList.CStringAt(fpi->mFontList.Count() - 1);
        return PR_FALSE; // stop processing
    }

    return PR_TRUE; // keep processing
}

nsFontPS*
nsFontPSXft::FindFont(PRUnichar aChar, const nsFont& aFont, 
                           nsFontMetricsPS* aFontMetrics)
{
  PRBool inited = PR_FALSE;
  nsCOMPtr<nsIAtom> langGroup;
  fontPSInfo fpi;
  fpi.fontps = aFontMetrics->GetFontsPS();
  if (!fpi.fontps)
    return nsnull;

  int i = 0;

  while (1) {
    //
    // see if it is already in the list of fonts
    //
    for (; i<fpi.fontps->Count(); i++) {
      fontps *fi = (fontps *)fpi.fontps->ElementAt(i);
      if (!fi->entry || !fi->charset) {
        NS_ASSERTION(fi->entry, "invalid entry");
        NS_ASSERTION(fi->charset, "invalid charset");
        continue;
      }
      if (FcCharSetHasChar(fi->charset, aChar)) {
        if (!fi->fontps) {
#ifdef PR_LOGGING
          if (PR_LOG_TEST(gFontMetricsPSM, PR_LOG_DEBUG)) {
            PR_LogPrint("CreateFontPS %s/%s\n",
                fi->entry->mFamilyName.get(), fi->entry->mStyleName.get());
          }
#endif
          fi->fontps = CreateFontPS(fi->entry, aFont, aFontMetrics);
        }
        if (fi->fontps)
          return fi->fontps;
      }
    }

    // if already get all matched fonts and not found suitable
    // then return nsnull
    if (fpi.fontps->Count() > 0)
      return nsnull;

    //
    // it is not already in the list of fonts
    // so add more fonts to the list
    //
    if (inited)
      return nsnull;
 
    inited = PR_TRUE;

    fpi.nsfont = &aFont;
    fpi.alreadyLoaded = aFontMetrics->GetFontsAlreadyLoadedList();
    fpi.mGenericFont = nsnull;

    aFontMetrics->GetLangGroup(getter_AddRefs(langGroup));
    if (!langGroup)
      langGroup = NS_NewAtom("x-western");

    // enumerate over the font names passed in
    aFont.EnumerateFamilies(nsFontPSXft::CSSFontEnumCallback, &fpi);

    nsCOMPtr<nsIPref> prefService;
    prefService = do_GetService(NS_PREF_CONTRACTID);
    if (!prefService)
      return nsnull;

    nsXPIDLCString value;
    nsCAutoString  defaultFont;

    nsAutoString langGroupStr;
    langGroup->ToString(langGroupStr);

    // Set up the default font name if it's not set
    if (!fpi.mGenericFont) {
      nsCAutoString name("font.default.");
      LossyAppendUTF16toASCII(langGroupStr, name);
      prefService->CopyCharPref(name.get(), getter_Copies(value));

      if (value.get())
        defaultFont = value.get();
      else
        defaultFont = "serif";

      fpi.mGenericFont = &defaultFont;
    }

    // If pattern is null, set up the base bits of it so we can
    // match.  If we need to match later we don't have to set it up
    // again.
    FcPattern *pattern = nsnull;

    pattern = FcPatternCreate();
    if (!pattern)
      return nsnull;

    // XXX need to add user defined family

    // Add CSS names - walk the list of fonts, adding the generic as
    // the last font
    for (int i=0; i < fpi.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 (fpi.mFontIsGeneric[i])
        break;;

      nsCString *familyName = fpi.mFontList.CStringAt(i);
      NS_AddFFRE(pattern, familyName, PR_FALSE);
    }

    // Add the language group.  Note that we do this before adding any
    // generics.  That's because the language is more important than
    // any generic font.
    NS_AddLangGroup(pattern, langGroup);

    // If there's a generic add a pref for the generic if there's one
    // set.
    if (fpi.mGenericFont && !aFont.systemFont) {
      NS_AddGenericFontFromPref(fpi.mGenericFont, langGroup, pattern,
#ifdef PR_LOGGING
                                gFontMetricsPSM); 
#else 
                                nsnull);
#endif
    }

    // Add the generic if there is one.
    if (fpi.mGenericFont && !aFont.systemFont)
      NS_AddFFRE(pattern, fpi.mGenericFont, PR_FALSE);

    // Add the slant type
    FcPatternAddInteger(pattern, FC_SLANT, NS_CalculateSlant(aFont.style));

    // Add the weight
    FcPatternAddInteger(pattern, FC_WEIGHT, NS_CalculateWeight(aFont.weight));

    // Set up the default substitutions for this font
    FcConfigSubstitute(0, pattern, FcMatchPattern);
    FcDefaultSubstitute(pattern);

    FcResult   result;
    FcFontSet *set = FcFontSort(0, pattern, FcTrue, 0, &result);
    if (!set) {
       return nsnull;
    }

    // Create a list of new font objects based on the fonts returned
    // as part of the query
    for (int i=0; i < set->nfont; ++i) {
        FcBool fc_outline;
        FcChar8 *fc_family;
        FcChar8 *fc_style;
        FcCharSet *fc_charset;

        // skip if the fonts is not outline font files.
        result = FcPatternGetBool (set->fonts[i], FC_OUTLINE, 0, &fc_outline);
        if (!fc_outline)
          continue;

        result = FcPatternGetString (set->fonts[i], FC_FAMILY, 0, &fc_family);
        if (result != FcResultMatch || fc_family == nsnull)
          continue;

        result = FcPatternGetString (set->fonts[i], FC_STYLE, 0, &fc_style);
        if (result != FcResultMatch || fc_style == nsnull)
          continue;

        result = FcPatternGetCharSet(set->fonts[i], FC_CHARSET, 0, &fc_charset);
        if (result != FcResultMatch || fc_charset == nsnull)
          continue;

        nsXftEntry *xftEntry = new nsXftEntry(set->fonts[i]);
        if (!xftEntry)
          continue;

        fontps *fps = new fontps;
        fps->entry = xftEntry;
        fps->charset = fc_charset;
        fps->fontps = nsnull;
        fpi.fontps->AppendElement(fps);
    }

    // we're done with the set now
    FcFontSetDestroy(set);

    FcPatternDestroy(pattern);
  }

  return nsnull;
}

nsFontPSXft::nsFontPSXft(const nsFont& aFont,
                                   nsFontMetricsPS* aFontMetrics)
  :nsFontPS(aFont, aFontMetrics)
{
}

nsresult
nsFontPSXft::Init(nsXftEntry* aEntry,
                       nsPSFontGenerator* aPSFontGen)
{
  NS_ENSURE_TRUE(aEntry && aPSFontGen, NS_ERROR_FAILURE);
  mEntry = aEntry;
  mPSFontGenerator = aPSFontGen;

  float app2dev;
  nsIDeviceContext* dc = mFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, NS_ERROR_NULL_POINTER);
  app2dev = dc->AppUnitsToDevUnits();
  
  mPixelSize = NSToIntRound(app2dev * mFont->size);

  FT_Error fterror; 
  fterror = FT_Init_FreeType(&mFreeTypeLibrary);
  if (fterror) {
    NS_ERROR("failed to initialize FreeType library");
    mFreeTypeLibrary = nsnull;
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

nsFontPSXft::~nsFontPSXft()
{
  if (mEntry->mFace) 
    FT_Done_Face(mEntry->mFace);

  if (FT_Done_FreeType(mFreeTypeLibrary))
    return;

  mEntry = nsnull;
}

FT_Face
nsFontPSXft::getFTFace()
{
  FT_Face face = mEntry->mFace;

  if (face)
    return (face);

  if (FT_New_Face(mFreeTypeLibrary, mEntry->mFontFileName.get(), 
                  mEntry->mFaceIndex, &face) ||
      FT_Set_Pixel_Sizes(face, mPixelSize, 0))
    return nsnull;

  mEntry->mFace = face;
  return face;
}

nscoord
nsFontPSXft::GetWidth(const char* aString, PRUint32 aLength)
{
  return GetWidth(NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength);
}

nscoord
nsFontPSXft::GetWidth(const PRUnichar* aString, PRUint32 aLength)
{
  // get the face/size from the FreeType cache
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return 0;

  // XXX : we might need some caching here
  double em_size = 1.0 * face->units_per_EM;
  double x_scale = face->size->metrics.x_ppem / em_size;
  double origin_x = 0;
  for (PRUint32 i=0; i<aLength; i++) {
    FT_UInt glyph_index = FT_Get_Char_Index((FT_Face)face, aString[i]);
    if (FT_Load_Glyph(face, glyph_index, FT_LOAD_NO_SCALE ))
      origin_x += face->size->metrics.x_ppem/2 + 2;
    else
      origin_x += (face->glyph->advance.x) * x_scale;
  }

  NS_ENSURE_TRUE(mFontMetrics, 0);

  nsDeviceContextPS* dc = mFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, 0);

  origin_x *= dc->DevUnitsToAppUnits();

  return NSToCoordRound((nscoord)origin_x);
}

nscoord
nsFontPSXft::DrawString(nsRenderingContextPS* aContext,
                        nscoord aX, nscoord aY,
                        const char* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext, 0);
  return DrawString(aContext, aX, aY, 
                    NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength);
}

nscoord
nsFontPSXft::DrawString(nsRenderingContextPS* aContext,
                        nscoord aX, nscoord aY,
                        const PRUnichar* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext && aLength, 0);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, 0);

  psObj->moveto(aX, aY);

  PRInt32 currSubFont, prevSubFont = -1;
  PRUint32 start = 0;
  PRUint32 i;

  // XXX : ignore surrogate pairs for now
  nsString *subSet = mPSFontGenerator->GetSubset();
  for (i = 0; i < aLength; ++i) {
    currSubFont = mPSFontGenerator->AddToSubset(aString[i]);
    if (prevSubFont != currSubFont) {
      if (prevSubFont != -1)
        psObj->show(&aString[start], i - start, *subSet, prevSubFont);
      NS_ASSERTION(!mFontNameBase.IsEmpty(),
                  "font base name shouldn't be empty");
      psObj->setfont(mFontNameBase, mHeight, currSubFont);
      prevSubFont = currSubFont;
      start = i;
    }
  }

  if (prevSubFont != -1)
    psObj->show(&aString[start], i - start, *subSet, prevSubFont); 
  
  return GetWidth(aString, aLength);
}

int
nsFontPSXft::ascent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(face->ascender, face->size->metrics.y_scale);
}

int
nsFontPSXft::descent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(-face->descender, face->size->metrics.y_scale);
}

int
nsFontPSXft::max_ascent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  TT_OS2 *tt_os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (tt_os2)
     return FT_DESIGN_UNITS_TO_PIXELS(tt_os2->sTypoAscender,
                                      face->size->metrics.y_scale);
  else
     return FT_DESIGN_UNITS_TO_PIXELS(face->bbox.yMax,
                                      face->size->metrics.y_scale);
}

int
nsFontPSXft::max_descent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  TT_OS2 *tt_os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (tt_os2)
     return FT_DESIGN_UNITS_TO_PIXELS(-tt_os2->sTypoDescender,
                                      face->size->metrics.y_scale);
  else
     return FT_DESIGN_UNITS_TO_PIXELS(-face->bbox.yMin,
                                      face->size->metrics.y_scale);
}

int
nsFontPSXft::max_width()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(face->max_advance_width,
                                   face->size->metrics.x_scale);
}

PRBool
nsFontPSXft::getXHeight(unsigned long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face || !aVal)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(face->height, face->size->metrics.y_scale);

  return PR_TRUE;
}

PRBool
nsFontPSXft::underlinePosition(long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(-face->underline_position,
                                   face->size->metrics.y_scale);
  return PR_TRUE;
}

PRBool
nsFontPSXft::underline_thickness(unsigned long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness,
                                   face->size->metrics.x_scale);
  return PR_TRUE;
}

PRBool
nsFontPSXft::superscript_y(long &aVal)
{
  aVal = 0;
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;

  TT_OS2 *tt_os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (!tt_os2)
    return PR_FALSE;

  aVal = FT_DESIGN_UNITS_TO_PIXELS(tt_os2->ySuperscriptYOffset,
                                  face->size->metrics.y_scale);
  return PR_TRUE;
}

PRBool
nsFontPSXft::subscript_y(long &aVal)
{
  aVal = 0;
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;

  TT_OS2 *tt_os2 = (TT_OS2 *) FT_Get_Sfnt_Table(face, ft_sfnt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (!tt_os2)
    return PR_FALSE;

  aVal = FT_DESIGN_UNITS_TO_PIXELS(tt_os2->ySubscriptYOffset,
                                  face->size->metrics.y_scale);

  // some fonts have the sign wrong. it should be always positive.
  aVal = (aVal < 0) ? -aVal : aVal;
  return PR_TRUE;
}

nsresult
nsFontPSXft::RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app)
{
 
  nscoord leading, emHeight, emAscent, emDescent;
  nscoord maxHeight, maxAscent, maxDescent, maxAdvance;
  nscoord xHeight, spaceWidth, aveCharWidth;
  nscoord underlineOffset, underlineSize, superscriptOffset, subscriptOffset;
  nscoord strikeoutOffset, strikeoutSize;

  int lineSpacing = ascent() + descent();
  if (lineSpacing > mPixelSize) {
    leading = nscoord((lineSpacing - mPixelSize) * dev2app);
  }
  else {
    leading = 0;
  }
  emHeight = PR_MAX(1, nscoord(mPixelSize * dev2app));
  emAscent = nscoord(ascent() * mPixelSize * dev2app / lineSpacing);
  emDescent = emHeight - emAscent;

  maxHeight  = nscoord((max_ascent() + max_descent()) * dev2app);
  maxAscent  = nscoord(max_ascent() * dev2app) ;
  maxDescent = nscoord(max_descent() * dev2app);
  maxAdvance = nscoord(max_width() * dev2app);

  // 56% of ascent, best guess for non-true type
  xHeight = NSToCoordRound((float)ascent()* dev2app * 0.56f);

  PRUnichar space = (PRUnichar)' ';
  spaceWidth = NSToCoordRound(GetWidth(&space, 1));
  PRUnichar averageX = (PRUnichar)'x';
  aveCharWidth = NSToCoordRound(GetWidth(&averageX, 1));
  
  unsigned long pr = 0;
  if (getXHeight(pr)) {
    xHeight = (nscoord(pr * dev2app));
  }

  float height;
  long val;
  
  height = ascent() + descent();
  underlineOffset = -NSToIntRound(
                    PR_MAX (1, floor (0.1 * height + 0.5)) * dev2app);

  if (underline_thickness(pr)) {
    /* this will only be provided from adobe .afm fonts */
    underlineSize = nscoord(PR_MAX(dev2app, NSToIntRound(pr * dev2app)));
  }
  else {
    height = ascent() + descent();
    underlineSize = NSToIntRound(
                    PR_MAX(1, floor (0.05 * height + 0.5)) * dev2app);
  }

  if (superscript_y(val)) {
    superscriptOffset = nscoord(PR_MAX(dev2app, NSToIntRound(val * dev2app)));
  }
  else {
    superscriptOffset = xHeight;
  }

  if (subscript_y(val)) {
    subscriptOffset = nscoord(PR_MAX(dev2app, NSToIntRound(val * dev2app)));
  }
  else {
   subscriptOffset = xHeight;
  }

  /* need better way to calculate this */
  strikeoutOffset = NSToCoordRound(xHeight / 2.0);
  strikeoutSize = underlineSize;
  
  // TODO: leading never used, does it equal to "Height"?
  aFontMetrics->SetHeight(emHeight);
  aFontMetrics->SetEmHeight(emHeight);
  aFontMetrics->SetEmAscent(emAscent);
  aFontMetrics->SetEmDescent(emDescent);
  aFontMetrics->SetMaxHeight(maxHeight);
  aFontMetrics->SetMaxAscent(maxAscent);
  aFontMetrics->SetMaxDescent(maxDescent);
  aFontMetrics->SetMaxAdvance(maxAdvance);
  aFontMetrics->SetXHeight(xHeight);
  aFontMetrics->SetSpaceWidth(spaceWidth);
  aFontMetrics->SetAveCharWidth(aveCharWidth);
  aFontMetrics->SetUnderline(underlineOffset, underlineSize);
  aFontMetrics->SetSuperscriptOffset(superscriptOffset);
  aFontMetrics->SetSubscriptOffset(subscriptOffset);
  aFontMetrics->SetStrikeout(strikeoutOffset, strikeoutSize);

  return NS_OK;
}

nsresult
nsFontPSXft::SetupFont(nsRenderingContextPS* aContext)
{
  NS_ENSURE_TRUE(aContext, NS_ERROR_FAILURE);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, NS_ERROR_FAILURE);

  mFontMetrics->GetHeight(mHeight);

  if (mFontNameBase.IsEmpty()) {
    int wmode = 0;
    FT_Face face = getFTFace();
    NS_ENSURE_TRUE(face, NS_ERROR_NULL_POINTER);
    if (NS_FAILED(FT2ToType1FontName(face, wmode, mFontNameBase)))
      return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

#ifdef MOZ_MATHML
nsresult
nsFontPSXft::GetBoundingMetrics(const char*        aString,
                                     PRUint32           aLength,
                                     nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult
nsFontPSXft::GetBoundingMetrics(const PRUnichar*   aString,
                                     PRUint32           aLength,
                                     nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}
#endif //MOZ_MATHML

#else

PRBool
nsFontPSFreeType::CSSFontEnumCallback(const nsString& aFamily, PRBool aGeneric,
                                      void* aFpi)
{
  fontPSInfo* fpi = (fontPSInfo*)aFpi;
  nsCAutoString familyname;
  if (aGeneric) {
    // need lang to lookup generic pref
    if (strlen(fpi->lang.get()) == 0) {
      return PR_TRUE; // keep trying
    }
    nsXPIDLCString value;
    nsresult rv;
    nsCOMPtr<nsIPref> pref(do_GetService(NS_PREF_CONTRACTID, &rv));
    NS_ENSURE_SUCCESS(rv, PR_TRUE); // keep trying
    nsCAutoString name("font.name.");
    LossyAppendUTF16toASCII(aFamily, name);
    name.Append(char('.'));
    name.Append(fpi->lang);
    pref->CopyCharPref(name.get(), getter_Copies(value));
    if (!value.get())
      return PR_TRUE; // keep trying
    // strip down to just the family name
    PRInt32 startFamily = value.FindChar('-') + 1;
    if (startFamily < 0) // 1st '-' not found. Not FFRE but just familyName.
      familyname = value;
    else { 
      PRInt32 endFamily = value.FindChar('-', startFamily);
      if (endFamily < 0) // 2nd '-' not found
        familyname.Append(Substring(value, startFamily));
      else  // FFRE 
        familyname.Append(Substring(value, startFamily, endFamily - startFamily));
    }
    PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG,
        ("generic font \"%s\" -> \"%s\"\n", name.get(), familyname.get()));
  }
  else
    LossyAppendUTF16toASCII(aFamily, familyname);

  AddFontEntries(familyname, fpi->lang, fpi->weight, 
                 nsIFontCatalogService::kFCWidthAny, fpi->slant,
                 nsIFontCatalogService::kFCSpacingAny, fpi);

  return PR_TRUE;
}

PRBool
nsFontPSFreeType::AddUserPref(nsIAtom *aLang, const nsFont& aFont,
                              fontPSInfo *aFpi)
{
  nsCAutoString emptyStr;
  fontPSInfo *fpi = (fontPSInfo*)aFpi;
  nsresult rv = NS_OK;
  nsCAutoString fontName;
  nsCOMPtr<nsIPref> pref(do_GetService(NS_PREF_CONTRACTID, &rv));
  NS_ENSURE_SUCCESS(rv, PR_FALSE);

  nsXPIDLCString value;
  nsCAutoString name("font.default.");
  name.Append(fpi->lang);
  pref->CopyCharPref(name.get(), getter_Copies(value));
  if (!value.get())
    return PR_FALSE;

  name.Assign("font.name.");
  name.Append(value);
  name.Append(char('.'));
  name.Append(fpi->lang);
  pref->CopyCharPref(name.get(), getter_Copies(value));

  if (!value.get())
    return PR_FALSE;

  // strip down to just the family name
  PRInt32 startFamily = value.FindChar('-') + 1;
  if (startFamily < 0) // '-' not found. Not FFRE but just familyName 
    fontName = value;
  else {
    PRInt32 endFamily = value.FindChar('-', startFamily);
    if (endFamily < 0) // 2nd '-' not found
      fontName.Append(Substring(value, startFamily));
    else  // FFRE
      fontName.Append(Substring(value, startFamily, endFamily - startFamily));
  }

  AddFontEntries(fontName, fpi->lang, fpi->weight,
                 nsIFontCatalogService::kFCWidthAny, fpi->slant,
                 nsIFontCatalogService::kFCSpacingAny, fpi);

  // wildcard the language
  AddFontEntries(fontName, emptyStr, fpi->weight,
                 nsIFontCatalogService::kFCWidthAny, fpi->slant,
                 nsIFontCatalogService::kFCSpacingAny, fpi);

  return PR_TRUE;
}

static nsFontPS*
CreateFontPS(nsITrueTypeFontCatalogEntry *aEntry, const nsFont& aFont,
             nsFontMetricsPS* aFontMetrics)
{
  nsresult rv;
  nsDeviceContextPS* dc = aFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, nsnull);

  nsCAutoString familyName, styleName;
  aEntry->GetFamilyName(familyName);
  aEntry->GetStyleName(styleName);
  ToLowerCase(familyName);
  ToLowerCase(styleName);
  
  nsCAutoString fontName;
  fontName.Append(familyName);
  fontName.Append("-");
  fontName.Append(styleName);
  nsCStringKey key(fontName);

  nsHashtable *psFGList = dc->GetPSFontGeneratorList();
  NS_ENSURE_TRUE(psFGList, nsnull);
  
  nsPSFontGenerator* psFontGen = (nsPSFontGenerator*) psFGList->Get(&key);
  if (!psFontGen) {
    psFontGen = new nsFT2Type1Generator;
    NS_ENSURE_TRUE(psFontGen, nsnull);
    rv = ((nsFT2Type1Generator*)psFontGen)->Init(aEntry);
    if (NS_FAILED(rv)) {
      delete psFontGen;
      return nsnull;
    }
    psFGList->Put(&key, (void *) psFontGen);
  }
  nsFontPSFreeType* font = new nsFontPSFreeType(aFont, aFontMetrics);
  NS_ENSURE_TRUE(font, nsnull);
  rv = font->Init(aEntry, psFontGen);
  if (NS_FAILED(rv)) {
    delete font;
    return nsnull;
  }
  return font;
}

nsFontPS*
nsFontPSFreeType::FindFont(PRUnichar aChar, const nsFont& aFont, 
                           nsFontMetricsPS* aFontMetrics)
{
  PRBool inited = PR_FALSE;
  int anyWeight  = nsIFontCatalogService::kFCWeightAny;
  int anyWidth   = nsIFontCatalogService::kFCWidthAny;
  int anySlant   = nsIFontCatalogService::kFCSlantAny;
  int anySpacing = nsIFontCatalogService::kFCSpacingAny;
  nsCOMPtr<nsIAtom> lang;
  nsCAutoString emptyStr;
  nsCAutoString locale;
  fontPSInfo fpi, fpi2;
  fpi.fontps = aFontMetrics->GetFontsPS();
  if (!fpi.fontps)
    return nsnull;

  int i = 0;
  while (1) {
    //
    // see if it is already in the list of fonts
    //
    for (; i<fpi.fontps->Count(); i++) {
      fontps *fi = (fontps *)fpi.fontps->ElementAt(i);
      if (!fi->entry || !fi->ccmap) {
        NS_ASSERTION(fi->entry, "invalid entry");
        NS_ASSERTION(fi->ccmap, "invalid ccmap");
        continue;
      }
      if (CCMAP_HAS_CHAR(fi->ccmap, aChar)) {
        if (!fi->fontps) {
#ifdef PR_LOGGING
          if (PR_LOG_TEST(gFontMetricsPSM, PR_LOG_DEBUG)) {
            nsCAutoString familyName, styleName;
            fi->entry->GetFamilyName(familyName);
            fi->entry->GetStyleName(styleName);
            PR_LogPrint("CreateFontPS %s/%s\n",
                familyName.get(), styleName.get());
          }
#endif
          fi->fontps = CreateFontPS(fi->entry, aFont, aFontMetrics);
        }
        if (fi->fontps)
          return fi->fontps;
      }
    }

    //
    // it is not already in the list of fonts
    // so add more fonts to the list
    //

    if (!inited) {
      fpi.nsfont = &aFont;
      fpi.alreadyLoaded = aFontMetrics->GetFontsAlreadyLoadedList();
      aFontMetrics->GetLangGroup(getter_AddRefs(lang));
      if (!lang)
        lang = NS_NewAtom("x-western");
      const char *langStr;
      lang->GetUTF8String(&langStr);
      if (langStr)
        fpi.lang.Append(langStr);
      gUsersLocale->GetUTF8String(&langStr);
      if (langStr)
        locale.Append(langStr);
      if (NS_IS_BOLD(fpi.nsfont->weight))
        fpi.weight = nsIFontCatalogService::kFCWeightBold;
      else
        fpi.weight = nsIFontCatalogService::kFCWeightMedium;
      if (fpi.nsfont->style == NS_FONT_STYLE_NORMAL)
        fpi.slant = nsIFontCatalogService::kFCSlantRoman;
      else
        fpi.slant = nsIFontCatalogService::kFCSlantItalic;
      inited = PR_TRUE;
    }

    //
    // Add fonts to the list following the CSS spec, user pref
    // After that slowly loosen the spec to enlarge the list
    //
    int state = aFontMetrics->GetFontPSState();
    aFontMetrics->IncrementFontPSState();

    switch (state) {
      case 0:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get the CSS specified entries for the element's language"));
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi);
        break;

      case 1:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get the CSS specified entries for the user's locale"));
        fpi2 = fpi;
        fpi2.lang = locale;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 2:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get the CSS specified entries for any language"));
        fpi2 = fpi;
        fpi2.lang = emptyStr;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 3:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get the user pref for the element's language"));
        AddUserPref(lang, aFont, &fpi);
        break;

      case 4:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get the user pref for the user's locale"));
        fpi2 = fpi;
        fpi2.lang = locale;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 5:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get all the entries for this language/style"));
        AddFontEntries(emptyStr, fpi.lang, fpi.weight, anyWidth, fpi.slant,
                       anySpacing, &fpi);
        break;

      case 6:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "get all the entries for the locale/style"));
        AddFontEntries(emptyStr, locale, fpi.weight, anyWidth, fpi.slant,
                       anySpacing, &fpi);
        break;

      case 7:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations of CSS "
            "specified entries for the element's language"));
        fpi2 = fpi;
        fpi2.weight = anyWeight;
        fpi2.slant = 0;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 8:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations of CSS "
            "specified entries for the user's locale"));
        fpi2 = fpi;
        fpi2.lang = locale;
        fpi2.weight = anyWeight;
        fpi2.slant = 0;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 9:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations of CSS "
            "specified entries for any language"));
        fpi2 = fpi;
        fpi2.lang = emptyStr;
        fpi2.weight = anyWeight;
        fpi2.slant = 0;
        aFont.EnumerateFamilies(nsFontPSFreeType::CSSFontEnumCallback, &fpi2);
        break;

      case 10:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations of the user pref"));
        fpi2 = fpi;
        fpi2.lang   = emptyStr;
        fpi2.weight = anyWeight;
        fpi2.slant  = 0;
        AddUserPref(lang, aFont, &fpi2);
        break;

      case 11:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations for this language"));
        AddFontEntries(emptyStr, fpi.lang, anyWeight, anyWidth, anySlant,
                       anySpacing, &fpi);
        break;

      case 12:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n",
            "wildcard the slant/weight variations of the locale"));
        AddFontEntries(emptyStr, locale, anyWeight, anyWidth, anySlant,
                       anySpacing, &fpi);
        break;

      case 13:
        PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("%s\n", "get ALL font entries"));
        AddFontEntries(emptyStr, emptyStr, anyWeight, anyWidth, anySlant,
                       anySpacing, &fpi);
        break;

      default:
        // try to always return a font even if no font supports this char
        if (fpi.fontps->Count()) {
          PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG,
              ("failed to find a font supporting 0x%04x so "
              "returning 1st font in list\n", aChar));
          fontps *fi = (fontps *)fpi.fontps->ElementAt(0);
          if (!fi->fontps)
            fi->fontps = CreateFontPS(fi->entry, aFont, aFontMetrics);
          return fi->fontps;
        }
        PR_LOG(gFontMetricsPSM, PR_LOG_WARNING,
            ("failed to find a font supporting 0x%04x\n", aChar));
        return (nsnull);
    }
  }

  return nsnull;
}

nsresult
nsFontPSFreeType::AddFontEntries(nsACString& aFamilyName, nsACString& aLanguage,
                                 PRUint16 aWeight, PRUint16 aWidth,
                                 PRUint16 aSlant, PRUint16 aSpacing,
                                 fontPSInfo* aFpi)
{
  nsresult rv = NS_OK;
  nsCAutoString name(aFamilyName);
  nsCAutoString lang(aLanguage);
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    family   = '%s'", name.get()));
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    lang     = '%s'", lang.get()));
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    aWeight  = %d", aWeight));
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    aWidth   = %d", aWidth));
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    aSlant   = %d", aSlant));
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    aSpacing = %d", aSpacing));

  nsCOMPtr<nsIFontCatalogService> fcs(do_GetService(kFCSCID, &rv));
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIArray> entryList;
  rv = fcs->GetFontCatalogEntries(aFamilyName, aLanguage,
                                  aWeight, aWidth, aSlant, aSpacing,
                                  getter_AddRefs(entryList));
  NS_ENSURE_SUCCESS(rv, rv);

  PRUint32 i, count = 0;
  NS_ENSURE_TRUE(entryList, NS_ERROR_FAILURE);

  rv = entryList->GetLength(&count);
  NS_ENSURE_SUCCESS(rv, rv);
  PR_LOG(gFontMetricsPSM, PR_LOG_DEBUG, ("    count    = %d", count));

  for (i=0; i<count; i++) {
    nsCOMPtr<nsITrueTypeFontCatalogEntry> entry = do_QueryElementAt(entryList,
                                                                    i, &rv);
    NS_ENSURE_SUCCESS(rv, rv);

    // check if already in list
    nsVoidKey key((void*)entry);
    PRBool loaded = aFpi->alreadyLoaded->Exists(&key);
#ifdef PR_LOGGING
    if (PR_LOG_TEST(gFontMetricsPSM, PR_LOG_DEBUG)) {
      nsCAutoString fontname, stylename;
      entry->GetFamilyName(fontname);
      entry->GetStyleName(stylename);
      PR_LogPrint("    -- %s '%s/%s'\n", (loaded ? "already loaded" : "load"),
          fontname.get(), stylename.get());
    }
#endif
    if (loaded)
      continue;

    PRUint16 *ccmap;
    PRUint32 size;
    entry->GetCCMap(&size, &ccmap);
    nsITrueTypeFontCatalogEntry *e = entry;
    NS_IF_ADDREF(e);
    fontps *fps = new fontps;
    NS_ENSURE_TRUE(fps, NS_ERROR_OUT_OF_MEMORY);
    fps->entry  = entry;
    fps->fontps = nsnull;
    fps->ccmap  = ccmap;
    aFpi->fontps->AppendElement(fps);
    aFpi->alreadyLoaded->Put(&key, (void*)1);
  }

  return rv;
}

nsFontPSFreeType::nsFontPSFreeType(const nsFont& aFont,
                                   nsFontMetricsPS* aFontMetrics)
  :nsFontPS(aFont, aFontMetrics)
{
}

nsresult
nsFontPSFreeType::Init(nsITrueTypeFontCatalogEntry* aEntry,
                       nsPSFontGenerator* aPSFontGen)
{
  NS_ENSURE_TRUE(mFont && mFontMetrics, NS_ERROR_FAILURE);
  NS_ENSURE_TRUE(aEntry && aPSFontGen, NS_ERROR_FAILURE);
  mEntry = aEntry;
  mPSFontGenerator = aPSFontGen;

  float app2dev;
  nsIDeviceContext* dc = mFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, NS_ERROR_NULL_POINTER);
  app2dev = dc->AppUnitsToDevUnits();
  
  mPixelSize = NSToIntRound(app2dev * mFont->size);

  mImageDesc.font.face_id    = (void*)mEntry;
  mImageDesc.font.pix_width  = mPixelSize;
  mImageDesc.font.pix_height = mPixelSize;
  mImageDesc.image_type = 0;

  nsresult rv;
  mFt2 = do_GetService(NS_FREETYPE2_CONTRACTID, &rv);
  if (NS_FAILED(rv)) {
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

nsFontPSFreeType::~nsFontPSFreeType()
{
  mEntry = nsnull;
}

nscoord
nsFontPSFreeType::GetWidth(const char* aString, PRUint32 aLength)
{
  return GetWidth(NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength);
}

nscoord
nsFontPSFreeType::GetWidth(const PRUnichar* aString, PRUint32 aLength)
{
  FT_UInt glyph_index;
  FT_Glyph glyph;
  double origin_x = 0;

  // get the face/size from the FreeType cache
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return 0;

  FTC_Image_Cache iCache;
  nsresult rv = mFt2->GetImageCache(&iCache);
  if (NS_FAILED(rv)) {
    NS_ERROR("Failed to get Image Cache");
    return 0;
  }

  for (PRUint32 i=0; i<aLength; i++) {
    mFt2->GetCharIndex((FT_Face)face, aString[i], &glyph_index);
    nsresult rv = mFt2->ImageCacheLookup(iCache, &mImageDesc,
                                         glyph_index, &glyph);
    if (NS_FAILED(rv)) {
      origin_x += FT_REG_TO_16_16(face->size->metrics.x_ppem/2 + 2);
      continue;
    }
    origin_x += glyph->advance.x;
  }

  NS_ENSURE_TRUE(mFontMetrics, 0);

  nsDeviceContextPS* dc = mFontMetrics->GetDeviceContext();
  NS_ENSURE_TRUE(dc, 0);

  float dev2app;
  dev2app = dc->DevUnitsToAppUnits();
  origin_x *= dev2app;
  origin_x /= FT_REG_TO_16_16(1);

  return NSToCoordRound((nscoord)origin_x);
}

FT_Face
nsFontPSFreeType::getFTFace()
{
  FT_Face face = nsnull;
  
  FTC_Manager cManager;
  mFt2->GetFTCacheManager(&cManager);
  nsresult rv = mFt2->ManagerLookupSize(cManager, &mImageDesc.font,
                                        &face, nsnull);
  NS_ASSERTION(rv==0, "failed to get face/size");
  if (rv)
    return nsnull;
  return face;
}

nscoord
nsFontPSFreeType::DrawString(nsRenderingContextPS* aContext,
                             nscoord aX, nscoord aY,
                             const char* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext, 0);
  return DrawString(aContext, aX, aY, 
                    NS_ConvertASCIItoUTF16(aString, aLength).get(), aLength);
}

nscoord
nsFontPSFreeType::DrawString(nsRenderingContextPS* aContext,
                             nscoord aX, nscoord aY,
                             const PRUnichar* aString, PRUint32 aLength)
{
  NS_ENSURE_TRUE(aContext && aLength, 0);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, 0);

  psObj->moveto(aX, aY);

  PRInt32 currSubFont, prevSubFont = -1;
  PRUint32 start = 0;
  PRUint32 i;

  // XXX : ignore surrogate pairs for now
  nsString *subSet = mPSFontGenerator->GetSubset();
  for (i = 0; i < aLength; ++i) {
    currSubFont = mPSFontGenerator->AddToSubset(aString[i]);
    if (prevSubFont != currSubFont) {
      if (prevSubFont != -1)
        psObj->show(&aString[start], i - start, *subSet, prevSubFont); 
      NS_ASSERTION(!mFontNameBase.IsEmpty(),
                  "font base name shouldn't be empty");
      psObj->setfont(mFontNameBase, mHeight, currSubFont);
      prevSubFont = currSubFont;
      start = i;
    }
  }

  if (prevSubFont != -1)
    psObj->show(&aString[start], i - start, *subSet, prevSubFont);
  
  return GetWidth(aString, aLength);
}

int
nsFontPSFreeType::ascent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(face->ascender, face->size->metrics.y_scale);
}

int
nsFontPSFreeType::descent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(-face->descender, face->size->metrics.y_scale);
}

int
nsFontPSFreeType::max_ascent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  void * p;
  mFt2->GetSfntTable(face, ft_sfnt_os2, &p);
  TT_OS2 * tt_os2 = (TT_OS2 *)p;
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (tt_os2)
     return FT_DESIGN_UNITS_TO_PIXELS(tt_os2->sTypoAscender,
                                      face->size->metrics.y_scale);
  else
     return FT_DESIGN_UNITS_TO_PIXELS(face->bbox.yMax,
                                      face->size->metrics.y_scale);
}

int
nsFontPSFreeType::max_descent()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  void *p;
  mFt2->GetSfntTable(face, ft_sfnt_os2, &p);
  TT_OS2 *tt_os2 = (TT_OS2 *) p;
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (tt_os2)
     return FT_DESIGN_UNITS_TO_PIXELS(-tt_os2->sTypoDescender,
                                      face->size->metrics.y_scale);
  else
     return FT_DESIGN_UNITS_TO_PIXELS(-face->bbox.yMin,
                                      face->size->metrics.y_scale);
}

int
nsFontPSFreeType::max_width()
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  NS_ENSURE_TRUE(face, 0);
  return FT_DESIGN_UNITS_TO_PIXELS(face->max_advance_width,
                                   face->size->metrics.x_scale);
}

PRBool
nsFontPSFreeType::getXHeight(unsigned long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face || !aVal)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(face->height, face->size->metrics.y_scale);

  return PR_TRUE;
}

PRBool
nsFontPSFreeType::underlinePosition(long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(-face->underline_position,
                                   face->size->metrics.y_scale);
  return PR_TRUE;
}

PRBool
nsFontPSFreeType::underline_thickness(unsigned long &aVal)
{
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;
  aVal = FT_DESIGN_UNITS_TO_PIXELS(face->underline_thickness,
                                   face->size->metrics.x_scale);
  return PR_TRUE;
}

PRBool
nsFontPSFreeType::superscript_y(long &aVal)
{
  aVal = 0;
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;

  TT_OS2 *tt_os2;
  mFt2->GetSfntTable(face, ft_sfnt_os2, (void**)&tt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (!tt_os2)
    return PR_FALSE;

  aVal = FT_DESIGN_UNITS_TO_PIXELS(tt_os2->ySuperscriptYOffset,
                                  face->size->metrics.y_scale);
  return PR_TRUE;
}

PRBool
nsFontPSFreeType::subscript_y(long &aVal)
{
  aVal = 0;
  FT_Face face = getFTFace();
  NS_ASSERTION(face, "failed to get face/size");
  if (!face)
    return PR_FALSE;

  TT_OS2 *tt_os2;
  mFt2->GetSfntTable(face, ft_sfnt_os2, (void**)&tt_os2);
  NS_ASSERTION(tt_os2, "unable to get OS2 table");
  if (!tt_os2)
    return PR_FALSE;

  aVal = FT_DESIGN_UNITS_TO_PIXELS(tt_os2->ySubscriptYOffset,
                                  face->size->metrics.y_scale);

  // some fonts have the sign wrong. it should be always positive.
  aVal = (aVal < 0) ? -aVal : aVal;
  return PR_TRUE;
}

nsresult
nsFontPSFreeType::RealizeFont(nsFontMetricsPS* aFontMetrics, float dev2app)
{
 
  nscoord leading, emHeight, emAscent, emDescent;
  nscoord maxHeight, maxAscent, maxDescent, maxAdvance;
  nscoord xHeight, spaceWidth, aveCharWidth;
  nscoord underlineOffset, underlineSize, superscriptOffset, subscriptOffset;
  nscoord strikeoutOffset, strikeoutSize;

  int lineSpacing = ascent() + descent();
  if (lineSpacing > mPixelSize) {
    leading = nscoord((lineSpacing - mPixelSize) * dev2app);
  }
  else {
    leading = 0;
  }
  emHeight = PR_MAX(1, nscoord(mPixelSize * dev2app));
  emAscent = nscoord(ascent() * mPixelSize * dev2app / lineSpacing);
  emDescent = emHeight - emAscent;

  maxHeight  = nscoord((max_ascent() + max_descent()) * dev2app);
  maxAscent  = nscoord(max_ascent() * dev2app) ;
  maxDescent = nscoord(max_descent() * dev2app);
  maxAdvance = nscoord(max_width() * dev2app);

  // 56% of ascent, best guess for non-true type
  xHeight = NSToCoordRound((float)ascent()* dev2app * 0.56f);

  PRUnichar space = (PRUnichar)' ';
  spaceWidth = NSToCoordRound(GetWidth(&space, 1));
  PRUnichar averageX = (PRUnichar)'x';
  aveCharWidth = NSToCoordRound(GetWidth(&averageX, 1));
  
  unsigned long pr = 0;
  if (getXHeight(pr)) {
    xHeight = (nscoord(pr * dev2app));
  }

  float height;
  long val;
  
  height = ascent() + descent();
  underlineOffset = -NSToIntRound(
                    PR_MAX (1, floor (0.1 * height + 0.5)) * dev2app);

  if (underline_thickness(pr)) {
    /* this will only be provided from adobe .afm fonts */
    underlineSize = nscoord(PR_MAX(dev2app, NSToIntRound(pr * dev2app)));
  }
  else {
    height = ascent() + descent();
    underlineSize = NSToIntRound(
                    PR_MAX(1, floor (0.05 * height + 0.5)) * dev2app);
  }

  if (superscript_y(val)) {
    superscriptOffset = nscoord(PR_MAX(dev2app, NSToIntRound(val * dev2app)));
  }
  else {
    superscriptOffset = xHeight;
  }

  if (subscript_y(val)) {
    subscriptOffset = nscoord(PR_MAX(dev2app, NSToIntRound(val * dev2app)));
  }
  else {
   subscriptOffset = xHeight;
  }

  /* need better way to calculate this */
  strikeoutOffset = NSToCoordRound(xHeight / 2.0);
  strikeoutSize = underlineSize;
  
  // TODO: leading never used, does it equal to "Height"?
  aFontMetrics->SetHeight(emHeight);
  aFontMetrics->SetEmHeight(emHeight);
  aFontMetrics->SetEmAscent(emAscent);
  aFontMetrics->SetEmDescent(emDescent);
  aFontMetrics->SetMaxHeight(maxHeight);
  aFontMetrics->SetMaxAscent(maxAscent);
  aFontMetrics->SetMaxDescent(maxDescent);
  aFontMetrics->SetMaxAdvance(maxAdvance);
  aFontMetrics->SetXHeight(xHeight);
  aFontMetrics->SetSpaceWidth(spaceWidth);
  aFontMetrics->SetAveCharWidth(aveCharWidth);
  aFontMetrics->SetUnderline(underlineOffset, underlineSize);
  aFontMetrics->SetSuperscriptOffset(superscriptOffset);
  aFontMetrics->SetSubscriptOffset(subscriptOffset);
  aFontMetrics->SetStrikeout(strikeoutOffset, strikeoutSize);

  return NS_OK;
}

nsresult
nsFontPSFreeType::SetupFont(nsRenderingContextPS* aContext)
{
  NS_ENSURE_TRUE(aContext, NS_ERROR_FAILURE);
  nsPostScriptObj* psObj = aContext->GetPostScriptObj();
  NS_ENSURE_TRUE(psObj, NS_ERROR_FAILURE);

  mFontMetrics->GetHeight(mHeight);

  if (mFontNameBase.IsEmpty()) {
    int wmode = 0;
    FT_Face face = getFTFace();
    NS_ENSURE_TRUE(face, NS_ERROR_NULL_POINTER);
    if (NS_FAILED(FT2ToType1FontName(face, wmode, mFontNameBase)))
      return NS_ERROR_FAILURE;
  }
  
  return NS_OK;
}

#ifdef MOZ_MATHML
nsresult
nsFontPSFreeType::GetBoundingMetrics(const char*        aString,
                                     PRUint32           aLength,
                                     nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}

nsresult
nsFontPSFreeType::GetBoundingMetrics(const PRUnichar*   aString,
                                     PRUint32           aLength,
                                     nsBoundingMetrics& aBoundingMetrics)
{
  return NS_ERROR_NOT_IMPLEMENTED;
}
#endif //MOZ_MATHML

#endif //MOZ_ENABLE_FREETYPE2
#endif //MOZ_ENABLE_XFT

// Implementation of nsPSFontGenerator
nsPSFontGenerator::nsPSFontGenerator()
{
}

nsPSFontGenerator::~nsPSFontGenerator()
{
}

void nsPSFontGenerator::GeneratePSFont(FILE* aFile)
{
  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.
PRInt32
nsPSFontGenerator::AddToSubset(PRUnichar aChar)
{
  PRInt32 index = mSubset.FindChar(aChar);
  if (index == kNotFound) {
    mSubset.Append(aChar);
    index = mSubset.Length() - 1;
  }
  return index / kSubFontSize;
}

nsString *nsPSFontGenerator::GetSubset()
{
  return &mSubset;
}

#ifdef MOZ_ENABLE_XFT
nsXftType1Generator::nsXftType1Generator()
{
}

nsresult
nsXftType1Generator::Init(nsXftEntry* aEntry)
{
  NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
  mEntry = aEntry;

  FT_Error error = FT_Init_FreeType(&mFreeTypeLibrary);
  NS_ASSERTION(!error, "failed to initialize FreeType library");
  if (error) {
    mFreeTypeLibrary = nsnull;
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

nsXftType1Generator::~nsXftType1Generator()
{
  if (mEntry->mFace) 
    FT_Done_Face(mEntry->mFace);

  if (FT_Done_FreeType(mFreeTypeLibrary))
    return;

  mEntry = nsnull;
}

void nsXftType1Generator::GeneratePSFont(FILE* aFile)
{
  FT_Face face = mEntry->mFace;

  if (face == nsnull) {
    if (FT_New_Face(mFreeTypeLibrary, mEntry->mFontFileName.get(), mEntry->mFaceIndex, &face) ||
        face == nsnull /* || FT_Set_Pixel_Sizes(face, 16, 0) */) 
       return;
     mEntry->mFace = face;
  }

  int wmode = 0;
  if (!mSubset.IsEmpty())
    FT2SubsetToType1FontSet(face, mSubset, wmode, aFile);
}

#else
#ifdef MOZ_ENABLE_FREETYPE2

nsFT2Type1Generator::nsFT2Type1Generator()
{
}

nsresult
nsFT2Type1Generator::Init(nsITrueTypeFontCatalogEntry* aEntry)
{
  NS_ENSURE_TRUE(aEntry, NS_ERROR_FAILURE);
  mEntry = aEntry;
  nsresult rv;
  mFt2 = do_GetService(NS_FREETYPE2_CONTRACTID, &rv);
  if (NS_FAILED(rv)) {
    return NS_ERROR_FAILURE;
  }
  return NS_OK;
}

nsFT2Type1Generator::~nsFT2Type1Generator()
{
  mEntry = nsnull;
}

void nsFT2Type1Generator::GeneratePSFont(FILE* aFile)
{
  nsCAutoString fontName, styleName;
  mEntry->GetFamilyName(fontName);
  mEntry->GetStyleName(styleName);
  
  mImageDesc.font.face_id    = (void*)mEntry;
  // TT glyph has no relation to size
  mImageDesc.font.pix_width  = 16;
  mImageDesc.font.pix_height = 16;
  mImageDesc.image_type = 0;
  FT_Face face = nsnull;
  FTC_Manager cManager;
  mFt2->GetFTCacheManager(&cManager);
  nsresult rv = mFt2->ManagerLookupSize(cManager, &mImageDesc.font,
                                        &face, nsnull);
  if (NS_FAILED(rv))
    return;
 
  int wmode = 0;
  if (!mSubset.IsEmpty())
    FT2SubsetToType1FontSet(face, mSubset, wmode, aFile);
}

#endif //MOZ_ENABLE_FREETYPE2
#endif //MOZ_ENABLE_XFT
