short nsUnicodeFontMappingMac::GetFontID(PRUnichar aChar) {
    // initialize to bogus values
    short firstSymbolicFont = BAD_FONT_NUM, firstNonSymbolicFont = BAD_FONT_NUM;
    PRInt32 firstSymbolicFontIndex = -1;
    
    // Trap invisible chars
    if (CCMAP_HAS_CHAR_EXT(gIgnorableCCMapExt, aChar)) {
      return IGNORABLE_FONT_NUM;
    }

    // find the first symbolic font that has a glyph for aChar
    // and if there is one, remember it's index in the font list
    for(PRInt32 i = 0; i < mFontList.Count(); i++)
    {
        nsUnicodeFontMappingEntry* entry = (nsUnicodeFontMappingEntry*) mFontList[i];
        PRUint16 *ccmap = entry->GetCCMap();
        if(ccmap && CCMAP_HAS_CHAR(ccmap, aChar))
        {
            firstSymbolicFontIndex = i;
            firstSymbolicFont = entry->GetFontNum();
            break;
        }
    }

    // find the first non-symbolic font that has a glyph for aChar
	nsUnicodeBlock block = GetBlock(aChar);
	if(block < kUnicodeBlockFixedScriptMax) 
	{
		firstNonSymbolicFont = mScriptFallbackFontIDs[gUtil->BlockToScript(block)];
		
		// if there was no symbolic font we don't need to loop through the list again
        if(firstSymbolicFont == BAD_FONT_NUM)
            return firstNonSymbolicFont;
        
        // find the index of the first non symbolic font in the list and return the 
        // first font (symbolic or non symbolic) in the list that has a glyph for this char
        for(PRInt32 i = 0; i < mFontList.Count(); i++)
        {
            nsUnicodeFontMappingEntry* entry = (nsUnicodeFontMappingEntry*) mFontList[i];
            if(entry->GetFontNum() == firstNonSymbolicFont)
            {
                return (firstSymbolicFontIndex < i ? firstSymbolicFont : firstNonSymbolicFont);
            }
        }
        return firstNonSymbolicFont;
	}
	
    return (firstSymbolicFont != BAD_FONT_NUM ? firstSymbolicFont : 
		mScriptFallbackFontIDs[ mPrivBlockToScript[ block - kUnicodeBlockFixedScriptMax] ]);
}
void
nsCompressedCharMap::SetChars(PRUint16* aCCMap)
{
  int i, j;
  if(mExtended){
    PRUint32 page = CCMAP_BEGIN_AT_START_OF_MAP;
    while (NextNonEmptyCCMapPage(aCCMap, &page)) {
      PRUint32 pagechar = page;
      for (i=0; i<(CCMAP_BITS_PER_PAGE/8); i++) {
        for (j=0; j<8; j++) {
          if (CCMAP_HAS_CHAR_EXT(aCCMap, pagechar)) {
            SetChar(pagechar);
          }
          pagechar++;
        }
      }
    }
  } else {
    //
    // Copy the input CCMap
    //
    // walk thru the upper pointers
    PRUint16 *upper = &aCCMap[0];
    for (i=0; i<CCMAP_NUM_UPPER_POINTERS; i++) {
      if (upper[i] == CCMAP_EMPTY_MID)
        continue;

      // walk the mid array
      PRUint16 *mid = &aCCMap[upper[i]];
      for (j=0; j<CCMAP_NUM_MID_POINTERS; j++) {
        if (mid[j] == CCMAP_EMPTY_PAGE)
          continue;

        PRUint32 base = (i*CCMAP_NUM_UCHARS_PER_MID) + (j*CCMAP_NUM_UCHARS_PER_PAGE);
        NS_ASSERTION(base<NUM_UNICODE_CHARS, "invalid page address");
        ALU_TYPE *page = (ALU_TYPE*)&aCCMap[mid[j]];
        SetChars((PRUint16)base, page);
      }
    }
  }
}
// Non-BMP unicode support extension, create ccmap for both BMP and extended planes 
PRUint16*
MapToCCMapExt(PRUint32* aBmpPlaneMap, PRUint32** aOtherPlaneMaps, PRUint32 aOtherPlaneNum)
{
  nsCompressedCharMap* otherPlaneObj[EXTENDED_UNICODE_PLANES];
  PRUint32 totalSize;
  PRUint16 i;
  PRUint32 *planeCCMapOffsets;
  PRUint32 currOffset;

  NS_ASSERTION(aOtherPlaneNum <= EXTENDED_UNICODE_PLANES, "illegal argument value");
  if (aOtherPlaneNum > EXTENDED_UNICODE_PLANES)
    return nsnull;

  // Put the data into a temp map
  nsCompressedCharMap bmpCcmapObj;
  bmpCcmapObj.SetChars(aBmpPlaneMap);

  // Add bmp size
  totalSize = bmpCcmapObj.GetSize();

  // Add bmp length field
  totalSize += CCMAP_EXTRA;
  
  // Add Plane array 
  totalSize += EXTENDED_UNICODE_PLANES * sizeof(PRUint32)/sizeof(PRUint16);

  // Add an empty plane ccmap
  // A totally empty plane ccmap can be represented by 16 *(PRUint16)0. 
  totalSize += CCMAP_EMPTY_SIZE_PER_INT16;

  // Create ccmap for other planes
  for (i = 0; i < aOtherPlaneNum; i++) {
    if (aOtherPlaneMaps[i]) {
      otherPlaneObj[i] = new nsCompressedCharMap();
      NS_ASSERTION(otherPlaneObj, "unable to create new nsCompressedCharMap");
      if(otherPlaneObj) {
        otherPlaneObj[i]->SetChars(aOtherPlaneMaps[i]);
        totalSize += otherPlaneObj[i]->GetSize();
      }
    } else {
      otherPlaneObj[i] = nsnull;
    }
  }

  PRUint16 *ccmap = (PRUint16*)PR_Malloc(totalSize * sizeof(PRUint16));
  NS_ASSERTION(ccmap, "failed to alloc new CCMap");

  if (!ccmap)
    return nsnull;

  // Assign BMP ccmap size
  ccmap += CCMAP_EXTRA;
  CCMAP_SIZE(ccmap) = bmpCcmapObj.GetSize();
  CCMAP_FLAG(ccmap) = CCMAP_SURROGATE_FLAG;

  // Fill bmp plane ccmap 
  bmpCcmapObj.FillCCMap(ccmap);

  // Get pointer for plane ccmap offset array
  currOffset = bmpCcmapObj.GetSize();
  planeCCMapOffsets = (PRUint32*)(ccmap+currOffset);
  currOffset += sizeof(PRUint32)/sizeof(PRUint16)*EXTENDED_UNICODE_PLANES;

  // Put a empty ccmap there 
  memset(ccmap+currOffset, '\0', sizeof(PRUint16)*16);
  PRUint32 emptyCCMapOffset = currOffset;
  currOffset += CCMAP_EMPTY_SIZE_PER_INT16;

  // Now fill all rest of the planes' ccmap and put off in array
  for (i = 0; i <aOtherPlaneNum; i++) {
    if (aOtherPlaneMaps[i] && otherPlaneObj[i]) {
      *(planeCCMapOffsets+i) = currOffset;
      otherPlaneObj[i]->FillCCMap(ccmap+currOffset);
      currOffset += otherPlaneObj[i]->GetSize();
    }
    else 
      *(planeCCMapOffsets+i) = emptyCCMapOffset;
  }
  for (; i < EXTENDED_UNICODE_PLANES; i++) {
    *(planeCCMapOffsets+i) = emptyCCMapOffset;
  }

  // remove all nsCompressedCharMap objects allocated 
  for (i = 0; i < aOtherPlaneNum; i++) {
    if (otherPlaneObj[i]) 
      delete otherPlaneObj[i];
  }

#ifdef DEBUG
  PRUint32 k, h, l, plane, offset;
  PRBool oldb;
  PRBool newb;

  // testing for BMP plane
  for (k=0; k<NUM_UNICODE_CHARS; k++) {
    oldb = IS_REPRESENTABLE(aBmpPlaneMap, k);
    newb = CCMAP_HAS_CHAR_EXT(ccmap, k);
    NS_ASSERTION(oldb==newb,"failed to generate map correctly");
  }

  //testing for extension plane 
  for (k = 0x10000; k < 0x100000; k++) {
    plane = k/0x10000;
    if (plane > aOtherPlaneNum)
      break;
    if (aOtherPlaneMaps[plane-1])
      oldb = IS_REPRESENTABLE(aOtherPlaneMaps[plane-1], k&0xffff);
    else
      oldb = 0;
    newb = CCMAP_HAS_CHAR_EXT(ccmap, k);
    NS_ASSERTION(oldb==newb, "failed to generate extension map correctly");
  }

  
  // testing for non-BMP plane
    for (h = 0; h < 0x400; h++) {
      for (l = 0; l < 0x400; l++) {
        plane = h >> 6;
        offset = (h*0x400 + l) & 0xffff;
        if (aOtherPlaneMaps[plane])
          oldb = IS_REPRESENTABLE(aOtherPlaneMaps[plane], offset);
        else
          oldb = 0;
        newb = CCMAP_HAS_CHAR_EXT2(ccmap, h+0xd800, l+0xdc00);
        NS_ASSERTION(oldb==newb, "failed to generate extension map correctly");
      }
    }
#endif

  return ccmap;
}