/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ex: set tabstop=8 softtabstop=2 shiftwidth=2 expandtab: */
/* ***** BEGIN LICENSE BLOCK *****
 * Version: NPL 1.1/GPL 2.0/LGPL 2.1 
 *
 * The contents of this file are subject to the Netscape 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/NPL/ 
 *
 * 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):
 *   Brian Stell <bstell@netscape.com>
 *
 *
 * 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 NPL, 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 NPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "gfx-config.h"
#include "nsFT2FontCatalog.h"
#include "nsFontDebug.h"

PRUint32 gFontDebug = 0 | NS_FONT_DEBUG_FONT_SCAN;

#if (defined(MOZ_ENABLE_FREETYPE2))

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <dirent.h>
#include "nsAppDirectoryServiceDefs.h"
#include "nsLocalFile.h"
#include "nsIEnumerator.h"
#include "nsITimelineService.h"
#include "nsArray.h"

//
// Short overview:
//  This code is here primarily to solve this problem: getting the list
//  of valid glyphs in a TrueType font is very expensive when using
//  the FreeType2 library. To solve this problem this code looks for
//  a pre-built summary file in the font dir. If this font summary
//  file is missing it is built and stored in the font dir if possible
//  so it need not be generated by every user. If the user cannot write
//  in the font dir the summary is stored in a per-user dir 
//  (ie: $HOME/.mozilla/fontsummaries).
//
// The routines are in alphabetic order to assist people reading
// this code printed on paper in finding the routines.
//
// The interesting high level entry points are:
//
// nsFT2FontCatalog::InitGlobals();
//   Called during startup to read (and update) the font catalog(s).
//
// nsFT2FontCatalog::GetFontNames();
//   Called when the font code is looking for a font.
//
// Some interesting low level entry points are:
//
// nsFT2FontCatalog::NewFceFromFontFile();
//   Called to get a TrueType font summary.
//   Uses FreeType2 to actually open the font file.
//   This checks for invalid glyphs so this can take a while.
//   The summary will later be stored in the disk font catalog.
//
// nsFT2FontCatalog::NewFceFromSummary();
//   Called to get the TrueType font summary from the disk font catalog.
//

// Solaris is missing a prototype for ctime
extern "C" {char *ctime(const time_t *timep);}

#include <ft2build.h>
#include FT_GLYPH_H
#include FT_FREETYPE_H
#include FT_TRUETYPE_TABLES_H
#include FT_TRUETYPE_IDS_H

// these should be static but the compilier complains
extern nsFontVendorName sVendorNamesList[];
extern nsulCodePageRangeLanguage    ulCodePageRange1Language[];
extern nsulCodePageRangeLanguage    ulCodePageRange2Language[];

nsHashtable* nsFT2FontCatalog::sVendorNames = nsnull;
nsIPref*     nsFT2FontCatalog::sPref = nsnull;

#endif

//
// Implementation of Interfaces nsIFontCatalogService
//
NS_IMPL_ISUPPORTS1(nsFT2FontCatalog, nsIFontCatalogService)

nsFT2FontCatalog::nsFT2FontCatalog()
#ifdef MOZ_ENABLE_FREETYPE2
 : mFontCatalog(nsnull),
   mRange1Language(nsnull),
   mRange2Language(nsnull)
#endif
{
#if (defined(MOZ_ENABLE_FREETYPE2))
  nsresult rv;
  mAvailableFontCatalogService = PR_FALSE;

  mFt2 = do_GetService(NS_FREETYPE2_CONTRACTID, &rv);
  if (NS_FAILED(rv)) {
    // FreeType is not available
    return;
  }

  FT_Library lib;
  mFt2->GetLibrary(&lib);
  if (!lib) {
    // FreeType is not available
    return;
  }

  if (!InitGlobals(lib)) {
    // Font Catalog Service is not available
    return;
  }
  mAvailableFontCatalogService = PR_TRUE;
#endif
}

nsFT2FontCatalog::~nsFT2FontCatalog()
{
#if (defined(MOZ_ENABLE_FREETYPE2))
  FreeGlobals();
#endif
}

NS_IMETHODIMP
nsFT2FontCatalog::GetFontCatalogEntries(const nsACString & aFamilyName,
                                        const nsACString & aLanguage,
                                        PRUint16 aWeight,
                                        PRUint16 aWidth,
                                        PRUint16 aSlant,
                                        PRUint16 aSpacing,
                                        nsIArray **_retval)
{
#if (!defined(MOZ_ENABLE_FREETYPE2))
  *_retval = nsnull;
  return NS_OK;
#else
  if (mAvailableFontCatalogService == PR_FALSE) {
    *_retval = nsnull;
    return NS_OK;
  }
  
  nsFontCatalog *fc = NewFontCatalog();
  if (!fc)
    return NS_ERROR_OUT_OF_MEMORY;

  GetFontNames(aFamilyName, aLanguage, aWeight, aWidth, aSlant, aSpacing, fc);
  nsCOMPtr<nsITrueTypeFontCatalogEntry> aFce;
  nsCOMPtr<nsISupports> genericFce;
  nsCOMPtr<nsIMutableArray> entries;
  NS_NewArray(getter_AddRefs(entries));
  if (!entries)
    return NS_ERROR_OUT_OF_MEMORY;
  
  int i;
  for (i = 0; i < fc->numFonts; i++) {
    aFce = nsFreeTypeGetFaceID(fc->fonts[i]);
    genericFce = do_QueryInterface(aFce);
    entries->InsertElementAt(genericFce, 0, PR_FALSE);
  }
 
  free(fc->fonts);
  free(fc);
  *_retval = entries;
  NS_ADDREF(*_retval);
#endif
  return NS_OK;
}

//--------------------------------------------------

#if (defined(MOZ_ENABLE_FREETYPE2))

void
nsFT2FontCatalog::AddDir(nsDirCatalog *dc, nsDirCatalogEntry *dir)
{
  if (dc->numDirs >= dc->numSlots) {
    dc->numSlots += PR_MAX(1, PR_MIN(dc->numDirs, 128));
    dc->dirs = (nsDirCatalogEntry **)realloc(dc->dirs,
                           dc->numSlots*sizeof(nsDirCatalogEntry *));
  }
  dc->dirs[dc->numDirs] = dir;
  dc->numDirs++;
}

PRBool
nsFT2FontCatalog::AddFceIfCurrent(const char *aFileName, 
                                  nsHashtable* aFceHash,
                                  PRInt64 aFileModTime,
                                  nsFontCatalog *aFontCatalog)
{
  int i;
  nsCStringKey key(aFileName);

  //
  // get it from the font summaries
  //
  nsFontCatalogEntry *fce = (nsFontCatalogEntry *)aFceHash->Get(&key);
  if (!fce)
    return PR_FALSE;

  //
  // Check the time
  //
  PRInt64 fs_modtime = fce->mMTime;
  LL_DIV(aFileModTime, aFileModTime, 1000); // microsec -> millisec
  if (LL_NE(aFileModTime, fs_modtime))
    return PR_FALSE;

  //
  // Move to the font catalog
  //
  aFceHash->Remove(&key);
  AddFont(aFontCatalog, fce);

  //
  // get other faces of the font
  //
  for (i=1; i<fce->mNumFaces; i++) {
    nsCAutoString key_str(aFileName);
    // We use this hash when we are checking the files' timestamp.
    // Since we do not want to open the file we cannot use the
    // ttc face names (we would need to open the font to get that).
    // So for the key we append a slash and number to give us a unique key
    // for each ttc face.
    char buf[20];
    sprintf(buf, "/%d", i);
    key_str.Append(buf);
    key = key_str;
    fce = (nsFontCatalogEntry *)aFceHash->Get(&key);
    NS_ASSERTION(fce, "additional font faces missing");
    if (!fce) {
      FONT_CATALOG_PRINTF(("missing font face %d, %s", i, aFileName));
      return PR_FALSE;
    }
    aFceHash->Remove(&key);
    AddFont(aFontCatalog, fce);
  }

  return PR_TRUE;
}

void
nsFT2FontCatalog::AddFont(nsFontCatalog *fc, nsFontCatalogEntry *fce)
{
  if (fc->numFonts >= fc->numSlots) {
    fc->numSlots += PR_MAX(1, PR_MIN(fc->numFonts, 128));
    fc->fonts = (nsFontCatalogEntry **)realloc(fc->fonts,
                           fc->numSlots*sizeof(nsFontCatalogEntry *));
  }
  fc->fonts[fc->numFonts] = fce;
  fc->numFonts++;
}

int
nsFT2FontCatalog::CheckFontSummaryVersion(nsNameValuePairDB *aDB)
{
  const char *type, *name, *value;
  unsigned int num, major, minor, rev;
  int result = FC_FILE_GARBLED;

  if (aDB->GetNextGroup(&type, FONT_SUMMARY_VERSION_TAG)) {
    while (aDB->GetNextElement(&name, &value) > 0) {
      if (*name == '\0') // ignore comments
        continue;
      if (strcmp(name, "Version")==0) {
        num = sscanf(value, "%u.%u.%u", &major, &minor, &rev);
        if (num != 3) {
          FONT_CATALOG_PRINTF(("failed to parse version number (%s)", value));
          return result;
        }
        // FONT_SUMMARY_VERSION_MAJOR
        // It is presumed that major versions are not backwards compatibile.
        if (major == FONT_SUMMARY_VERSION_MAJOR)
          result = FC_FILE_OKAY;
        else
          FONT_CATALOG_PRINTF(("version major %d != %d", major, 
                               FONT_SUMMARY_VERSION_MAJOR));

        // FONT_SUMMARY_VERSION_MINOR
        // If there is a backwards compatibility with different
        // minor versions put the test here.

        // FONT_SUMMARY_VERSION_REV
        // if there should not be backwards compatibility issues
        // with different revisions.
      }
    }
  }

  return result;
}

#ifdef DEBUG
void
nsFT2FontCatalog::DumpFontCatalog(nsFontCatalog *fc)
{
  int i;
  for (i=0; i<fc->numFonts; i++) {
    nsFontCatalogEntry *fce;
    fce = fc->fonts[i];
    if (!fce->mFlags&FCE_FLAGS_ISVALID)
      continue;
    DumpFontCatalogEntry(fce);
  }
}
#endif

#ifdef DEBUG
void
nsFT2FontCatalog::DumpFontCatalogEntry(nsFontCatalogEntry *fce)
{
  printf("  fce->mFontFileName      = %s\n", fce->mFontFileName);
  printf("    fce->mMTime           = %ld %s", fce->mMTime,ctime(&fce->mMTime));
  printf("    fce->mFlags           = 0x%08x\n", fce->mFlags);
  printf("    fce->mFaceIndex       = %d\n", fce->mFaceIndex);
  printf("    fce->mNumFaces        = %d\n", fce->mNumFaces);
  printf("    fce->mFontType        = %s\n", fce->mFontType);
  printf("    fce->mFamilyName      = %s\n", fce->mFamilyName);
  printf("    fce->mStyleName       = %s\n", fce->mStyleName);
  printf("    fce->mNumGlyphs       = %d\n", fce->mNumGlyphs);
  printf("    fce->mNumUsableGlyphs = %d\n", fce->mNumUsableGlyphs);
  printf("    fce->mFaceFlags       = 0x%08lx\n", fce->mFaceFlags);
  printf("    fce->mStyleFlags      = 0x%08lx\n", fce->mStyleFlags);
  printf("                            style: ");
  printf("%s, ", fce->mStyleFlags & FT_STYLE_FLAG_ITALIC ?
                         "italic" : "roman");
  printf("%s", fce->mStyleFlags & FT_STYLE_FLAG_BOLD ?
                         "bold" : "regular");
  printf("\n");
  printf("    fce->mWeight          = %d\n", fce->mWeight);
  printf("    fce->mWidth           = %d\n", fce->mWidth);
  printf("    lang groups (0x%08lx, 0x%08lx): ",
               fce->mCodePageRange1, fce->mCodePageRange2);

  if ((fce->mCodePageRange1 == 0)
      && (fce->mCodePageRange2 == 0)) {
    printf("guessing latin1 (%d glyphs)", fce->mNumGlyphs);
    if (fce->mNumGlyphs > 300) {
      printf("why no lang groups for %s (%s) and so many (%d) glyphs \n",
                 fce->mFamilyName, fce->mFontFileName, fce->mNumGlyphs);
    }
  }

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_LATIN1)
      || (fce->mCodePageRange1 & TT_OS2_CPR1_MAC_ROMAN)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_WE_LATIN1)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_US))
    printf("latin1 (iso8859-1), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_LATIN2)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_LATIN2))
    printf("latin2 (iso8859-2), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_CYRILLIC)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_CYRILLIC))
    printf("cyrillic (iso8859-5), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_GREEK)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_GREEK)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_GREEK_437G))
    printf("greek (iso8859-7), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_TURKISH)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_TURKISH))
    printf("turkish (iso8859-9), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_HEBREW)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_HEBREW))
    printf("hebrew (iso8859-8), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_ARABIC)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_ARABIC)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_ARABIC_708))
    printf("arabic (iso8859-6), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_BALTIC)
      || (fce->mCodePageRange2 & TT_OS2_CPR2_BALTIC))
    printf("baltic (iso8859-4), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_VIETNAMESE)
    printf("vietnamese (viscii1.1-1), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_THAI)
    printf("thai (tis620.2533-1), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_JAPANESE)
    printf("japanese (jisx0208.1990-0), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_CHINESE_SIMP)
    printf("simplified Chinese (gb2312.1980-1), ");

  if ((fce->mCodePageRange1 & TT_OS2_CPR1_KO_WANSUNG)
      || (fce->mCodePageRange1 & TT_OS2_CPR1_KO_JOHAB))
    printf("korean (ksc5601.1992-3), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_CHINESE_TRAD)
    printf("traditional Chinese (big5-0), ");

  if (fce->mCodePageRange1 & TT_OS2_CPR1_SYMBOL)
    printf("symbol (misc-fontspecific), ");

  if (fce->mCodePageRange2 & TT_OS2_CPR2_RUSSIAN)
    printf("russian (koi8-r), ");

  if (fce->mCodePageRange2 & TT_OS2_CPR2_NORDIC)
    printf("nordic (iso8859-10), ");

  if (fce->mCodePageRange2 & TT_OS2_CPR2_CA_FRENCH)
    printf("canadian french (iso8859-1), ");

  if (fce->mCodePageRange2 & TT_OS2_CPR2_ICELANDIC)
    printf("icelandic (iso8859-1), ");

  if (fce->mCodePageRange2 & TT_OS2_CPR2_PORTUGESE)
    printf("portugese (iso8859-1), ");

  printf("\n");

  printf("    fce->mVendorID        = %4s\n", fce->mVendorID);
  printf("    fce->mFoundryName     = %s\n", fce->mFoundryName);
  printf("    fce->mEmbeddedBitmapHeights=");
  for (int i=0; i<fce->mNumEmbeddedBitmaps; i++)
    printf("%d,", fce->mEmbeddedBitmapHeights[i]);
  printf("\n");
  printCCMap(fce->mCCMap);
}
#endif

void
nsFT2FontCatalog::FixUpFontCatalog(nsFontCatalog *fc)
{
  int i;
  for (i=0; i<fc->numFonts; i++) {
    nsFontCatalogEntry *fce;
    fce = fc->fonts[i];
    if (!fce->mFlags&FCE_FLAGS_ISVALID)
      continue;
    // some TrueType fonts seem to have weights in the 1 to 9 range
    if ((fce->mWeight>=1) && (fce->mWeight<=9)) {
      if (mIsNewCatalog)
        FONT_CATALOG_PRINTF(("change weight from %d to %d, %s",
                           fce->mWeight, fce->mWeight*100, fce->mFamilyName));
      fce->mWeight *= 100;
    }
    if ((fce->mWeight<100) || (fce->mWeight>900)) {
      FONT_CATALOG_PRINTF(("invalid weight %d, %s", fce->mWeight, 
                            fce->mFamilyName));
      fce->mFlags &= ~FCE_FLAGS_ISVALID;
      continue;
    }
    if (fce->mWidth>8) {
      FONT_CATALOG_PRINTF(("limit width from %d to 8, %s", fce->mWidth, 
                           fce->mFamilyName));
      fce->mWidth = 8;
    }
    nsCAutoString familyName(fce->mFamilyName);
    free((void*)fce->mFamilyName);
    ToLowerCase(familyName);
    // nsFontMetricsGTK (like XLFD) does not allow a dash in the name
    familyName.ReplaceChar('-', ' ');
    fce->mFamilyName = strdup(familyName.get());
    if (!fce->mFamilyName) {
      fce->mFlags &= ~FCE_FLAGS_ISVALID;
      continue;
    }

    nsCAutoString vendorID(fce->mVendorID);
    ToLowerCase(vendorID);
    vendorID.StripChars(" ");
    nsCStringKey key(vendorID);
    const char *vendorStr = (const char *)sVendorNames->Get(&key);
    if (!vendorStr) {
      if (fce->mVendorID[0])
        vendorStr = fce->mVendorID;
      else
        vendorStr = "<unknown>";
    }
    nsCAutoString vendorName(vendorStr);
    ToLowerCase(vendorName);
    fce->mFoundryName = strdup(vendorName.get());
    if (!fce->mFoundryName) {
      fce->mFlags &= ~FCE_FLAGS_ISVALID;
      continue;
    }
    if ((fce->mCodePageRange1==0) && (fce->mCodePageRange2==0)
        && !(fce->mFlags&FCE_FLAGS_SYMBOL)) {
      // no lang group set so try guessing Latin1
      NS_ASSERTION(fce->mNumGlyphs<=300,
                "font has no lang group bits set AND has over 300 glyphs");
      if (fce->mNumGlyphs>300)
        FONT_CATALOG_PRINTF(("no CodePageRange bits but %d glyphs, %s",
                    fce->mNumGlyphs, fce->mFamilyName));
      fce->mCodePageRange1 |= TT_OS2_CPR1_LATIN1;
    }
  }
}

PRBool
nsFT2FontCatalog::FreeFceHashEntry(nsHashKey* aKey, void* aData, void* aClosure)
{
  nsFontCatalogEntry *fce = (nsFontCatalogEntry *)aData;
  FreeFontCatalogEntry(fce);
  return PR_TRUE;
}

void
nsFT2FontCatalog::FreeFontCatalog(nsFontCatalog *fc)
{
  int i;
  for (i=0; i<fc->numFonts; i++) {
    nsFontCatalogEntry *fce;
    fce = fc->fonts[i];
    FreeFontCatalogEntry(fce);
  }
  free(fc->fonts);
  free(fc);
}

void
nsFT2FontCatalog::FreeDirCatalog(nsDirCatalog *dc)
{
  int i;
  for (i=0; i<dc->numDirs; i++) {
    nsDirCatalogEntry *dce;
    dce = dc->dirs[i];
    FreeDirCatalogEntry(dce);
  }
  free(dc->dirs);
  free(dc);
}

void
nsFT2FontCatalog::FreeDirCatalogEntry(nsDirCatalogEntry *dce)
{
  if (!dce) {
    NS_ASSERTION(dce, "dce is null");
    return;
  }

  FREE_IF(dce->mDirName);
  free(dce);
}


void
nsFT2FontCatalog::FreeFontCatalogEntry(nsFontCatalogEntry *fce)
{
  if (!fce) {
    NS_ASSERTION(fce, "fce is null");
    return;
  }

  FREE_IF(fce->mFontFileName);
  FREE_IF(fce->mFontType);
  FREE_IF(fce->mFamilyName);
  FREE_IF(fce->mStyleName);
  FREE_IF(fce->mFoundryName);
  FREE_IF(fce->mEmbeddedBitmapHeights);
  if (fce->mCCMap)
    FreeCCMap(fce->mCCMap);
  free(fce);
}

void
nsFT2FontCatalog::FreeGlobals()
{
  if (mFontCatalog) {
    FreeFontCatalog(mFontCatalog);
    mFontCatalog = nsnull;
  }

  // sVendorNames elements are not alloc'd so no need call Reset
  delete sVendorNames;
 
  delete mRange1Language;
  delete mRange2Language;

  NS_IF_RELEASE(sPref);
}

void
nsFT2FontCatalog::GetDirsPrefEnumCallback(const char* aName, void* aClosure)
{
  nsDirCatalog *dirCatalog = (nsDirCatalog *)aClosure;
  nsDirCatalogEntry *dce;
  dce = (nsDirCatalogEntry *)calloc(1, sizeof(nsDirCatalogEntry));
  if (!dce)
    return;
  // native charset?!
  sPref->CopyCharPref(aName, (char **)&dce->mDirName);
  if (!dce->mDirName)
    return;
  AddDir(dirCatalog, dce);
}

PRInt32 *
nsFT2FontCatalog::GetEmbeddedBitmapHeights(nsFontCatalogEntry *aFce)
{
  return aFce->mEmbeddedBitmapHeights;
}

PRInt32
nsFT2FontCatalog::GetFaceIndex(nsFontCatalogEntry *aFce)
{
  return aFce->mFaceIndex;
}

const char*
nsFT2FontCatalog::GetFamilyName(nsFontCatalogEntry *aFce)
{
  return aFce->mFamilyName;
}

const char*
nsFT2FontCatalog::GetFileName(nsFontCatalogEntry *aFce)
{
  return aFce->mFontFileName;
}

int
nsFT2FontCatalog::GetFontCatalog(FT_Library lib, nsFontCatalog *aFontCatalog, 
                                 nsDirCatalog *aOldDirCatalog)
{
  int i;
  nsresult rv;
  nsCAutoString font_summaries_dir_path;
  nsCAutoString font_download_dir_path;
  PRBool exists;
  nsCOMPtr<nsIFile> font_summaries_dir, font_download_dir;

  if (lib) {
    //
    // Get the dir for downloaded fonts
    //
    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(font_download_dir));
    if (NS_FAILED(rv))
      goto cleanup_and_return;

    rv = font_download_dir->AppendNative(FONT_DOWNLOAD_SUBDIR);
    if (NS_FAILED(rv))
      goto cleanup_and_return;
    exists = PR_FALSE;
    rv = font_download_dir->Exists(&exists);
    if (NS_FAILED(rv))
      goto cleanup_and_return;
    
    if (!exists) {
      rv = font_download_dir->Create(nsIFile::DIRECTORY_TYPE, 0775);
      if (NS_FAILED(rv))
        goto cleanup_and_return;
    }
    rv = font_download_dir->GetNativePath(font_download_dir_path);
    if (NS_FAILED(rv))
      goto cleanup_and_return;

    //
    // Get the user dir for font catalogs
    //
    rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILES_ROOT_DIR, getter_AddRefs(font_summaries_dir));
    if (NS_FAILED(rv))
      goto cleanup_and_return;

    rv = font_summaries_dir->AppendNative(FONT_DOWNLOAD_SUBDIR);
    if (NS_FAILED(rv))
      goto cleanup_and_return;
    rv = font_summaries_dir->AppendNative(FONT_SUMMARIES_SUBDIR);
    if (NS_FAILED(rv))
      goto cleanup_and_return;
    exists = PR_FALSE;
    rv = font_summaries_dir->Exists(&exists);
    if (NS_FAILED(rv))
      goto cleanup_and_return;
    
    if (!exists) {
      rv = font_summaries_dir->Create(nsIFile::DIRECTORY_TYPE, 0775);
      if (NS_FAILED(rv))
        goto cleanup_and_return;
    }
    rv = font_summaries_dir->GetNativePath(font_summaries_dir_path);
    if (NS_FAILED(rv))
      goto cleanup_and_return;

    //
    // Get the font summaries for the public font dirs
    //
    for (i=0; i<aOldDirCatalog->numDirs; i++) {
      HandleFontDir(lib, aFontCatalog, font_summaries_dir_path,
                    nsDependentCString(aOldDirCatalog->dirs[i]->mDirName));
    }

    //
    // Get the font summaries for the downloaded/private font dir
    //
    HandleFontDir(lib, aFontCatalog, font_summaries_dir_path, font_download_dir_path);
  }

  return 0;

cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::GetFontCatalog failed"));
  return -1;
}

void
nsFT2FontCatalog::GetFontNames(const nsACString & aFamilyName,
                               const nsACString & aLanguage,
                               PRUint16           aWeight,
                               PRUint16           aWidth,
                               PRUint16           aSlant,
                               PRUint16           aSpacing,
                               nsFontCatalog*     aFC)
{
  int i;
  PRUint16 min_weight = PR_MAX(0, aWeight-125);
  PRUint16 max_weight = PR_MIN(999, aWeight+125);
  nsCAutoString familyName, language;
  FONT_CATALOG_PRINTF(("looking for FreeType font matching"));

  ToLowerCase(aFamilyName, familyName);
  ToLowerCase(aLanguage, language);
  
  FONT_CATALOG_PRINTF(("familyName=%s; language=%s; "
                       "weight=%d; width=%d; slant=%d; spacing=%d",
                       familyName.get(), language.get(),
                       aWeight, aWidth, aSlant, aSpacing));

  unsigned long bit1 = GetRangeLanguage(language, CPR1);
  unsigned long bit2 = GetRangeLanguage(language, CPR2);

  PRUint16 italicBit = 0;
  switch (aSlant) {
    case kFCSlantRoman:
      break;
    case kFCSlantItalic:
    case kFCSlantOblique:
    case kFCSlantReverseItalic:
    case kFCSlantReverseOblique:
      italicBit = 1;
      break;
    default:
      break;
  }
  
  PRUint16 monoBit = 0;
  switch (aSpacing) {
    case kFCSpacingMonospace:
      monoBit = 0;
      break;
    case kFCSpacingProportional:
      monoBit = 1;
      break;
    default:
      break;
  }
  
  /* column headers for the debug output*/
  FONT_CATALOG_PRINTF(("%s\t%-20s\t%-8s\t%-8s\t%-8s%-8s%-8s\t%-8s\t",
                        "mFlags",
                        "mFamilyName",
                        "mCodePageRange1",
                        "mCodePageRange2",
                        "mWeight",
                        "mWidth",
                        "mStyleFlags",
                        "fce->mFaceFlags"));

  for (i=0; i<mFontCatalog->numFonts; i++) {
    nsFontCatalogEntry *fce = mFontCatalog->fonts[i];
    // not all "fce" are valid
    if (!fce->mFlags&FCE_FLAGS_ISVALID)
      continue;
    // family
    if (!familyName.IsEmpty() && !familyName.Equals(fce->mFamilyName))
      continue;
    // language
    if (!language.IsEmpty() &&
        !((fce->mCodePageRange1 & bit1) || (fce->mCodePageRange2 & bit2)))
      continue;
    // weight (the meaning is not well defined so allow some variance)
    if ((aWeight != kFCWeightAny)
         && ((fce->mWeight < min_weight) || (fce->mWeight > max_weight)))
      continue;
    // width
    if ((aWidth != kFCWidthAny) && (aWidth != fce->mWidth))
      continue;
    // slant
    if ((aSlant != kFCSlantAny)
         && !((fce->mStyleFlags & FT_STYLE_FLAG_ITALIC) == italicBit))
      continue;
    // spacing
    if ((aSpacing != kFCSpacingAny)
         && !((fce->mFaceFlags & FT_FACE_FLAG_FIXED_WIDTH) == monoBit))
      continue;
    // match all patterns
    FONT_CATALOG_PRINTF(("%0x\t%-20s\t%08lx\t%08lx\t%i\t%i\t%08lx\t%08lx",
                        fce->mFlags,
                        fce->mFamilyName,
                        fce->mCodePageRange1,
                        fce->mCodePageRange2,
                        fce->mWeight,
                        fce->mWidth,
                        fce->mStyleFlags,
                        fce->mFaceFlags));
    AddFont(aFC, fce);
  }
  return;
}

PRBool
nsFT2FontCatalog::GetFontSummaryName(const nsACString &aFontDirName,      // native charset
                                     const nsACString &aFontSummariesDir, // native charset
                                     nsACString &aFontSummaryFileName,
                                     nsACString 
                                               &aFallbackFontSummaryFileName)
{
  int error;
  struct stat file_info;
  PRBool exists = PR_FALSE;
  PRBool public_dir_writable = PR_FALSE;
  PRBool public_summary_writable = PR_FALSE;
  nsresult rv;
  nsCOMPtr<nsILocalFile> font_dir;

  //
  // see if we should use the "public" one in the font dir itself
  // or the "private" one in our $HOME/.mozilla/fontsummaries dir
  //
  font_dir = new nsLocalFile();
  font_dir->InitWithNativePath(aFontDirName);
  rv = font_dir->IsWritable(&public_dir_writable);
  if (NS_SUCCEEDED(rv) && public_dir_writable) {
    FONT_CATALOG_PRINTF(("can write \"%s\"", PromiseFlatCString(aFontDirName).get()));
    nsCOMPtr<nsILocalFile> summary_file = new nsLocalFile();
    summary_file->InitWithNativePath(aFontDirName);
    summary_file->AppendNative(PUBLIC_FONT_SUMMARY_NAME);
    nsCAutoString font_summary_path;
    summary_file->GetNativePath(font_summary_path);
    FONT_CATALOG_PRINTF(("font_summary_path = \"%s\"", font_summary_path.get()));
    rv = summary_file->Exists(&exists);
    if (NS_SUCCEEDED(rv)) {
      if (!exists) {
        public_summary_writable = PR_TRUE;
        aFontSummaryFileName = font_summary_path;
      }
      else {
        FONT_CATALOG_PRINTF(("font summary \"%s\" exists", font_summary_path.get()));
        rv = summary_file->IsWritable(&public_summary_writable);
        if (NS_SUCCEEDED(rv) && public_summary_writable) {
          FONT_CATALOG_PRINTF(("font summary \"%s\" is writable", 
                               font_summary_path.get()));
          public_summary_writable = PR_TRUE;
          aFontSummaryFileName = font_summary_path;
        }
      }
    }
  }

  if (!public_summary_writable) {
    //
    // Generate the private font summary name from:
    //  1) the user's product directory
    //  2) the font dir's parent dir's device/inode
    //  3) the font dir's name in that dir
    // file name = 
    // <last part of the path>.d<device number>.ndb
    // eg: for:
    // user's product dir "/home/bstell/.mozilla/fontsummaries"
    // the font dir "/home/bstell/tt_font"
    // where /home/bstell is inode 1234 on device 3,2
    // the name would be:
    //
    //   /home/bstell/.mozilla/fontsummaries/tt_font.d0302.i1234.ndb
    //

    //
    // Split the parent dir and file name
    //
    PRInt32 slash = 0, last_slash = -1;
    // RFindChar not coded so do it by hand
    while ((slash=aFontDirName.FindChar('/', slash))>=0) {
      last_slash = slash;
      slash++;
    }
    if (last_slash < 0) {
      FONT_CATALOG_PRINTF(("did not find a \"/\" in %s", 
                           PromiseFlatCString(aFontDirName).get())); 
      return PR_FALSE;
    }
    int right_len =  aFontDirName.Length() - last_slash - 1;
    nsCAutoString parent_dir(Substring(aFontDirName, 0, last_slash));
    nsCAutoString font_dir_name_tail(Substring(aFontDirName,
                                               last_slash+1, right_len));
    //
    // Get the parent dir's device and inode
    //
    error = stat(PromiseFlatCString(parent_dir).get(), &file_info);
    if (error) {
      FONT_CATALOG_PRINTF(("failed to stat %s", 
                PromiseFlatCString(parent_dir).get()));
      return PR_FALSE;
    }

    int dev = file_info.st_dev;
    int inode = file_info.st_ino;
    FONT_CATALOG_PRINTF(("parent dir dev = %04x, inode = %d", dev, inode));
    char buf[64];
    sprintf(buf, ".d%04x.i%d", dev, inode);
    font_dir_name_tail.Append(buf);

    //
    // Build the font summary name
    //
    aFontSummaryFileName = aFontSummariesDir;
    aFontSummaryFileName.Append("/");
    aFontSummaryFileName.Append(font_dir_name_tail);
    aFontSummaryFileName.Append(FONT_SUMMARIES_EXTENSION);

    //
    // Build the backup Font Summary name
    //
    aFallbackFontSummaryFileName = aFontDirName;
    aFallbackFontSummaryFileName.Append("/");
    aFallbackFontSummaryFileName.Append(PUBLIC_FONT_SUMMARY_NAME);
  }

  return PR_TRUE;
}

const char *
nsFT2FontCatalog::GetFoundry(nsFontCatalogEntry *aFce)
{
  nsCAutoString foundry(aFce->mVendorID);
  ToLowerCase(foundry);
  foundry.StripChars(" ");
  nsCStringKey key(foundry);
  const char *vendorName = (const char *)sVendorNames->Get(&key);
  if (!vendorName) {
    if (aFce->mVendorID[0])
      vendorName = aFce->mVendorID;
    else
      vendorName = "<unknown>";
  }
  return vendorName;
}

PRInt32
nsFT2FontCatalog::GetNumEmbeddedBitmaps(nsFontCatalogEntry *aFce)
{
  return aFce->mNumEmbeddedBitmaps;
}

PRBool
nsFT2FontCatalog::HandleFontDir(FT_Library aFreeTypeLibrary, 
                                nsFontCatalog *aFontCatalog,
                                const nsACString &aFontSummariesDir, // native charset
                                const nsACString &aFontDirName)      // native charset
{
  int i, status = -1;
  PRBool rslt, current;
  nsCAutoString fileName;
  nsHashtable* fontFileNamesHash = nsnull;
  nsHashtable* fallbackFceHash = nsnull;
  nsresult rv;
  nsCAutoString fontSummaryFilename, fallbackFontSummaryFilename;
  nsFontCatalog *dirFontCatalog = nsnull;
  PRBool moreFilesInDir = PR_FALSE;
  nsCOMPtr<nsIFile> dir;
  nsCOMPtr<nsILocalFile> dirLocal;
  nsCOMPtr<nsILocalFile> dirEntry;
  nsCOMPtr<nsISimpleEnumerator> dirIterator;
  PRBool summary_needs_update = PR_FALSE;
  nsFontCatalogEntry *fce;

  //
  // temp holder for the font summaries in this dir
  //
  dirFontCatalog = NewFontCatalog();
  if (!dirFontCatalog)
    goto cleanup_and_return;
  // also hash to quick access
  fontFileNamesHash = new nsHashtable();
  if (!fontFileNamesHash)
    goto cleanup_and_return;

  //
  // Figure out where the font summary is
  //
  rslt = GetFontSummaryName(aFontDirName, aFontSummariesDir,
                                         fontSummaryFilename,
                                         fallbackFontSummaryFilename);
  if (!rslt) {
    FONT_CATALOG_PRINTF(("failed to get font summary name for %s %s",
                         PromiseFlatCString(aFontDirName).get(),
                         PromiseFlatCString(aFontSummariesDir).get()));
    goto cleanup_and_return;
  }

  FONT_CATALOG_PRINTF(("for \"%s\":\n    font summary = %s"
                                  "\n    fallback     = %s", 
                       PromiseFlatCString(aFontDirName).get(), 
                       PromiseFlatCString(fontSummaryFilename).get(),
                       fallbackFontSummaryFilename.Length()>0 ?
                       fallbackFontSummaryFilename.get()
                       :"<none>"));
            
  //
  // Get the font summaries
  //
  ReadFontDirSummary(fontSummaryFilename, fontFileNamesHash);

  //
  // Open the dir
  //
  dir = new nsLocalFile();
  dirLocal = do_QueryInterface(dir);
  dirLocal->InitWithNativePath(aFontDirName);
  rv = dir->GetDirectoryEntries(getter_AddRefs(dirIterator));
  if (NS_FAILED(rv)) {
    FONT_CATALOG_PRINTF(("failed to open dir (get iterator) for %s", 
                         PromiseFlatCString(aFontDirName).get()));
    goto cleanup_and_return;
  }

  rv = dirIterator->HasMoreElements(&moreFilesInDir);
  if (NS_FAILED(rv)) {
    FONT_CATALOG_PRINTF(("failed HasMoreElements"));
    goto cleanup_and_return;
  }

  //
  // Compare the files in the dir to the font summaries
  //
  while (moreFilesInDir) {
    PRBool isFile;
    current = PR_FALSE;
    rv = dirIterator->GetNext((nsISupports**)getter_AddRefs(dirEntry));
    if (NS_FAILED(rv)) {
      FONT_CATALOG_PRINTF(("failed GetNext"));
      goto cleanup_and_return;
    }
    //char *path;
    dirEntry->GetNativePath(fileName);
    FONT_CATALOG_PRINTF(("dirEntry = \"%s\"", fileName.get()));
    rv = dirEntry->IsFile(&isFile);
    if (NS_SUCCEEDED(rv) && isFile) {
      PRInt64 modtime;
      dirEntry->GetLastModifiedTime(&modtime);
      current = AddFceIfCurrent(fileName.get(), fontFileNamesHash,
                             modtime, dirFontCatalog);
      if (!current) {
        // Ignore the font summary itself
        if (fileName.Equals(fontSummaryFilename) ||
            fileName.Equals(fallbackFontSummaryFilename)) {
          FONT_CATALOG_PRINTF(("font summary %s is not a font", fileName.get()));
          current = PR_TRUE;
        }
        // If not in font summary, try the fallback summary
        else if (fallbackFontSummaryFilename.Length()) {
          if (!fallbackFceHash) {
            fallbackFceHash = new nsHashtable();
            if (fallbackFceHash) {
              ReadFontDirSummary(fallbackFontSummaryFilename, 
                                              fallbackFceHash);
            }
          }
          if (fallbackFceHash) {
            summary_needs_update = PR_TRUE;
            current = AddFceIfCurrent(fileName.get(), fallbackFceHash,
                              modtime, dirFontCatalog);
          }
        }

        //
        // If not found in font summary or fallback
        // then scan the font
        //
        if (!current) {
          summary_needs_update = PR_TRUE;
          HandleFontFile(aFreeTypeLibrary, dirFontCatalog,
                                      fileName.get());
        }
      }
    }
    rv = dirIterator->HasMoreElements(&moreFilesInDir);
    if (NS_FAILED(rv)) {
      FONT_CATALOG_PRINTF(("failed HasMoreElements"));
      goto cleanup_and_return;
    }
  }

  //
  // if hash count != 0 then needs update
  //
  if (fontFileNamesHash->Count()) {
    summary_needs_update = PR_TRUE;
  }

  //
  // Check if need to update font summary
  //
  if (summary_needs_update) {
    FONT_CATALOG_PRINTF(("update the font summary"));
    nsNameValuePairDB tmp_fc;
    if (!tmp_fc.OpenTmpForWrite(fontSummaryFilename))
      return 0; // failed to write but still got font data
    // print the Font Summary version
    PrintFontSummaryVersion(&tmp_fc);
    // print the font summaries
    PrintFontSummaries(&tmp_fc, dirFontCatalog);
    if (tmp_fc.HadError())
      return 0; // failed to write but still got font data
    tmp_fc.RenameTmp(PromiseFlatCString(fontSummaryFilename).get());
  }

  //
  // transfer the fce from this dir to the main font catalog
  //
  for (i=0; i<dirFontCatalog->numFonts; i++) {
    fce = dirFontCatalog->fonts[i];
    AddFont(aFontCatalog, fce);
  }
  dirFontCatalog->numFonts = 0;

  status = 0;

cleanup_and_return:
  if(dirFontCatalog)
    FreeFontCatalog(dirFontCatalog);
  if(fontFileNamesHash) {
    fontFileNamesHash->Reset(FreeFceHashEntry, nsnull);
    delete fontFileNamesHash;
  }
  if(fallbackFceHash) {
    fallbackFceHash->Reset(FreeFceHashEntry, nsnull);
    delete fallbackFceHash;
  }
  return status;
}

void
nsFT2FontCatalog::HandleFontFile(FT_Library aFreeTypeLibrary,
                                 nsFontCatalog *aFontCatalog,
                                 const char *aFontFileName)
{
  int j, num_faces;
  nsFontCatalogEntry *fce;

  /* FONT_CATALOG_PRINTF(("handle font %s", aFontFileName)); */
  fce = NewFceFromFontFile(aFreeTypeLibrary, aFontFileName,
                                     0, &num_faces);
  if (!fce) {
    //FONT_CATALOG_PRINTF(("failed to catalog %s", aFontFileName));
    return;
  }
  AddFont(aFontCatalog, fce);

  /* loop thru the additional faces */
  for (j=1; j<num_faces; j++) {
    fce = NewFceFromFontFile(aFreeTypeLibrary, aFontFileName,
                                       j, nsnull);
    if (!fce) {
      //FONT_CATALOG_PRINTF(("failed to catalog %s/%d\n", aFontFileName, j));
      return;
    }
    AddFont(aFontCatalog, fce);
  }
}

PRBool
nsFT2FontCatalog::InitGlobals(FT_Library lib)
{
  nsDirCatalog *dirCatalog = nsnull;
  nsCAutoString prefix("font.directory.truetype.");
#ifdef DEBUG
  int dump_catalog = 0;
  int num_ftfonts = 0;
  int i;
#endif
  nsFontVendorName *vn = sVendorNamesList;

  nsulCodePageRangeLanguage *crl = nsnull;

  nsServiceManager::GetService(NS_PREF_CONTRACTID,
                               NS_GET_IID(nsIPref),
                               (nsISupports**) &sPref);
  if (!sPref)
    goto cleanup_and_return;

  mFontCatalog = NewFontCatalog();
  if (!mFontCatalog)
    goto cleanup_and_return;

  sVendorNames = new nsHashtable();
  if (!sVendorNames)
    goto cleanup_and_return;
  while (vn->vendorID) {
    nsCAutoString name(vn->vendorID);
    ToLowerCase(name);
    nsCStringKey key(name);
    sVendorNames->Put(&key, (void*)vn->vendorName);
    vn++;
  }

  mRange1Language = new nsHashtable();
  if (!mRange1Language)
    goto cleanup_and_return;
  crl = ulCodePageRange1Language;
  while (crl->language) {
    nsCStringKey key(crl->language);
    mRange1Language->Put(&key, &(crl->bit));
    crl++;
  }

  mRange2Language = new nsHashtable();
  if (!mRange2Language)
    goto cleanup_and_return;
  crl = ulCodePageRange2Language;
  while (crl->language) {
    nsCStringKey key(crl->language);
    mRange2Language->Put(&key, &(crl->bit));
    crl++;
  }

  // get dirs list from prefs
  dirCatalog = NewDirCatalog();
  if (!dirCatalog)
    goto cleanup_and_return;
  sPref->EnumerateChildren(prefix.get(), GetDirsPrefEnumCallback,
                           dirCatalog);

  NS_TIMELINE_START_TIMER("nsFT2FontCatalog::GetFontCatalog");
  GetFontCatalog(lib, mFontCatalog, dirCatalog);
  NS_TIMELINE_STOP_TIMER("nsFT2FontCatalog::GetFontCatalog");
  NS_TIMELINE_MARK_TIMER("nsFT2FontCatalog::GetFontCatalog");
  FreeDirCatalog(dirCatalog);

  FixUpFontCatalog(mFontCatalog);
#ifdef DEBUG
  if (dump_catalog)
    DumpFontCatalog(mFontCatalog);
#endif

#ifdef DEBUG
  for (i=0; i<mFontCatalog->numFonts; i++) {
    if (mFontCatalog->fonts[i]->mFlags&FCE_FLAGS_ISVALID)
      num_ftfonts++;
  }
  FONT_CATALOG_PRINTF(("can use %d TrueType fonts (font dirs hold %d files)",
              num_ftfonts, mFontCatalog->numFonts));
#endif

  return(PR_TRUE);

cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::InitGlobals failed"));
  FreeGlobals();
  return(PR_FALSE);
}

//
// Routine to tell if a char should be blank
// This routing is simplistic but Erik van der Poel recommended
// this and it seems to be sufficient for the windows code;
// In the windows code see SHOULD_BE_SPACE_CHAR(ch)
//
PRBool
nsFT2FontCatalog::IsSpace(FT_Long c)
{
  switch (c) {
    case 0x0020: // ascii space
    case 0x00A0: // non-breaking space
    case 0x3000: // ideographic space
      return PR_TRUE;
  }
  // 2000 == EN quad
  // 2001 == EM quad
  // 2002 == EN space
  // 2003 == EM space
  // 2004 == 3 per EM space
  // 2005 == 4 per EM space
  // 2006 == 6 per EM space
  // 2007 == figure space
  // 2008 == punction space
  // 2009 == thin space
  // 200A == hair space
  // 200B == zero width space

  if ((c >= 0x2000) && (c <= 0x200b))
    return PR_TRUE;
  return PR_FALSE;
}

nsDirCatalog *
nsFT2FontCatalog::NewDirCatalog()
{
  nsDirCatalog *dc;
  dc = (nsDirCatalog *)calloc(1, sizeof(nsDirCatalog));
  return dc;
}

nsFontCatalogEntry *
nsFT2FontCatalog::NewFceFromFontFile(FT_Library aFreeTypeLibrary,
                                     const char *aFontFileName,
                                     int aFaceIndex, int * aNumFaces)
{
  PRUint32 i, len;
  nsFontCatalogEntry *fce;
  FT_Error fterror;
  FT_Face face = nsnull;
  FT_UInt glyph_index;
  FT_GlyphSlot slot;
  TT_OS2 *tt_os2;
  int error;
  struct stat file_info;
  int num_checked = 0;
  int blank_chars;
  nsCompressedCharMap ccmapObj;
  nsresult rv;

  fce = (nsFontCatalogEntry *)calloc(1, sizeof(nsFontCatalogEntry));
  if (!fce)
    return nsnull;

  FONT_SCAN_PRINTF(("font %s ", aFontFileName));
  if (aFaceIndex) {
    FONT_SCAN_PRINTF(("face %d ", aFaceIndex+1));
  }

  error = stat(aFontFileName, &file_info);
  if (error) {
    FONT_SCAN_PRINTF(("  unable to stat font file"));
    goto cleanup_and_return;
  }

  fce->mFontFileName = strdup(aFontFileName);
  if (!fce->mFontFileName)
    goto no_memory_cleanup_and_return;

  fce->mMTime = file_info.st_mtime;
  fce->mFaceIndex = aFaceIndex;

  // open the font
  rv = mFt2->NewFace(aFreeTypeLibrary, aFontFileName, aFaceIndex, &face);
  if (NS_FAILED(rv)) {
    FONT_SCAN_PRINTF(("  FreeType failed to open, error=%d", fterror));
    goto cleanup_and_return;
  }
  if (!FT_IS_SCALABLE(face)) {
    FONT_SCAN_PRINTF(("  not scalable, ignoring"));
    goto cleanup_and_return;
  }

  NS_ASSERTION(face->family_name,"font is missing FamilyName");
  if (!face->family_name)
    goto cleanup_and_return;
  nsTTFontFamilyEncoderInfo *ffei;
  ffei = nsFreeType2::GetCustomEncoderInfo(face->family_name);
  if (ffei) {
    fce->mFlags = FCE_FLAGS_ISVALID | FCE_FLAGS_SYMBOL;
  }
  else {
    for (i=0; i < face->num_charmaps; i++) {

      if (face->charmaps[i]->platform_id == TT_PLATFORM_MICROSOFT) {
        if (face->charmaps[i]->encoding_id == TT_MS_ID_UNICODE_CS) {
          fce->mFlags = FCE_FLAGS_ISVALID | FCE_FLAGS_UNICODE;
          fterror = mFt2->SetCharmap(face, face->charmaps[i]);
          if (fterror) {
            FONT_SCAN_PRINTF(("failed to select unicode charmap"));
            goto cleanup_and_return;
          }
        }
#if defined(TT_MS_ID_UCS_4)
          else if (face->charmaps[i]->encoding_id == TT_MS_ID_UCS_4) {
          fce->mFlags = FCE_FLAGS_ISVALID | FCE_FLAGS_UNICODE | FCE_FLAGS_SURROGATE;
          fterror = mFt2->SetCharmap(face, face->charmaps[i]);
          if (fterror) {
            FONT_SCAN_PRINTF(("failed to select unicode charmap"));
            goto cleanup_and_return;
          }
          break;
        }
#endif
      }
    }
  }
  if (!fce->mFlags&FCE_FLAGS_ISVALID) {
    FONT_SCAN_PRINTF(("%s is missing cmap", face->family_name));
    goto cleanup_and_return;
  }

  mFt2->GetSfntTable(face, ft_sfnt_os2, (void**)&tt_os2);
  if (!tt_os2) {
    FONT_SCAN_PRINTF(("unable to get OS2 table"));
    goto cleanup_and_return;
  }
  fce->mFontType = strdup("TrueType");
  if (!fce->mFontType)
    goto no_memory_cleanup_and_return;
  fce->mWeight = tt_os2->usWeightClass;
  fce->mWidth  = tt_os2->usWidthClass;
  fce->mCodePageRange1 = tt_os2->ulCodePageRange1;
  fce->mCodePageRange2 = tt_os2->ulCodePageRange2;
  // sadly too many fonts have vendor ID blank
  //NS_ASSERTION(*((char*)tt_os2->achVendID), "invalid vendor id");
  memset((char*)fce->mVendorID, 0, sizeof(fce->mVendorID));
  strncpy((char*)fce->mVendorID, (char*)tt_os2->achVendID, 
           sizeof(fce->mVendorID)-1);
  fce->mNumFaces = face->num_faces;
  if (aNumFaces!=nsnull)
    *aNumFaces = face->num_faces;

  //FONT_CATALOG_PRINTF(("family = %s", face->family_name));
  fce->mFamilyName = strdup(face->family_name);
  if (!fce->mFamilyName)
    goto no_memory_cleanup_and_return;

  fce->mStyleName = strdup(face->style_name);
  if (!fce->mStyleName)
    goto no_memory_cleanup_and_return;

  fce->mFaceFlags = face->face_flags;
  fce->mStyleFlags = face->style_flags;
  fce->mNumGlyphs = face->num_glyphs;

  fce->mNumEmbeddedBitmaps = face->num_fixed_sizes;
  if (fce->mNumEmbeddedBitmaps) {
    fce->mEmbeddedBitmapHeights = (int*)calloc(fce->mNumEmbeddedBitmaps,
                                      sizeof(fce->mEmbeddedBitmapHeights[0]));
    if (!fce->mEmbeddedBitmapHeights)
      goto no_memory_cleanup_and_return;
    for (int i=0; i<fce->mNumEmbeddedBitmaps; i++)
      fce->mEmbeddedBitmapHeights[i] = face->available_sizes[i].height;
  }

  if ((fce->mFlags & FCE_FLAGS_UNICODE) || (fce->mFlags & FCE_FLAGS_SURROGATE)) {
    if (fce->mFlags & FCE_FLAGS_SURROGATE) {
      ccmapObj.Extend();
    }

    // check fast methods are defined
    PRBool has_fast_methods;
    mFt2->SupportsExtFunc(&has_fast_methods);

    PRBool scan_done = PR_FALSE;
    if (has_fast_methods) {
      mFt2->GetFirstChar(face, &glyph_index, (FT_ULong*)&i);
      if (glyph_index == 0) {
        // no glyph in the font
        scan_done = PR_TRUE;
      }
    } else {
      i=0;
    }

    len = NUM_UNICODE_CHARS;
    if(fce->mFlags & FCE_FLAGS_SURROGATE) {
      len = 0xFFFFF;
    }

    blank_chars = 0;
    slot = face->glyph;
    FONT_SCAN_PRINTF(("            "));

    while (!scan_done && i < len) {
      if (!has_fast_methods) {
        mFt2->GetCharIndex(face, (FT_ULong)i, &glyph_index);
        //FONT_CATALOG_PRINTF(("i=%d, glyph_index=%d", i, glyph_index));
        if (glyph_index == 0) {
          goto next_char;
        }
      }
      if (gFontDebug & NS_FONT_DEBUG_FONT_SCAN) {
        if ((num_checked % 1000) == 0) {
          FONT_SCAN_PRINTF(("\b\b\b\b\b\b\b\b\b\b\b\b\b\b%7d glyphs", num_checked));
        }
      }
      num_checked++;
      rv = mFt2->LoadGlyph(face, glyph_index,
                 FT_LOAD_NO_SCALE | FT_LOAD_NO_HINTING);
      //FONT_CATALOG_PRINTF(("fterror = %d", fterror));
      if (NS_FAILED(rv)) {
        FONT_CATALOG_PRINTF(("error loading %d (glyph index = %d)", 
                    i, glyph_index));
        goto next_char;
      }
      if (slot->format == ft_glyph_format_outline) {
        //FONT_CATALOG_PRINTF(("n_contours=%d", slot->outline.n_contours));
        if ((slot->outline.n_contours==0) && (!IsSpace(i))) {
          blank_chars++;
          FT_Glyph glyph;
          FT_BBox bbox;
          mFt2->GetGlyph(slot, &glyph);
          mFt2->GlyphGetCBox(glyph, ft_glyph_bbox_pixels, &bbox);
          mFt2->DoneGlyph(glyph);
          if((bbox.xMax==0) && (bbox.xMin==0)
             && (bbox.yMax==0) && (bbox.yMin==0)) {
            goto next_char;
          }
          FONT_SCAN_PRINTF(("no contours %d (glyph index = %d)\n", i, 
                            glyph_index));
          FONT_CATALOG_PRINTF(("character 0x%04x no contour but glyph has "
                               "non-zero bounding box", i));
          NS_WARNING("character no contour but glyph has non-zero bounding box");
        }
      }
      else if (slot->format == ft_glyph_format_bitmap) {
        // this "empty bitmap" test is just a guess
        if ((slot->bitmap.rows==0) || (slot->bitmap.width==0)) {
          FONT_CATALOG_PRINTF(("no bitmap for glyph 0x%04x", i));
          FONT_CATALOG_PRINTF(("continue at line %d", __LINE__));
          FONT_SCAN_PRINTF(("empty bitmap %d (glyph index = %d)\n", 
                             i, glyph_index));
          goto next_char;
        }
      }
      else {
        NS_WARNING("need to test for empty (non-space char) glyph");
      }
      ccmapObj.SetChar(i);
      fce->mNumUsableGlyphs++;
next_char:
      if (has_fast_methods) {
         FT_ULong ncharcode;
         mFt2->GetNextChar(face, (FT_ULong)i, &glyph_index, &ncharcode);
         i = ncharcode;
         if (glyph_index == 0) {
           // no more glyph in the font
           scan_done = PR_TRUE;
         }
      } else {
        i++;
      }
    }
    if (gFontDebug & NS_FONT_DEBUG_FONT_SCAN) {
      FONT_SCAN_PRINTF(("\b\b\b\b\b\b\b\b\b\b\b\b\b\b%7d glyphs", num_checked));
      if (blank_chars) {
        FONT_SCAN_PRINTF((" (%d invalid)", blank_chars));
      }
    }
    if (fce->mNumUsableGlyphs == 0)
      fce->mFlags |= FCE_FLAGS_ISVALID;

    fce->mCCMap = ccmapObj.NewCCMap();
  }
  else if (fce->mFlags & FCE_FLAGS_SYMBOL) {
    FONT_SCAN_PRINTF(("uses custom encoder"));
    if (ffei) {
      nsCOMPtr<nsICharRepresentable> mapper;
      mapper = do_QueryInterface(ffei->mEncodingInfo->mConverter);
      if (mapper) {
        fce->mCCMap = MapperToCCMap(mapper);
      }
    }
  }
  else {
    FONT_SCAN_PRINTF(("unknown font encoding"));
  }
  if (!fce->mCCMap) {
    FONT_CATALOG_PRINTF(("Failed to copy CCMap"));
    goto cleanup_and_return;
  }

  mFt2->DoneFace(face);

  FONT_SCAN_PRINTF(("\n"));
  return fce;

// regardless of why we could not use the file we want to put it in the
// catalog so we do not reopen invalid files every time we startup
cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::NewFceFromFontFile failed"));
  fce->mFlags &= ~FCE_FLAGS_ISVALID;
  fce->mFontType = strdup("");
  fce->mNumFaces = 0;
  if (aNumFaces)
    *aNumFaces = 0;
  fce->mFamilyName = strdup("");
  fce->mFlags = 0;
  fce->mStyleName = strdup("");
  FREE_IF(fce->mEmbeddedBitmapHeights);
  if (face)
    mFt2->DoneFace(face);
  FONT_SCAN_PRINTF(("\n"));
  return fce;

no_memory_cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::NewFceFromFontFile: out of memory"));
  FREE_IF(fce->mFontFileName);
  FREE_IF(fce->mFontType);
  FREE_IF(fce->mFamilyName);
  FREE_IF(fce->mStyleName);
  if (fce->mCCMap)
    FreeCCMap(fce->mCCMap);
  FREE_IF(fce);
  if (face)
    mFt2->DoneFace(face);
  FONT_SCAN_PRINTF(("\n"));
  return nsnull;
}

nsFontCatalogEntry *
nsFT2FontCatalog::NewFceFromSummary(nsNameValuePairDB *aDB)
{
  const char *type, *name, *value;
  PRBool rslt;
  nsFontCatalogEntry *fce = nsnull;
  long longVal;
  unsigned long uLongVal;
  int num, intVal, i;
  nsCompressedCharMap ccmapObj;

  fce = (nsFontCatalogEntry *)calloc(1, sizeof(nsFontCatalogEntry));
  if (!fce)
    goto cleanup_and_return;

  if (!aDB->GetNextGroup(&type, "Font_", 5)) {
    FONT_CATALOG_PRINTF(("file garbled: expected begin=Font_, got %s", type));
    goto cleanup_and_return;
  }

  while (aDB->GetNextElement(&name, &value) > 0) {
    if (STRMATCH(name,"FamilyName")) {
      if (fce->mFamilyName) {
        FONT_CATALOG_PRINTF(("family name defined multiple times (%s, %s)", 
                          fce->mFamilyName, value));
        goto cleanup_and_return;
      }
      fce->mFamilyName = strdup(value);
      if (!fce->mFamilyName)
        goto cleanup_and_return;
    }
    else if (STRMATCH(name,"Flags")) {
      if (fce->mFlags != 0) {
        FONT_CATALOG_PRINTF(("Flags defined multiple times (%s)",
                fce->mFontFileName?fce->mFontFileName:""));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse Flags (%s)",value));
        goto cleanup_and_return;
      }
      fce->mFlags = uLongVal;
      if (fce->mFlags & FCE_FLAGS_SURROGATE) {
        ccmapObj.Extend();
      }
    }
    else if (STRMATCH(name,"FontFileName")) {
      if (fce->mFontFileName) {
        FONT_CATALOG_PRINTF(("font filename defined multiple times (%s, %s)",
                          fce->mFontFileName, value));
        goto cleanup_and_return;
      }
      fce->mFontFileName = strdup(value);
      if (!fce->mFontFileName)
        goto cleanup_and_return;
    }
    else if (STRMATCH(name,"MTime")) {
      if (fce->mMTime != 0) {
        FONT_CATALOG_PRINTF(("time defined multiple times (%s)",
                fce->mFontFileName?fce->mFontFileName:""));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lu", &uLongVal);
      if ((num != 1) || (uLongVal==0)) {
        FONT_CATALOG_PRINTF(("failed to parse time (%s)",value));
        goto cleanup_and_return;
      }
      fce->mMTime = uLongVal;
    }
    else if (STRMATCH(name,"FontType")) {
      if (fce->mFontType) {
        FONT_CATALOG_PRINTF(("font type defined multiple times (%s, %s)",
                          fce->mFontType, value));
        goto cleanup_and_return;
      }
      fce->mFontType = strdup(value);
      if (!fce->mFontType)
        goto cleanup_and_return;
    }
    else if (STRMATCH(name,"FaceIndex")) {
      if (fce->mFaceIndex != 0) {
        FONT_CATALOG_PRINTF(("face index defined multiple times (%d, %s)",
                          fce->mFaceIndex, value));
        goto cleanup_and_return;
      }
      longVal = atol(value);
      if (longVal<0) {
        FONT_CATALOG_PRINTF(("failed to parse face index (%s)",value));
        goto cleanup_and_return;
      }
      fce->mFaceIndex = longVal;
    }
    else if (STRMATCH(name,"NumFaces")) {
      if (fce->mNumFaces != 0) {
        FONT_CATALOG_PRINTF(("num faces defined multiple times (%d, %s)",
                          fce->mNumFaces, value));
        goto cleanup_and_return;
      }
      intVal = atoi(value);
      if (intVal<0) {
        FONT_CATALOG_PRINTF(("failed to parse num faces (%s)",value));
        goto cleanup_and_return;
      }
      fce->mNumFaces = intVal;
    }
    else if (STRMATCH(name,"StyleName")) {
      if (fce->mStyleName != 0) {
        FONT_CATALOG_PRINTF(("font style defined multiple times (%s, %s)", 
                              fce->mStyleName, value));
        goto cleanup_and_return;
      }
      fce->mStyleName = strdup(value);
      if (!fce->mStyleName)
        goto cleanup_and_return;
    }
    else if (STRMATCH(name,"NumGlyphs")) {
      if (fce->mNumGlyphs != 0) {
        FONT_CATALOG_PRINTF(("num glyphs defined multiple times (%d, %s)",
                                     fce->mNumGlyphs, value));
        goto cleanup_and_return;
      }
      intVal = atoi(value);
      if (intVal<0) {
        FONT_CATALOG_PRINTF(("failed to parse num glyphs (%s)",value));
        goto cleanup_and_return;
      }
      fce->mNumGlyphs = intVal;
    }
    else if (STRMATCH(name,"NumUsableGlyphs")) {
      if (fce->mNumUsableGlyphs != 0) {
        FONT_CATALOG_PRINTF(("num usable glyphs defined multiple times (%d, %s)",
                                     fce->mNumUsableGlyphs, value));
        goto cleanup_and_return;
      }
      intVal = atoi(value);
      if (intVal<0) {
        FONT_CATALOG_PRINTF(("failed to parse num usable glyphs (%s)",value));
        goto cleanup_and_return;
      }
      fce->mNumUsableGlyphs = intVal;
    }
    else if (STRMATCH(name,"FaceFlags")) {
      if (fce->mFaceFlags != 0) {
        FONT_CATALOG_PRINTF(("face flags defined multiple times (0x%lx, %s)",
                          fce->mFaceFlags, value));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse face flags (%s)",value));
        goto cleanup_and_return;
      }
      fce->mFaceFlags = uLongVal;
    }
    else if (STRMATCH(name,"StyleFlags")) {
      if (fce->mStyleFlags != 0) {
        FONT_CATALOG_PRINTF(("style flags defined multiple times (0x%lx, %s)",
                          fce->mStyleFlags, value));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse style flags (%s)",value));
        goto cleanup_and_return;
      }
      fce->mStyleFlags = uLongVal;
    }
    else if (STRMATCH(name,"Weight")) {
      if (fce->mWeight != 0) {
        FONT_CATALOG_PRINTF(("weight defined multiple times (0x%d, %s)",
                          fce->mWeight, value));
        goto cleanup_and_return;
      }
      intVal = atoi(value);
      if ((intVal<0) || (intVal>950)) {
        FONT_CATALOG_PRINTF(("failed to parse weight (%s)",value));
        goto cleanup_and_return;
      }
      fce->mWeight = (unsigned short)intVal;
    }
    else if (STRMATCH(name,"Width")) {
      if (fce->mWidth != 0) {
        FONT_CATALOG_PRINTF(("width defined multiple times (0x%d, %s)",
                          fce->mWeight, value));
        goto cleanup_and_return;
      }
      intVal = atoi(value);
      if ((intVal<0) || (intVal>9)) {
        FONT_CATALOG_PRINTF(("failed to parse (or invalid) width (%s)",value));
        goto cleanup_and_return;
      }
      fce->mWidth = (unsigned short)intVal;
    }
    else if (STRMATCH(name,"CodePageRange1")) {
      if (fce->mCodePageRange1 != 0) {
        FONT_CATALOG_PRINTF(("CodePageRange1 defined multiple times (0x%lx, %s)",
                          fce->mCodePageRange1, value));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse mCodePageRange1 (%s)",value));
        goto cleanup_and_return;
      }
      fce->mCodePageRange1 = uLongVal;
    }
    else if (STRMATCH(name,"CodePageRange2")) {
      if (fce->mCodePageRange2 != 0) {
        FONT_CATALOG_PRINTF(("CodePageRange2 defined multiple times (0x%lx, %s)",
                          fce->mCodePageRange2, value));
        goto cleanup_and_return;
      }
      num = sscanf(value, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse mCodePageRange2 (%s)",value));
        goto cleanup_and_return;
      }
      fce->mCodePageRange2 = uLongVal;
    }
    else if (STRMATCH(name,"VendorID")) {
      if (fce->mVendorID[0] != '\0') {
        FONT_CATALOG_PRINTF(("vendor id defined multiple times (%s, %s)",
                          fce->mVendorID, value));
        goto cleanup_and_return;
      }
      memset((char*)fce->mVendorID, 0, sizeof(fce->mVendorID));
      strncpy((char*)fce->mVendorID, value, sizeof(fce->mVendorID)-1);
    }
    else if (STRMATCH(name,"EmbeddedBitmapHeights")) {
      if (fce->mNumEmbeddedBitmaps != 0) {
        FONT_CATALOG_PRINTF(("EmbeddedBitmapHeights defined multiple times"));
        goto cleanup_and_return;
      }
      int embeddedBitmapHeights[1024];
      int maxEmbeddedBitmaps =
              sizeof(embeddedBitmapHeights)/sizeof(embeddedBitmapHeights[0]);
      int numEmbeddedBitmaps = 0;
      const char *p = value;
      const char *q;
      while ((q=strchr(p, ','))) {
        intVal = atoi(p);
        if (intVal < 1) {
          FONT_CATALOG_PRINTF(("failed to parse EmbeddedBitmapHeights(%s)",value));
          goto cleanup_and_return;
        }
        embeddedBitmapHeights[numEmbeddedBitmaps++] = intVal;
        if (numEmbeddedBitmaps >= maxEmbeddedBitmaps) {
          FONT_CATALOG_PRINTF(("can only handle %d numEmbeddedBitmaps",
                                maxEmbeddedBitmaps));
          break;
        }
        p = q + 1; // point to next height
      }
      fce->mNumEmbeddedBitmaps = numEmbeddedBitmaps;
      if (fce->mNumEmbeddedBitmaps) {
        fce->mEmbeddedBitmapHeights = (int*)calloc(fce->mNumEmbeddedBitmaps,
                                                    sizeof(int));
        if (!fce->mEmbeddedBitmapHeights)
          goto cleanup_and_return;
        for (int i=0; i<fce->mNumEmbeddedBitmaps; i++)
          fce->mEmbeddedBitmapHeights[i] = embeddedBitmapHeights[i];
      }
    }
    else if (STRNMATCH(name,"CCMap:",6)) {
      num = sscanf(name+6, "%lx", &uLongVal);
      if (num != 1) {
        FONT_CATALOG_PRINTF(("failed to parse CCMap address (%s)",name+6));
        goto cleanup_and_return;
      }
      rslt = ParseCCMapLine(&ccmapObj, uLongVal, value);
      if (!rslt) {
        FONT_CATALOG_PRINTF(("failed to parse CCMap value (%s)",value));
        goto cleanup_and_return;
      }
    }
  }

  fce->mCCMap = ccmapObj.NewCCMap();

  if (!fce->mCCMap) {
    FONT_CATALOG_PRINTF(("missing char map"));
    goto cleanup_and_return;
  }
  if (!fce->mFamilyName) {
    FONT_CATALOG_PRINTF(("missing family name"));
    goto cleanup_and_return;
  }
  if ((fce->mFlags&FCE_FLAGS_ISVALID) && (fce->mFlags == 0)) {
    FONT_CATALOG_PRINTF(("missing flags"));
    goto cleanup_and_return;
  }
  if (!fce->mFontFileName) {
    FONT_CATALOG_PRINTF(("missing font file name"));
    goto cleanup_and_return;
  }
  if (fce->mMTime == 0) {
    FONT_CATALOG_PRINTF(("missing mtime"));
    goto cleanup_and_return;
  }
  if (!fce->mFontType) {
    FONT_CATALOG_PRINTF(("missing font type"));
    goto cleanup_and_return;
  }
  if (!fce->mStyleName) {
    FONT_CATALOG_PRINTF(("missing style name"));
    goto cleanup_and_return;
  }
  if ((fce->mFlags&FCE_FLAGS_ISVALID) && (fce->mNumGlyphs == 0)) {
    FONT_CATALOG_PRINTF(("missing num glyphs"));
    goto cleanup_and_return;
  }
  if (fce->mFlags&FCE_FLAGS_ISVALID 
      && ((fce->mFlags & FCE_FLAGS_UNICODE) || (fce->mFlags & FCE_FLAGS_SURROGATE))
      && (fce->mNumUsableGlyphs == 0)) {
    FONT_CATALOG_PRINTF(("missing num usable glyphs"));
    goto cleanup_and_return;
  }

  return fce;

cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::NewFceFromSummary failed"));
  if (fce->mCCMap)
    FreeCCMap(fce->mCCMap);
  FREE_IF(fce->mFamilyName);
  FREE_IF(fce->mFontFileName);
  FREE_IF(fce->mFontType);
  FREE_IF(fce->mStyleName);
  FREE_IF(fce->mEmbeddedBitmapHeights);
  FREE_IF(fce);
  return nsnull;
}

nsFontCatalog *
nsFT2FontCatalog::NewFontCatalog()
{
  nsFontCatalog *fc;
  fc = (nsFontCatalog *)calloc(1, sizeof(nsFontCatalog));
  return fc;
}

PRBool
nsFT2FontCatalog::ParseCCMapLine(nsCompressedCharMap *aCCMapObj, long base, 
                                 const char *valString)
{
  int i, j, len;
  unsigned int byte;
  PRUint32 pagechar = (PRUint32)base;

  // check we have the right number of chars
  len = strlen(valString);
  if (len != (2*(CCMAP_BITS_PER_PAGE/8)))
    return PR_FALSE;

  const char *p = valString;
  for (i=0; i<(CCMAP_BITS_PER_PAGE/8); i++) {
#if 0 // scanf is way too slow
    int num = sscanf(valString+(2*i), "%2x", &byte);
    if (num != 1)
      return PR_FALSE;
#else
    NS_ASSERTION((*p>='0' && *p<='9')
              || (*p>='a' && *p<='f')
              || (*p>='A' && *p<='F'), "Invalid ccmap char");
    if (*p <= '9')
      byte = *p++ - '0';
    else {
      byte = (*p++ & 0x4F) - 'A' + 10;
    }
    byte <<= 4;
    NS_ASSERTION((*p>='0' && *p<='9')
              || (*p>='a' && *p<='f')
              || (*p>='A' && *p<='F'), "Invalid ccmap char");
    if (*p <= '9')
      byte |= *p++ - '0';
    else {
      byte |= (*p++ & 0x4F) - 'A' + 10;
    }
#endif
    if (byte == 0) {
      pagechar += 8;
      continue;
    }

    for (j=0; j<8; j++) {
      if (byte & (1<<j))
        aCCMapObj->SetChar(pagechar);
      pagechar++;
    }
  }
  return PR_TRUE;
}


void
nsFT2FontCatalog::PrintCCMap(nsNameValuePairDB *aDB, PRUint16 *aCCMap)
{
  if (!aCCMap)
    return;

  PRUint32 page = CCMAP_BEGIN_AT_START_OF_MAP;
  while (NextNonEmptyCCMapPage(aCCMap, &page)) {
    //FONT_CATALOG_PRINTF(("page starting at 0x%04x has chars", page));
    PrintPageBits(aDB, aCCMap, page);
  }
}

void
nsFT2FontCatalog::PrintFontSummaries(nsNameValuePairDB *aDB, 
                                     nsFontCatalog *aFontCatalog)
{
  int i;
  nsFontCatalogEntry *fce;
  char font_num[30];
  char buf[30];

  aDB->PutStartGroup("FontSummariesInfo");
  aDB->PutElement("", "#############################");
  aDB->PutElement("", "# Font Summaries            #");
  aDB->PutElement("", "#############################");
  aDB->PutElement("", "#");
  sprintf(buf, "%d", aFontCatalog->numFonts);
  aDB->PutElement("NumFonts", buf);
  aDB->PutEndGroup("FontSummariesInfo");
  for (i=0; i<aFontCatalog->numFonts; i++) {
    fce = aFontCatalog->fonts[i];
    sprintf(font_num, "Font_%d", i);
    aDB->PutStartGroup(font_num);
    aDB->PutElement("FamilyName",     fce->mFamilyName);
    sprintf(buf, "%08x",             fce->mFlags);
    aDB->PutElement("Flags", buf);
    aDB->PutElement("FontFileName",   fce->mFontFileName);
    sprintf(buf, "%ld",               fce->mMTime);
    aDB->PutElement("MTime", buf);
    aDB->PutElement("FontType",       fce->mFontType);
    sprintf(buf, "%d",                fce->mFaceIndex);
    aDB->PutElement("FaceIndex", buf);
    sprintf(buf, "%d",                fce->mNumFaces);
    aDB->PutElement("NumFaces", buf);
    aDB->PutElement("StyleName",      fce->mStyleName);
    sprintf(buf, "%d",                fce->mNumGlyphs);
    aDB->PutElement("NumGlyphs", buf);
    sprintf(buf, "%d",                fce->mNumUsableGlyphs);
    aDB->PutElement("NumUsableGlyphs", buf);
    sprintf(buf, "%08lx",             fce->mFaceFlags);
    aDB->PutElement("FaceFlags", buf);
    sprintf(buf, "%08lx",             fce->mStyleFlags);
    aDB->PutElement("StyleFlags", buf);
    sprintf(buf, "%d",                fce->mWeight);
    aDB->PutElement("Weight", buf);
    sprintf(buf, "%d",                fce->mWidth);
    aDB->PutElement("Width", buf);
    sprintf(buf, "%08lx",             fce->mCodePageRange1);
    aDB->PutElement("CodePageRange1", buf);
    sprintf(buf, "%08lx",             fce->mCodePageRange2);
    aDB->PutElement("CodePageRange2", buf);
    aDB->PutElement("VendorID",       fce->mVendorID);
    nsCAutoString embeddedHeightStr("");
    for (int j=0; j<fce->mNumEmbeddedBitmaps; j++) {
      sprintf(buf, "%d,", fce->mEmbeddedBitmapHeights[j]);
      embeddedHeightStr.Append(buf);
    }
    aDB->PutElement("EmbeddedBitmapHeights", 
               PromiseFlatCString(embeddedHeightStr).get());
    aDB->PutElement("", "# ccmap");

    PrintCCMap(aDB, fce->mCCMap);
    aDB->PutEndGroup(font_num);
  }
}

void
nsFT2FontCatalog::PrintFontSummaryVersion(nsNameValuePairDB *aDB)
{
  char buf[30];

  aDB->PutStartGroup(FONT_SUMMARY_VERSION_TAG);
  aDB->PutElement("", "#############################");
  aDB->PutElement("", "# Font Summary Version      #");
  aDB->PutElement("", "#############################");
  aDB->PutElement("", "#");
  sprintf(buf, "%u.%u.%u", FONT_SUMMARY_VERSION_MAJOR, 
                           FONT_SUMMARY_VERSION_MINOR,
                           FONT_SUMMARY_VERSION_REV);
  aDB->PutElement("Version", buf);
  aDB->PutEndGroup(FONT_SUMMARY_VERSION_TAG);
}

void
nsFT2FontCatalog::PrintPageBits(nsNameValuePairDB *aDB, PRUint16 *aCCMap, 
                                PRUint32 aPageStart)
{
  int i;
  PRUint32 pagechar = aPageStart;
  char addr[64];
  char buf[64];


  nsCAutoString ccmap_line("");
  for (i=0; i<(CCMAP_BITS_PER_PAGE/8); i++) {
    unsigned char val = 0;
    for (int j=0; j<8; j++) {
      if (CCMAP_HAS_CHAR_EXT(aCCMap, pagechar)) {
        val |= 1 << j;
      }
      pagechar++;
    }
    sprintf(buf, "%02x", val);
    ccmap_line.Append(buf);
  }
  sprintf(addr, "CCMap:0x%04lx", (long)aPageStart);
  aDB->PutElement(addr, PromiseFlatCString(ccmap_line).get());
}

PRBool
nsFT2FontCatalog::ReadFontDirSummary(const nsACString& aFontSummaryFilename,
                                     nsHashtable* aFontFileNamesHash)
{
  nsNameValuePairDB fc;
  int status;

  if (!fc.OpenForRead(aFontSummaryFilename)) {
    FONT_CATALOG_PRINTF(("could not open font catalog %s", 
                         PromiseFlatCString(aFontSummaryFilename).get()));
    return PR_FALSE;
  }

  //
  // check the font summary version number
  //
  status = CheckFontSummaryVersion(&fc);
  if (status != FC_FILE_OKAY)
    goto cleanup_and_return;

  //
  // read font summaries
  //
  ReadFontSummaries(aFontFileNamesHash, &fc);

  return PR_TRUE;

cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::ReadFontDirSummary failed"));
  return PR_FALSE;
}

int
nsFT2FontCatalog::ReadFontSummaries(nsHashtable* aFontHash, 
                                    nsNameValuePairDB *aDB)
{

  const char *type, *name, *value;
  int i, numFonts = 0;
  nsFontCatalogEntry *fce = nsnull;

  //
  // Get the number of font summaries
  //
  if (!aDB->GetNextGroup(&type, "FontSummariesInfo")) {
    FONT_CATALOG_PRINTF(("file garbled: expected begin=FontSummariesInfo, "
                         "got %s", type));
    goto cleanup_and_return;
  }
  while (aDB->GetNextElement(&name, &value) > 0) {
    if (strcmp(name, "NumFonts")==0) {
      numFonts = atoi(value);
      if (numFonts<0) {
        FONT_CATALOG_PRINTF(("failed to parse num fonts (%s)", value));
        goto cleanup_and_return;
      }
    }
  }

  for (i=0; i<numFonts; i++) {
    fce = NewFceFromSummary(aDB);
    if (!fce)
      goto cleanup_and_return;
    nsCStringKey key(fce->mFontFileName);
    // Since we are hashing by file name we need to add a FaceIndex
    // else we would lose the other ttc faces
    if (fce->mFaceIndex != 0) {
      // add face num to hash key
      nsCAutoString key_str(fce->mFontFileName);
      char buf[20];
      sprintf(buf, "/%d", fce->mFaceIndex);
      key_str.Append(buf);
      key = key_str;
    }
    FONT_CATALOG_PRINTF(("key = %s", key.GetString()));
    aFontHash->Put(&key, (void *)fce);
  }

  return numFonts;

cleanup_and_return:
  FONT_CATALOG_PRINTF(("nsFT2FontCatalog::ReadFontSummaries failed"));
  return 0;
}

unsigned long
nsFT2FontCatalog::GetRangeLanguage(const nsACString & aLanguage,
                                   PRInt16 aRange)
{
  unsigned long *bit;
  if (aLanguage.IsEmpty())
    return 0;
  nsCStringKey key(aLanguage);
  if (aRange == CPR1)
    bit = (unsigned long *)(mRange1Language->Get(&key));
  if (aRange == CPR2)
    bit = (unsigned long *)(mRange2Language->Get(&key));
  if (bit)
    return *bit;
  return 0;
}

nsFontVendorName sVendorNamesList[] = {
  { "2REB", "2Rebels"                                     },
  { "39BC", "Finley's Barcode Fonts"                      },
  { "3ip",  "Three Islands Press"                         },
  { "918",  "RavenType"                                   },
  { "ABC",  "Altek Instruments"                           },
  { "ACUT", "Acute Type"                                  },
  { "ADBE", "Adobe"                                       },
  { "AGFA", "Agfa Monotype"                               },
  { "ALPH", "Alphameric Broadcast Solutions"              },
  { "ALTS", "Altsys (Macromedia)"                         },
  { "AOP",  "An Art of Penguin"                           },
  { "APOS", "Apostrophic Laboratories"                    },
  { "APPL", "Apple"                                       },
  { "ARPH", "Arphic Technology"                           },
  { "ARS",  "EN ARS"                                      },
  { "ATEC", "Page Technology Marketing"                   },
  { "AZLS", "Azalea Software"                             },
  { "B&H",  "Bigelow & Holmes"                            },
  { "BARS", "CIA (BAR CODES) UK"                          },
  { "BERT", "Berthold"                                    },
  { "BITM", "Bitmap Software"                             },
  { "BITS", "Bitstream"                                   },
  { "BLAH", "Mister Bla's Fontworx"                       },
  { "BORW", "Borware"                                     },
  { "BOYB", "BoyBeaver Fonts"                             },
  { "BRTC", "Bear Rock Technologies"                      },
  { "BWFW", "B/W Fontworks"                               },
  { "C21",  "Club 21"                                     },
  { "CAK",  "pluginfonts"                                 },
  { "CANO", "Canon"                                       },
  { "CASL", "H.W. Caslon"                                 },
  { "CDAC", "Centre for Development of Advanced Computing"},
  { "CFA",  "Computer Fonts Australia"                    },
  { "CONR", "Connare"                                     },
  { "COOL", "Cool Fonts"                                  },
  { "CORD", "corduroy"                                    },
  { "CTDL", "China Type Designs"                          },
  { "cwwf", "Computers World Wide/AC Capital Funding"     },
  { "DAMA", "Dalton Maag"                                 },
  { "DELV", "Delve Media Arts"                            },
  { "DFS",  "Datascan Font Service"                       },
  { "DGL",  "Digital Graphic Labs foundry"                },
  { "DS",   "Dainippon Screen"                            },
  { "DSCI", "Design Science"                              },
  { "DSST", "Dubina Nikolay"                              },
  { "DTC",  "Digital Typeface"                            },
  { "DTPS", "DTP-Software"                                },
  { "dtpT", "dtpTypes"                                    },
  { "DUXB", "Duxbury Systems"                             },
  { "DYNA", "DynaLab"                                     },
  { "EDGE", "Rivers Edge"                                 },
  { "EFF",  "Electronic Font Foundry"                     },
  { "EFNT", "E Fonts L.L.C."                              },
  { "ELSE", "Elseware (Hewlett-Packard)"                  },
  { "EMGR", "Emigre"                                      },
  { "EPSN", "Epson"                                       },
  { "ESIG", "E-Signature"                                 },
  { "EVER", "Everson Typography"                          },
  { "FBI",  "The Font Bureau"                             },
  { "FCAB", "The Font Cabinet"                            },
  { "FCAN", "fontage canada"                              },
  { "FNTF", "Fontfoundry"                                 },
  { "FONT", "Font Source"                                 },
  { "FS",   "Formula Solutions"                           },
  { "FSE",  "Font Source Europe"                          },
  { "FSI",  "FSI Fonts und Software"                      },
  { "FTFT", "FontFont"                                    },
  { "FWRE", "Fontware"                                    },
  { "GALA", "Galápagos Design Group"                      },
  { "GD",   "GD Fonts"                                    },
  { "GF",   "GarageFonts"                                 },
  { "GLYF", "Glyph Systems"                               },
  { "GOAT", "Dingbat Dungeon"                             },
  { "GOGO", "Fonts-A-Go-Go"                               },
  { "GPI",  "Gamma Productions"                           },
  { "GRIL", "Grilled cheese"                              },
  { "HILL", "Hill Systems"                                },
  { "HL",   "High-Logic"                                  },
  { "HOUS", "House Industries"                            },
  { "HP",   "Hewlett-Packard"                             },
  { "HS",   "HermesSOFT"                                  },
  { "HY",   "HanYang System"                              },
  { "IBM",  "IBM"                                         },
  { "IDAU", "IDAutomation"                                },
  { "IDEE", "IDEE TYPOGRAFICA"                            },
  { "IDF",  "International Digital Fonts"                 },
  { "ILP",  "Indigenous Language Project"                 },
  { "IMPR", "Impress"                                     },
  { "INVC", "Invoice Central"                             },
  { "ITF",  "International TypeFounders"                  },
  { "KATF", "Kingsley/ATF"                                },
  { "KUBA", "Kuba Tatarkiewicz"                           },
  { "LARA", "Larabiefonts"                                },
  { "LEAF", "Interleaf"                                   },
  { "LETR", "Letraset"                                    },
  { "LGX",  "Logix Research Institute"                    },
  { "LING", "Linguist's Software"                         },
  { "LINO", "Linotype"                                    },
  { "LP",   "LetterPerfect Fonts"                         },
  { "LTRX", "Lighttracks"                                 },
  { "MACR", "Macromedia"                                  },
  { "MATS", "Match Fonts"                                 },
  { "MC",   "Cerajewski Computer Consulting"              },
  { "MEIR", "Meir Sadan"                                  },
  { "MF",   "Magic Fonts"                                 },
  { "MFNT", "Masterfont"                                  },
  { "MILL", "Millan"                                      },
  { "MJ",   "Majus"                                       },
  { "MJR",  "Majur"                                       },
  { "MLGC", "Micrologic Software"                         },
  { "MONO", "Agfa Monotype"                               },
  { "MOON", "Moonlight Type and Technolog"                },
  { "MS",   "Microsoft"                                   },
  { "MSCR", "Majus"                                       },
  { "MSE",  "MSE-iT"                                      },
  { "MT",   "Agfa Monotype"                               },
  { "MTY",  "Motoya"                                      },
  { "MUTF", "Murasu Systems Sdn. Bhd"                     },
  { "MYFO", "MyFonts"                                     },
  { "NB",   "No Bodoni Typography"                        },
  { "NDTC", "Neufville Digital"                           },
  { "NEC",  "NEC"                                         },
  { "NIS",  "NIS"                                         },
  { "ORBI", "Orbit Enterprises"                           },
  { "P22",  "P22"                                         },
  { "PARA", "ParaType"                                    },
  { "PDWX", "Parsons Design Workx"                        },
  { "PF",   "Phil's Fonts"                                },
  { "PLAT", "PLATINUM technology"                         },
  { "PRFS", "Production First Software"                   },
  { "PSY",  "PSY/OPS"                                     },
  { "PTF",  "Porchez Typofonderie"                        },
  { "PTMI", "Page Technology Marketing"                   },
  { "PYRS", "Pyrus"                                       },
  { "QMSI", "QMS/Imagen"                                  },
  { "RKFN", "R K Fonts"                                   },
  { "robo", "Buro Petr van Blokland"                      },
  { "RRT",  "Red Rooster Typefounders"                    },
  { "RUDY", "RudynFluffy"                                 },
  { "RYOB", "Ryobi"                                       },
  { "SAX",  "s.a.x. Software"                             },
  { "Sean", "The FontSite"                                },
  { "SFUN", "Software Union"                              },
  { "SG",   "Scooter Graphics"                            },
  { "SHFT", "Shift"                                       },
  { "SIG",  "Signature Software"                          },
  { "SIL",  "SIL International"                           },
  { "SIT",  "Summit Information Technologies"             },
  { "skz",  "Celtic Lady's Fonts"                         },
  { "SN",   "SourceNet"                                   },
  { "SOHO", "Soft Horizons"                               },
  { "SOS",  "Standing Ovations Software"                  },
  { "STF",  "Brian Sooy + Sooy Type Foundry"              },
  { "SUNW", "sunwalk fontworks"                           },
  { "SWFT", "Swfte International"                         },
  { "SYN",  "SynFonts"                                    },
  { "SYRC", "Syriac Computing Institute"                  },
  { "TDR",  "Tansin A. Darcos"                            },
  { "TERM", "Terminal Design"                             },
  { "TF",   "Treacyfaces / Headliners"                    },
  { "TILD", "Tilde, SIA"                                  },
  { "TIRO", "Tiro Typeworks"                              },
  { "TMF",  "The MicroFoundry"                            },
  { "TPTC", "Test Pilot Collective"                       },
  { "TR",   "Type Revivals"                               },
  { "TS",   "TamilSoft"                                   },
  { "TTG",  "Twardoch Typography"                         },
  { "TYPE", "TypeGroup"                                   },
  { "TYPO", "Typodermic"                                  },
  { "UA",   "UnAuthorized Type"                           },
  { "UNKN", "Unknown Vendor"                              },
  { "URW",  "URW++"                                       },
  { "UT",   "Unitype"                                     },
  { "VKP",  "Vijay K. Patel"                              },
  { "VLKF", "Visualogik Technology & Design"              },
  { "VOG",  "Martin Vogel"                                },
  { "VROM", "Vladimir Romanov"                            },
  { "VT",   "VISUALTYPE SRL"                              },
  { "WASP", "Wasp barcode"                                },
  { "WM",   "Webmakers India"                             },
  { "XFC",  "Xerox Font Services"                         },
  { "Y&Y",  "Y&Y"                                         },
  { "ZeGr", "Zebra Font Factory"                          },
  { "zeta", "Tangram Studio"                              },
  { "ZSFT", "Zsoft"                                       },
  { nsnull,  nsnull                                       },
};
 

// TODO: what TT_OS2_CPR1_OEM standard for. use "oem" temporarily.
nsulCodePageRangeLanguage ulCodePageRange1Language[] = {
{ TT_OS2_CPR1_LATIN1 | TT_OS2_CPR1_MAC_ROMAN,     "x-western"           },
{ TT_OS2_CPR1_LATIN2,                             "x-central-euro"      },
{ TT_OS2_CPR1_CYRILLIC,                           "x-cyrillic"          },
{ TT_OS2_CPR1_GREEK,                              "el"                  },
{ TT_OS2_CPR1_TURKISH,                            "tr"                  },
{ TT_OS2_CPR1_HEBREW,                             "he"                  },
{ TT_OS2_CPR1_ARABIC,                             "ar"                  },
{ TT_OS2_CPR1_BALTIC,                             "x-baltic"            },
{ TT_OS2_CPR1_VIETNAMESE,                         "vietnamese"          },
{ TT_OS2_CPR1_THAI,                               "th"                  },
{ TT_OS2_CPR1_JAPANESE,                           "ja"                  },
{ TT_OS2_CPR1_CHINESE_SIMP,                       "zh-cn"               },
{ TT_OS2_CPR1_KO_WANSUNG | TT_OS2_CPR1_KO_JOHAB,  "ko"                  },
{ TT_OS2_CPR1_CHINESE_TRAD,                       "zh-tw"               },
{ TT_OS2_CPR1_OEM,                                "oem"                 },
{ TT_OS2_CPR1_SYMBOL,                             "symbol"              },
{ 0,                                              nsnull                },
};

nsulCodePageRangeLanguage ulCodePageRange2Language[] = {
{ TT_OS2_CPR2_GREEK | TT_OS2_CPR2_GREEK_437G,     "el"                  },
{ TT_OS2_CPR2_RUSSIAN,                            "x-cyrillic"          },
{ TT_OS2_CPR2_NORDIC,                             "x-western"           },
{ TT_OS2_CPR2_ARABIC | TT_OS2_CPR2_ARABIC_708,    "ar"                  },
{ TT_OS2_CPR2_CA_FRENCH,                          "x-western"           },
{ TT_OS2_CPR2_HEBREW,                             "he"                  },
{ TT_OS2_CPR2_ICELANDIC,                          "icelandic"           },
{ TT_OS2_CPR2_PORTUGESE,                          "x-western"           },
{ TT_OS2_CPR2_TURKISH,                            "tr"                  },
{ TT_OS2_CPR2_CYRILLIC,                           "x-cyrillic"          },
{ TT_OS2_CPR2_LATIN2,                             "x-central-euro"      },
{ TT_OS2_CPR2_BALTIC,                             "x-baltic"            },
{ TT_OS2_CPR2_WE_LATIN1 || TT_OS2_CPR2_US,        "x-western"           },
{ 0,                                              nsnull                },
};
#endif /* #if (!defined(MOZ_ENABLE_FREETYPE2)) */
