Mozilla/mozilla/gfx/src/windows/nsFontMetricsWin.cpp
kmcclusk%netscape.com 2fb86dacaa Added support for font weights
git-svn-id: svn://10.0.0.236/trunk@48383 18797224-902f-48f8-a5cc-f745e15eee43
1999-09-20 21:02:01 +00:00

2082 lines
54 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsFontMetricsWin.h"
#include "prmem.h"
#include "plhash.h"
static NS_DEFINE_IID(kIFontMetricsIID, NS_IFONT_METRICS_IID);
nsGlobalFont* nsFontMetricsWin::gGlobalFonts = nsnull;
static int gGlobalFontsAlloc = 0;
int nsFontMetricsWin::gGlobalFontsCount = 0;
PLHashTable* nsFontMetricsWin::gFamilyNames = nsnull;
//-- Font weight
PLHashTable* nsFontMetricsWin::gFontWeights = nsnull;
#define NS_MAX_FONT_WEIGHT 900
#define NS_MIN_FONT_WEIGHT 100
nsFontMetricsWin :: nsFontMetricsWin()
{
NS_INIT_REFCNT();
mSpaceWidth = 0;
}
nsFontMetricsWin :: ~nsFontMetricsWin()
{
if (nsnull != mFont) {
delete mFont;
mFont = nsnull;
}
mFontHandle = nsnull; // released below
if (mFonts) {
delete [] mFonts;
mFonts = nsnull;
}
if (mLoadedFonts) {
nsFontWin** font = mLoadedFonts;
nsFontWin** end = &mLoadedFonts[mLoadedFontsCount];
while (font < end) {
if ((*font)->font) {
::DeleteObject((*font)->font);
}
delete *font;
font++;
}
PR_Free(mLoadedFonts);
mLoadedFonts = nsnull;
}
mDeviceContext = nsnull;
}
#ifdef LEAK_DEBUG
nsrefcnt
nsFontMetricsWin :: AddRef()
{
NS_PRECONDITION(mRefCnt != 0, "resurrecting a dead object");
return ++mRefCnt;
}
nsrefcnt
nsFontMetricsWin :: Release()
{
NS_PRECONDITION(mRefCnt != 0, "too many release's");
if (--mRefCnt == 0) {
delete this;
}
return mRefCnt;
}
nsresult
nsFontMetricsWin :: QueryInterface(REFNSIID aIID, void** aInstancePtr)
{
if (NULL == aInstancePtr) {
return NS_ERROR_NULL_POINTER;
}
*aInstancePtr = NULL;
static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID);
static NS_DEFINE_IID(kClassIID, kIFontMetricsIID);
if (aIID.Equals(kClassIID)) {
*aInstancePtr = (void*) this;
NS_ADDREF_THIS();
return NS_OK;
}
if (aIID.Equals(kISupportsIID)) {
*aInstancePtr = (void*) ((nsISupports*)this);
NS_ADDREF_THIS();
return NS_OK;
}
return NS_NOINTERFACE;
}
#else
NS_IMPL_ISUPPORTS(nsFontMetricsWin, kIFontMetricsIID)
#endif
NS_IMETHODIMP
nsFontMetricsWin :: Init(const nsFont& aFont, nsIDeviceContext *aContext)
{
mFont = new nsFont(aFont);
//don't addref this to avoid circular refs
mDeviceContext = (nsDeviceContextWin *)aContext;
RealizeFont();
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: Destroy()
{
mDeviceContext = nsnull;
return NS_OK;
}
nsresult nsFontMetricsWin :: GetSpaceWidth(nscoord &aSpaceWidth)
{
aSpaceWidth = mSpaceWidth;
return NS_OK;
}
void
nsFontMetricsWin::FillLogFont(LOGFONT* logFont, PRInt32 aWeight)
{
// Fill in logFont structure; stolen from awt
logFont->lfWidth = 0;
logFont->lfEscapement = 0;
logFont->lfOrientation = 0;
logFont->lfUnderline =
(mFont->decorations & NS_FONT_DECORATION_UNDERLINE)
? TRUE : FALSE;
logFont->lfStrikeOut =
(mFont->decorations & NS_FONT_DECORATION_LINE_THROUGH)
? TRUE : FALSE;
logFont->lfCharSet = DEFAULT_CHARSET;
logFont->lfOutPrecision = OUT_DEFAULT_PRECIS;
logFont->lfClipPrecision = CLIP_DEFAULT_PRECIS;
logFont->lfQuality = DEFAULT_QUALITY;
logFont->lfPitchAndFamily = DEFAULT_PITCH | FF_DONTCARE;
logFont->lfWeight = aWeight;
logFont->lfItalic = (mFont->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE))
? TRUE : FALSE; // XXX need better oblique support
float app2dev, app2twip, scale;
mDeviceContext->GetAppUnitsToDevUnits(app2dev);
mDeviceContext->GetDevUnitsToTwips(app2twip);
mDeviceContext->GetCanonicalPixelScale(scale);
app2twip *= app2dev * scale;
// this interesting bit of code rounds the font size off to the floor point value
// this is necessary for proper font scaling under windows
PRInt32 sizePoints = NSTwipsToFloorIntPoints(nscoord(mFont->size * app2twip));
float rounded = ((float)NSIntPointsToTwips(sizePoints)) / app2twip;
// round font size off to floor point size to be windows compatible
logFont->lfHeight = - NSToIntRound(rounded * app2dev); // this is proper (windows) rounding
// logFont->lfHeight = - LONG(rounded * app2dev); // this floor rounding is to make ours compatible with Nav 4.0
#ifdef NS_DEBUG
// Make Purify happy
memset(logFont->lfFaceName, 0, sizeof(logFont->lfFaceName));
#endif
}
#undef CMAP
#define CMAP (('c') | ('m' << 8) | ('a' << 16) | ('p' << 24))
#undef HEAD
#define HEAD (('h') | ('e' << 8) | ('a' << 16) | ('d' << 24))
#undef LOCA
#define LOCA (('l') | ('o' << 8) | ('c' << 16) | ('a' << 24))
#undef NAME
#define NAME (('n') | ('a' << 8) | ('m' << 16) | ('e' << 24))
#undef GET_SHORT
#define GET_SHORT(p) (((p)[0] << 8) | (p)[1])
#undef GET_LONG
#define GET_LONG(p) (((p)[0] << 24) | ((p)[1] << 16) | ((p)[2] << 8) | (p)[3])
static PRUint16
GetGlyph(PRUint16 segCount, PRUint16* endCode, PRUint16* startCode,
PRUint16* idRangeOffset, PRUint16* idDelta, PRUint8* end, PRUint16 aChar)
{
PRUint16 glyph = 0;
PRUint16 i;
for (i = 0; i < segCount; i++) {
if (endCode[i] >= aChar) {
break;
}
}
PRUint16 startC = startCode[i];
if (startC <= aChar) {
if (idRangeOffset[i]) {
PRUint16* p =
(idRangeOffset[i]/2 + (aChar - startC) + &idRangeOffset[i]);
if ((PRUint8*) p < end) {
if (*p) {
glyph = idDelta[i] + *p;
}
}
}
else {
glyph = idDelta[i] + aChar;
}
}
return glyph;
}
static int
GetNAME(HDC aDC, nsString* aName)
{
DWORD len = GetFontData(aDC, NAME, 0, nsnull, 0);
if ((len == GDI_ERROR) || (!len)) {
return 0;
}
PRUint8* buf = (PRUint8*) PR_Malloc(len);
if (!buf) {
return 0;
}
DWORD newLen = GetFontData(aDC, NAME, 0, buf, len);
if (newLen != len) {
PR_Free(buf);
return 0;
}
PRUint8* p = buf + 2;
PRUint16 n = GET_SHORT(p);
p += 2;
PRUint16 offset = GET_SHORT(p);
p += 2;
PRUint16 i;
PRUint16 idLength;
PRUint16 idOffset;
for (i = 0; i < n; i++) {
PRUint16 platform = GET_SHORT(p);
p += 2;
PRUint16 encoding = GET_SHORT(p);
p += 4;
PRUint16 name = GET_SHORT(p);
p += 2;
idLength = GET_SHORT(p);
p += 2;
idOffset = GET_SHORT(p);
p += 2;
// XXX what about symbol? (platform 3, encoding 0)
if ((platform == 3) && (encoding == 1) && (name == 3)) {
break;
}
}
if (i == n) {
PR_Free(buf);
return 0;
}
p = buf + offset + idOffset;
idLength /= 2;
for (i = 0; i < idLength; i++) {
PRUnichar c = GET_SHORT(p);
p += 2;
aName->Append(c);
}
PR_Free(buf);
return 1;
}
static PLHashNumber
HashKey(const void* aString)
{
return (PLHashNumber)
nsCRT::HashValue(((const nsString*) aString)->GetUnicode());
}
static PRIntn
CompareKeys(const void* aStr1, const void* aStr2)
{
return nsCRT::strcmp(((const nsString*) aStr1)->GetUnicode(),
((const nsString*) aStr2)->GetUnicode()) == 0;
}
static int
GetIndexToLocFormat(HDC aDC)
{
PRUint16 indexToLocFormat;
if (GetFontData(aDC, HEAD, 50, &indexToLocFormat, 2) != 2) {
return -1;
}
if (!indexToLocFormat) {
return 0;
}
return 1;
}
static PRUint8*
GetSpaces(HDC aDC, PRUint32* aMaxGlyph)
{
int isLong = GetIndexToLocFormat(aDC);
if (isLong < 0) {
return nsnull;
}
DWORD len = GetFontData(aDC, LOCA, 0, nsnull, 0);
if ((len == GDI_ERROR) || (!len)) {
return nsnull;
}
PRUint8* buf = (PRUint8*) PR_Malloc(len);
if (!buf) {
return nsnull;
}
DWORD newLen = GetFontData(aDC, LOCA, 0, buf, len);
if (newLen != len) {
PR_Free(buf);
return nsnull;
}
if (isLong) {
DWORD longLen = ((len / 4) - 1);
*aMaxGlyph = longLen;
PRUint32* longBuf = (PRUint32*) buf;
for (PRUint32 i = 0; i < longLen; i++) {
if (longBuf[i] == longBuf[i+1]) {
buf[i] = 1;
}
else {
buf[i] = 0;
}
}
}
else {
DWORD shortLen = ((len / 2) - 1);
*aMaxGlyph = shortLen;
PRUint16* shortBuf = (PRUint16*) buf;
for (PRUint16 i = 0; i < shortLen; i++) {
if (shortBuf[i] == shortBuf[i+1]) {
buf[i] = 1;
}
else {
buf[i] = 0;
}
}
}
return buf;
}
#undef SET_SPACE
#define SET_SPACE(c) ADD_GLYPH(spaces, c)
#undef SHOULD_BE_SPACE
#define SHOULD_BE_SPACE(c) FONT_HAS_GLYPH(spaces, c)
PRUint8*
nsFontMetricsWin::GetCMAP(HDC aDC)
{
static PLHashTable* fontMaps = nsnull;
static int initialized = 0;
if (!initialized) {
initialized = 1;
fontMaps = PL_NewHashTable(0, HashKey, CompareKeys, nsnull, nsnull,
nsnull);
}
nsString* name = new nsString();
if (!name) {
return nsnull;
}
PRUint8* map;
if (GetNAME(aDC, name)) {
map = (PRUint8*) PL_HashTableLookup(fontMaps, name);
if (map) {
delete name;
return map;
}
map = (PRUint8*) PR_Calloc(8192, 1);
if (!map) {
delete name;
return nsnull;
}
}
else {
// return an empty map, so that we never try this font again
static PRUint8* emptyMap = nsnull;
if (!emptyMap) {
emptyMap = (PRUint8*) PR_Calloc(8192, 1);
}
delete name;
return emptyMap;
}
DWORD len = GetFontData(aDC, CMAP, 0, nsnull, 0);
if ((len == GDI_ERROR) || (!len)) {
delete name;
PR_Free(map);
return nsnull;
}
PRUint8* buf = (PRUint8*) PR_Malloc(len);
if (!buf) {
delete name;
PR_Free(map);
return nsnull;
}
DWORD newLen = GetFontData(aDC, CMAP, 0, buf, len);
if (newLen != len) {
PR_Free(buf);
delete name;
PR_Free(map);
return nsnull;
}
PRUint8* p = buf + 2;
PRUint16 n = GET_SHORT(p);
p += 2;
PRUint16 i;
PRUint32 offset;
for (i = 0; i < n; i++) {
PRUint16 platformID = GET_SHORT(p);
p += 2;
PRUint16 encodingID = GET_SHORT(p);
p += 2;
offset = GET_LONG(p);
p += 4;
// XXX what about symbol fonts? (platform = 3, encoding = 0)
if ((platformID == 3) && (encodingID == 1)) {
break;
}
}
if (i == n) {
PR_Free(buf);
delete name;
PR_Free(map);
return nsnull;
}
p = buf + offset;
PRUint16 format = GET_SHORT(p);
if (format != 4) {
PR_Free(buf);
delete name;
PR_Free(map);
return nsnull;
}
PRUint8* end = buf + len;
// XXX byte swapping only required for little endian (ifdef?)
while (p < end) {
PRUint8 tmp = p[0];
p[0] = p[1];
p[1] = tmp;
p += 2;
}
PRUint16* s = (PRUint16*) (buf + offset);
PRUint16 segCount = s[3] / 2;
PRUint16* endCode = &s[7];
PRUint16* startCode = endCode + segCount + 1;
PRUint16* idDelta = startCode + segCount;
PRUint16* idRangeOffset = idDelta + segCount;
PRUint16* glyphIdArray = idRangeOffset + segCount;
static int spacesInitialized = 0;
static PRUint8 spaces[8192];
if (!spacesInitialized) {
spacesInitialized = 1;
SET_SPACE(0x0020);
SET_SPACE(0x00A0);
for (PRUint16 c = 0x2000; c <= 0x200B; c++) {
SET_SPACE(c);
}
SET_SPACE(0x3000);
}
PRUint32 maxGlyph;
PRUint8* isSpace = GetSpaces(aDC, &maxGlyph);
if (!isSpace) {
PR_Free(buf);
delete name;
PR_Free(map);
return nsnull;
}
for (i = 0; i < segCount; i++) {
if (idRangeOffset[i]) {
PRUint16 startC = startCode[i];
PRUint16 endC = endCode[i];
for (PRUint32 c = startC; c <= endC; c++) {
PRUint16* g =
(idRangeOffset[i]/2 + (c - startC) + &idRangeOffset[i]);
if ((PRUint8*) g < end) {
if (*g) {
PRUint16 glyph = idDelta[i] + *g;
if (glyph < maxGlyph) {
if (isSpace[glyph]) {
if (SHOULD_BE_SPACE(c)) {
ADD_GLYPH(map, c);
}
}
else {
ADD_GLYPH(map, c);
}
}
}
}
else {
// XXX should we trust this font at all if it does this?
}
}
//printf("0x%04X-0x%04X ", startC, endC);
}
else {
PRUint16 endC = endCode[i];
for (PRUint32 c = startCode[i]; c <= endC; c++) {
PRUint16 glyph = idDelta[i] + c;
if (glyph < maxGlyph) {
if (isSpace[glyph]) {
if (SHOULD_BE_SPACE(c)) {
ADD_GLYPH(map, c);
}
}
else {
ADD_GLYPH(map, c);
}
}
}
//printf("0x%04X-0x%04X ", startCode[i], endC);
}
}
//printf("\n");
PR_Free(buf);
PR_Free(isSpace);
// XXX check to see if an identical map has already been added to table
PL_HashTableAdd(fontMaps, name, map);
return map;
}
nsFontWin*
nsFontMetricsWin::LoadFont(HDC aDC, nsString* aName)
{
LOGFONT logFont;
PRUint16 weightTable = LookForFontWeightTable(aDC, aName);
PRInt32 weight = GetFontWeight(mFont->weight, weightTable);
FillLogFont(&logFont, weight);
/*
* XXX we are losing info by converting from Unicode to system code page
* but we don't really have a choice since CreateFontIndirectW is
* not supported on Windows 9X (see below) -- erik
*/
logFont.lfFaceName[0] = 0;
WideCharToMultiByte(CP_ACP, 0, aName->GetUnicode(), aName->Length() + 1,
logFont.lfFaceName, sizeof(logFont.lfFaceName), nsnull, nsnull);
/*
* According to http://msdn.microsoft.com/library/
* CreateFontIndirectW is only supported on NT/2000
*/
HFONT hfont = ::CreateFontIndirect(&logFont);
if (hfont) {
if (mLoadedFontsCount == mLoadedFontsAlloc) {
int newSize = 2 * (mLoadedFontsAlloc ? mLoadedFontsAlloc : 1);
nsFontWin** newPointer = (nsFontWin**) PR_Realloc(mLoadedFonts,
newSize * sizeof(nsFontWin*));
if (newPointer) {
mLoadedFonts = newPointer;
mLoadedFontsAlloc = newSize;
}
else {
::DeleteObject(hfont);
return nsnull;
}
}
nsFontWin* font = new nsFontWin;
if (!font) {
::DeleteObject(hfont);
return nsnull;
}
mLoadedFonts[mLoadedFontsCount++] = font;
HFONT oldFont = (HFONT) ::SelectObject(aDC, (HGDIOBJ) hfont);
font->font = hfont;
font->map = GetCMAP(aDC);
if (!font->map) {
mLoadedFontsCount--;
::SelectObject(aDC, (HGDIOBJ) oldFont);
::DeleteObject(hfont);
return nsnull;
}
::SelectObject(aDC, (HGDIOBJ) oldFont);
return font;
}
return nsnull;
}
static int CALLBACK enumProc(const LOGFONT* logFont, const TEXTMETRIC* metrics,
DWORD fontType, LPARAM closure)
{
// XXX do we really want to ignore non-TrueType fonts?
if (!(fontType & TRUETYPE_FONTTYPE)) {
//printf("rejecting %s\n", logFont->lfFaceName);
return 1;
}
// XXX ignore vertical fonts
if (logFont->lfFaceName[0] == '@') {
return 1;
}
// XXX make this smarter: don't add font to list if we already have a font
// with the same font signature -- erik
if (nsFontMetricsWin::gGlobalFontsCount == gGlobalFontsAlloc) {
int newSize = 2 * (gGlobalFontsAlloc ? gGlobalFontsAlloc : 1);
nsGlobalFont* newPointer = (nsGlobalFont*)
PR_Realloc(nsFontMetricsWin::gGlobalFonts, newSize*sizeof(nsGlobalFont));
if (newPointer) {
nsFontMetricsWin::gGlobalFonts = newPointer;
gGlobalFontsAlloc = newSize;
}
else {
return 0;
}
}
nsGlobalFont* font =
&nsFontMetricsWin::gGlobalFonts[nsFontMetricsWin::gGlobalFontsCount++];
PRUnichar name[LF_FACESIZE];
name[0] = 0;
MultiByteToWideChar(CP_ACP, 0, logFont->lfFaceName,
strlen(logFont->lfFaceName) + 1, name, sizeof(name)/sizeof(name[0]));
font->name = new nsString(name);
if (!font->name) {
nsFontMetricsWin::gGlobalFontsCount--;
return 0;
}
font->map = nsnull;
font->logFont = *logFont;
font->skip = 0;
return 1;
}
nsGlobalFont*
nsFontMetricsWin::InitializeGlobalFonts(HDC aDC)
{
static int gInitialized = 0;
if (!gInitialized) {
gInitialized = 1;
LOGFONT logFont;
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfFaceName[0] = 0;
logFont.lfPitchAndFamily = 0;
/*
* msdn.microsoft.com/library states that
* EnumFontFamiliesExW is only on NT/2000
*/
EnumFontFamiliesEx(aDC, &logFont, enumProc, nsnull, 0);
}
return gGlobalFonts;
}
int
nsFontMetricsWin::SameAsPreviousMap(int aIndex)
{
for (int i = 0; i < aIndex; i++) {
if (!gGlobalFonts[i].skip) {
if (gGlobalFonts[i].map == gGlobalFonts[aIndex].map) {
gGlobalFonts[aIndex].skip = 1;
return 1;
}
PRUint8* map1 = gGlobalFonts[i].map;
PRUint8* map2 = gGlobalFonts[aIndex].map;
int j;
for (j = 0; j < 8192; j++) {
if (map1[j] != map2[j]) {
break;
}
}
if (j == 8192) {
gGlobalFonts[aIndex].skip = 1;
return 1;
}
}
}
return 0;
}
nsFontWin*
nsFontMetricsWin::FindGlobalFont(HDC aDC, PRUnichar c)
{
if (!gGlobalFonts) {
if (!InitializeGlobalFonts(aDC)) {
return nsnull;
}
}
for (int i = 0; i < gGlobalFontsCount; i++) {
if (!gGlobalFonts[i].skip) {
if (!gGlobalFonts[i].map) {
HFONT font = ::CreateFontIndirect(&gGlobalFonts[i].logFont);
if (!font) {
continue;
}
HFONT oldFont = (HFONT) ::SelectObject(aDC, font);
gGlobalFonts[i].map = GetCMAP(aDC);
::SelectObject(aDC, oldFont);
::DeleteObject(font);
if (!gGlobalFonts[i].map) {
continue;
}
if (SameAsPreviousMap(i)) {
continue;
}
}
if (FONT_HAS_GLYPH(gGlobalFonts[i].map, c)) {
return LoadFont(aDC, gGlobalFonts[i].name);
}
}
}
return nsnull;
}
//------------ Font weight utilities -------------------
// XXX: Should not need to store all these in a hash table.
// We need to restructure the font management code so there is one
// global place to cache font info. As the code is right now, there
// are two separate places that font info is stored, in the gFamilyNames
// hash table and the global font array. There are also cases where the
// font info is not cached at all.
// I initially tried to add the font weight info to those two places, but
// it was messy. In addition I discovered another code path which does not
// cache anything.
// Entry for storing hash table. Store as a single
// entry rather than doing a separate allocation for the
// fontName and weight.
typedef struct nsFontWeightEntry
{
nsString mFontName;
PRUint16 mWeightTable; // Each bit indicates the availability of a font weight.
} nsFontWeightEntry;
static PLHashNumber
HashKeyFontWeight(const void* aFontWeightEntry)
{
const nsString* string = &((const nsFontWeightEntry*) aFontWeightEntry)->mFontName;
return (PLHashNumber)
nsCRT::HashValue(string->GetUnicode());
}
static PRIntn
CompareKeysFontWeight(const void* aFontWeightEntry1, const void* aFontWeightEntry2)
{
const nsString* str1 = &((const nsFontWeightEntry*) aFontWeightEntry1)->mFontName;
const nsString* str2 = &((const nsFontWeightEntry*) aFontWeightEntry2)->mFontName;
return nsCRT::strcmp(str1->GetUnicode(), str2->GetUnicode()) == 0;
}
// Store the font weight as a bit in the aWeightTable
void nsFontMetricsWin::SetFontWeight(PRInt32 aWeight, PRUint16* aWeightTable) {
NS_ASSERTION((aWeight >= 0) && (aWeight <= 9), "Invalid font weight passed");
*aWeightTable |= 1 << (aWeight - 1);
}
// Check to see if a font weight is available within the font weight table
PRBool nsFontMetricsWin::IsFontWeightAvailable(PRInt32 aWeight, PRUint16 aWeightTable) {
PRInt32 normalizedWeight = aWeight / 100;
NS_ASSERTION((aWeight >= 100) && (aWeight <= 900), "Invalid font weight passed");
PRUint16 bitwiseWeight = 1 << (normalizedWeight - 1);
if (bitwiseWeight & aWeightTable) {
return PR_TRUE;
}
else {
return PR_FALSE;
}
}
static int CALLBACK nsFontWeightCallback(const LOGFONT* logFont, const TEXTMETRIC * metrics,
DWORD fontType, LPARAM closure)
{
// printf("Name %s Log font sizes %d\n",logFont->lfFaceName,logFont->lfWeight);
if (!(fontType & TRUETYPE_FONTTYPE)) {
// printf("rejecting %s\n", logFont->lfFaceName);
return TRUE;
}
if (NULL != metrics) {
int pos = metrics->tmWeight / 100;
// Set a bit to indicate the font weight is available
nsFontMetricsWin::SetFontWeight(metrics->tmWeight / 100, (PRUint16*)closure);
}
return TRUE; // Keep looking for more weights.
}
PRUint16
nsFontMetricsWin::GetFontWeightTable(HDC aDC, nsString* aFontName) {
// Look for all of the weights for a given font.
LOGFONT logFont;
logFont.lfCharSet = DEFAULT_CHARSET;
aFontName->ToCString(logFont.lfFaceName, LF_FACESIZE);
logFont.lfPitchAndFamily = 0;
PRUint16 weights = 0;
::EnumFontFamiliesEx(aDC, &logFont, nsFontWeightCallback, (LPARAM)&weights, 0);
// printf("font weights for %s dec %d hex %x \n", logFont.lfFaceName, weights, weights);
return weights;
}
// Calculate the closest font weight. This is necessary because we need to
// control the mapping of logical font weight to available weight to handle both CSS2
// default algorithm + the case where a font weight is choosen which is not available then made
// bolder or lighter. (e.g. a font weight of 200 is choosen but not available
// on the system so a weight of 400 is used instead when mapping to a physical font.
// If the font is made bolder we need to know that a font weight of 400 was choosen, so
// we can select a font weight which is greater.
PRInt32
nsFontMetricsWin::GetClosestWeight(PRInt32 aWeight, PRUint16 aWeightTable)
{
// Algorithm used From CSS2 section 15.5.1 Mapping font weight values to font names
PRInt32 newWeight = aWeight;
// Check for exact match
if ((aWeight > 0) && (nsFontMetricsWin::IsFontWeightAvailable(aWeight, aWeightTable))) {
return aWeight;
}
// Find lighter and darker weights to be used later.
// First look for lighter
PRInt32 lighterWeight = 0;
PRInt32 proposedLighterWeight = PR_MAX(0, aWeight - 100);
PRBool done = PR_FALSE;
while ((PR_FALSE == done) && (proposedLighterWeight >= 100)) {
if (nsFontMetricsWin::IsFontWeightAvailable(proposedLighterWeight, aWeightTable)) {
lighterWeight = proposedLighterWeight;
done = PR_TRUE;
} else {
proposedLighterWeight-= 100;
}
}
// Now look for darker
PRInt32 darkerWeight = 0;
done = PR_FALSE;
PRInt32 proposedDarkerWeight = PR_MIN(aWeight + 100, 900);
while ((PR_FALSE == done) && (proposedDarkerWeight <= 900)) {
if (nsFontMetricsWin::IsFontWeightAvailable(proposedDarkerWeight, aWeightTable)) {
darkerWeight = proposedDarkerWeight;
done = PR_TRUE;
} else {
proposedDarkerWeight+= 100;
}
}
// From CSS2 section 15.5.1
// If '500' is unassigned, it will be
// assigned the same font as '400'.
// If any of '300', '200', or '100' remains unassigned, it is
// assigned to the next lighter assigned keyword, if any, or
// the next darker otherwise.
// What about if the desired weight is 500 and 400 is unassigned?.
// This is not inlcluded in the CSS spec so I'll treat it in a consistent
// manner with unassigned '300', '200' and '100'
if (aWeight <= 500) {
if (0 != lighterWeight) {
return lighterWeight;
}
else {
return darkerWeight;
}
} else {
// Automatically chose the bolder weight if the next lighter weight
// makes it normal. (i.e goes over the normal to bold threshold.)
// From CSS2 section 15.5.1
// if any of the values '600', '700', '800', or '900' remains unassigned,
// they are assigned to the same face as the next darker assigned keyword,
// if any, or the next lighter one otherwise.
if (0 != darkerWeight) {
return darkerWeight;
} else {
return lighterWeight;
}
}
return aWeight;
}
PRInt32
nsFontMetricsWin::GetBolderWeight(PRInt32 aWeight, PRInt32 aDistance, PRUint16 aWeightTable)
{
PRInt32 newWeight = aWeight;
PRInt32 proposedWeight = aWeight + 100; // Start 1 bolder than the current
for (PRInt32 j = 0; j < aDistance; j++) {
PRBool aFoundAWeight = PR_FALSE;
while ((proposedWeight <= NS_MAX_FONT_WEIGHT) && (PR_FALSE == aFoundAWeight)) {
if (nsFontMetricsWin::IsFontWeightAvailable(proposedWeight, aWeightTable)) {
//
newWeight = proposedWeight;
aFoundAWeight = PR_TRUE;
}
proposedWeight+=100;
}
}
return newWeight;
}
PRInt32
nsFontMetricsWin::GetLighterWeight(PRInt32 aWeight, PRInt32 aDistance, PRUint16 aWeightTable)
{
PRInt32 newWeight = aWeight;
PRInt32 proposedWeight = aWeight - 100; // Start 1 lighter than the current
for (PRInt32 j = 0; j < aDistance; j++) {
PRBool aFoundAWeight = PR_FALSE;
while ((proposedWeight >= NS_MIN_FONT_WEIGHT) && (PR_FALSE == aFoundAWeight)) {
if (nsFontMetricsWin::IsFontWeightAvailable(proposedWeight, aWeightTable)) {
//
newWeight = proposedWeight;
aFoundAWeight = PR_TRUE;
}
proposedWeight-=100;
}
}
return newWeight;
}
PRInt32
nsFontMetricsWin::GetFontWeight(PRInt32 aWeight, PRUint16 aWeightTable) {
// The remainder is used to determine whether to make
// the font lighter or bolder
PRInt32 remainder = aWeight % 100;
PRInt32 normalizedWeight = aWeight / 100;
PRInt32 selectedWeight = 0;
// No remainder, so get the closest weight
if (remainder == 0) {
selectedWeight = GetClosestWeight(aWeight, aWeightTable);
} else {
NS_ASSERTION((remainder < 10) || (remainder > 90), "Invalid bolder or lighter value");
if (remainder < 10) {
PRInt32 weight = GetClosestWeight(normalizedWeight * 100, aWeightTable);
selectedWeight = GetBolderWeight(weight, remainder, aWeightTable);
} else {
// Have to add back 1 for the lighter weight since aWeight really refers to the
// whole number. eq. 398 really means 2 lighter than font weight 400.
PRInt32 weight = GetClosestWeight((normalizedWeight + 1) * 100, aWeightTable);
selectedWeight = GetLighterWeight(weight, 100-remainder, aWeightTable);
}
}
// printf("XXX Input weight %d output weight %d weight table hex %x\n", aWeight, selectedWeight, aWeightTable);
return selectedWeight;
}
PRUint16
nsFontMetricsWin::LookForFontWeightTable(HDC aDC, nsString* aName)
{
static int gInitializedFontWeights = 0;
// Initialize the font weight table if need be.
if (!gInitializedFontWeights) {
gInitializedFontWeights = 1;
gFontWeights = PL_NewHashTable(0, HashKeyFontWeight, CompareKeysFontWeight, nsnull, nsnull,
nsnull);
if (!gFontWeights) {
return 0;
}
}
// Use lower case name for hash table searches. This eliminates
// keeping multiple font weights entries when the font name varies
// only by case.
nsAutoString low = *aName;
low.ToLowerCase();
// See if the font weight has already been computed.
nsFontWeightEntry searchEntry;
searchEntry.mFontName = low;
searchEntry.mWeightTable = 0;
nsFontWeightEntry* weightEntry = (nsFontWeightEntry*)PL_HashTableLookup(gFontWeights, &searchEntry);
if (nsnull != weightEntry) {
// printf("Re-use weight entry\n");
return weightEntry->mWeightTable;
}
// Hasn't been computed, so need to compute and store it.
PRUint16 weightTable = GetFontWeightTable(aDC, aName);
// printf("Compute font weight %d\n", weightTable);
// Store it in font weight HashTable.
nsFontWeightEntry* fontWeightEntry = new nsFontWeightEntry;
fontWeightEntry->mFontName = low;
fontWeightEntry->mWeightTable = weightTable;
PL_HashTableAdd(gFontWeights, fontWeightEntry, fontWeightEntry);
return weightTable;
}
// ------------ End of font weight utilities
typedef struct nsFontFamilyName
{
char* mName;
char* mWinName;
} nsFontFamilyName;
static nsFontFamilyName gFamilyNameTable[] =
{
{ "times", "Times New Roman" },
{ "times roman", "Times New Roman" },
{ "times new roman", "Times New Roman" },
{ "arial", "Arial" },
{ "helvetica", "Arial" },
{ "courier", "Courier New" },
{ "courier new", "Courier New" },
{ "serif", "Times New Roman" },
{ "sans-serif", "Arial" },
{ "fantasy", "Arial" },
{ "cursive", "Arial" },
{ "monospace", "Courier New" },
{ "-moz-fixed", "Courier New" },
{ nsnull, nsnull }
};
PLHashTable*
nsFontMetricsWin::InitializeFamilyNames(void)
{
static int gInitialized = 0;
if (!gInitialized) {
gInitialized = 1;
gFamilyNames = PL_NewHashTable(0, HashKey, CompareKeys, nsnull, nsnull,
nsnull);
if (!gFamilyNames) {
return nsnull;
}
nsFontFamilyName* f = gFamilyNameTable;
while (f->mName) {
nsString* name = new nsString(f->mName);
nsString* winName = new nsString(f->mWinName);
if (name && winName) {
PL_HashTableAdd(gFamilyNames, name, (void*) winName);
}
f++;
}
}
return gFamilyNames;
}
nsFontWin*
nsFontMetricsWin::FindLocalFont(HDC aDC, PRUnichar aChar)
{
if (!gFamilyNames) {
if (!InitializeFamilyNames()) {
return nsnull;
}
}
while (mFontsIndex < mFontsCount) {
nsString* name = &mFonts[mFontsIndex++];
nsString* low = new nsString(*name);
if (low) {
low->ToLowerCase();
nsString* winName = (nsString*) PL_HashTableLookup(gFamilyNames, low);
delete low;
if (!winName) {
winName = name;
}
nsFontWin* font = LoadFont(aDC, winName);
if (font && FONT_HAS_GLYPH(font->map, aChar)) {
return font;
}
}
}
return nsnull;
}
nsFontWin*
nsFontMetricsWin::FindFont(HDC aDC, PRUnichar aChar)
{
nsFontWin* font = FindLocalFont(aDC, aChar);
if (!font) {
font = FindGlobalFont(aDC, aChar);
}
return font;
}
static PRBool
FontEnumCallback(const nsString& aFamily, PRBool aGeneric, void *aData)
{
nsFontMetricsWin* metrics = (nsFontMetricsWin*) aData;
if (metrics->mFontsCount == metrics->mFontsAlloc) {
int newSize = 2 * (metrics->mFontsAlloc ? metrics->mFontsAlloc : 1);
nsString* newPointer = new nsString[newSize];
if (newPointer) {
for (int i = metrics->mFontsCount - 1; i >= 0; i--) {
newPointer[i].SetString(metrics->mFonts[i].GetUnicode());
}
delete [] metrics->mFonts;
metrics->mFonts = newPointer;
metrics->mFontsAlloc = newSize;
}
else {
return PR_FALSE; // stop
}
}
metrics->mFonts[metrics->mFontsCount++].SetString(aFamily.GetUnicode());
if (aGeneric) {
return PR_FALSE; // stop
}
return PR_TRUE; // don't stop
}
/** ---------------------------------------------------
* See documentation in nsFontMetricsWin.h
* @update 05/28/99 dwc
*/
void
nsFontMetricsWin::RealizeFont()
{
HWND win = NULL;
HDC dc = NULL;
HDC dc1 = NULL;
if (NULL != mDeviceContext->mDC){
// XXX - DC If we are printing, we need to get the printer HDC and a screen HDC
// The screen HDC is because there seems to be a bug or requirment that the
// GetFontData() method call have a screen HDC, some printers HDC's return nothing
// thats will give us bad font data, and break us.
dc = mDeviceContext->mDC;
win = (HWND)mDeviceContext->mWidget;
dc1 = ::GetDC(win);
} else {
// Find font metrics and character widths
win = (HWND)mDeviceContext->mWidget;
dc = ::GetDC(win);
dc1 = dc;
}
mFont->EnumerateFamilies(FontEnumCallback, this);
nsFontWin* font = FindFont(dc1, 'a');
if (!font) {
return;
}
mFontHandle = font->font;
HFONT oldfont = (HFONT)::SelectObject(dc, (HGDIOBJ) mFontHandle);
// Get font metrics
float dev2app;
mDeviceContext->GetDevUnitsToAppUnits(dev2app);
OUTLINETEXTMETRIC oMetrics;
TEXTMETRIC& metrics = oMetrics.otmTextMetrics;
nscoord onePixel = NSToCoordRound(1 * dev2app);
if (0 < ::GetOutlineTextMetrics(dc, sizeof(oMetrics), &oMetrics)) {
// mXHeight = NSToCoordRound(oMetrics.otmsXHeight * dev2app); XXX not really supported on windows
mXHeight = NSToCoordRound((float)metrics.tmAscent * dev2app * 0.50f); // 50% of ascent, best guess for true type
mSuperscriptOffset = NSToCoordRound(oMetrics.otmptSuperscriptOffset.y * dev2app);
mSubscriptOffset = NSToCoordRound(oMetrics.otmptSubscriptOffset.y * dev2app);
mStrikeoutSize = MAX(onePixel, NSToCoordRound(oMetrics.otmsStrikeoutSize * dev2app));
mStrikeoutOffset = NSToCoordRound(oMetrics.otmsStrikeoutPosition * dev2app);
mUnderlineSize = MAX(onePixel, NSToCoordRound(oMetrics.otmsUnderscoreSize * dev2app));
mUnderlineOffset = NSToCoordRound(oMetrics.otmsUnderscorePosition * dev2app);
}
else {
// Make a best-effort guess at extended metrics
// this is based on general typographic guidelines
::GetTextMetrics(dc, &metrics);
mXHeight = NSToCoordRound((float)metrics.tmAscent * dev2app * 0.56f); // 56% of ascent, best guess for non-true type
mSuperscriptOffset = mXHeight; // XXX temporary code!
mSubscriptOffset = mXHeight; // XXX temporary code!
mStrikeoutSize = onePixel; // XXX this is a guess
mStrikeoutOffset = NSToCoordRound(mXHeight / 2.0f); // 50% of xHeight
mUnderlineSize = onePixel; // XXX this is a guess
mUnderlineOffset = -NSToCoordRound((float)metrics.tmDescent * dev2app * 0.30f); // 30% of descent
}
mHeight = NSToCoordRound(metrics.tmHeight * dev2app);
mAscent = NSToCoordRound(metrics.tmAscent * dev2app);
mDescent = NSToCoordRound(metrics.tmDescent * dev2app);
mLeading = NSToCoordRound(metrics.tmInternalLeading * dev2app);
mMaxAscent = NSToCoordRound(metrics.tmAscent * dev2app);
mMaxDescent = NSToCoordRound(metrics.tmDescent * dev2app);
mMaxAdvance = NSToCoordRound(metrics.tmMaxCharWidth * dev2app);
// Cache the width of a single space.
SIZE size;
::GetTextExtentPoint32(dc, " ", 1, &size);
mSpaceWidth = NSToCoordRound(size.cx * dev2app);
::SelectObject(dc, oldfont);
if (NULL == mDeviceContext->mDC){
::ReleaseDC(win, dc);
} else {
::ReleaseDC(win,dc1);
}
}
NS_IMETHODIMP
nsFontMetricsWin :: GetXHeight(nscoord& aResult)
{
aResult = mXHeight;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetSuperscriptOffset(nscoord& aResult)
{
aResult = mSuperscriptOffset;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetSubscriptOffset(nscoord& aResult)
{
aResult = mSubscriptOffset;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetStrikeout(nscoord& aOffset, nscoord& aSize)
{
aOffset = mStrikeoutOffset;
aSize = mStrikeoutSize;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetUnderline(nscoord& aOffset, nscoord& aSize)
{
aOffset = mUnderlineOffset;
aSize = mUnderlineSize;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetHeight(nscoord &aHeight)
{
aHeight = mHeight;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetLeading(nscoord &aLeading)
{
aLeading = mLeading;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetMaxAscent(nscoord &aAscent)
{
aAscent = mMaxAscent;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetMaxDescent(nscoord &aDescent)
{
aDescent = mMaxDescent;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetMaxAdvance(nscoord &aAdvance)
{
aAdvance = mMaxAdvance;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin :: GetFont(const nsFont *&aFont)
{
aFont = mFont;
return NS_OK;
}
NS_IMETHODIMP
nsFontMetricsWin::GetFontHandle(nsFontHandle &aHandle)
{
aHandle = mFontHandle;
return NS_OK;
}
// The following is a workaround for a Japanese Windows 95 problem.
PRUint8 bitToCharSet[64] =
{
/*00*/ ANSI_CHARSET,
/*01*/ EASTEUROPE_CHARSET,
/*02*/ RUSSIAN_CHARSET,
/*03*/ GREEK_CHARSET,
/*04*/ TURKISH_CHARSET,
/*05*/ HEBREW_CHARSET,
/*06*/ ARABIC_CHARSET,
/*07*/ BALTIC_CHARSET,
/*08*/ DEFAULT_CHARSET,
/*09*/ DEFAULT_CHARSET,
/*10*/ DEFAULT_CHARSET,
/*11*/ DEFAULT_CHARSET,
/*12*/ DEFAULT_CHARSET,
/*13*/ DEFAULT_CHARSET,
/*14*/ DEFAULT_CHARSET,
/*15*/ DEFAULT_CHARSET,
/*16*/ THAI_CHARSET,
/*17*/ SHIFTJIS_CHARSET,
/*18*/ GB2312_CHARSET,
/*19*/ HANGEUL_CHARSET,
/*20*/ CHINESEBIG5_CHARSET,
/*21*/ JOHAB_CHARSET,
/*22*/ DEFAULT_CHARSET,
/*23*/ DEFAULT_CHARSET,
/*24*/ DEFAULT_CHARSET,
/*25*/ DEFAULT_CHARSET,
/*26*/ DEFAULT_CHARSET,
/*27*/ DEFAULT_CHARSET,
/*28*/ DEFAULT_CHARSET,
/*29*/ DEFAULT_CHARSET,
/*30*/ DEFAULT_CHARSET,
/*31*/ DEFAULT_CHARSET,
/*32*/ DEFAULT_CHARSET,
/*33*/ DEFAULT_CHARSET,
/*34*/ DEFAULT_CHARSET,
/*35*/ DEFAULT_CHARSET,
/*36*/ DEFAULT_CHARSET,
/*37*/ DEFAULT_CHARSET,
/*38*/ DEFAULT_CHARSET,
/*39*/ DEFAULT_CHARSET,
/*40*/ DEFAULT_CHARSET,
/*41*/ DEFAULT_CHARSET,
/*42*/ DEFAULT_CHARSET,
/*43*/ DEFAULT_CHARSET,
/*44*/ DEFAULT_CHARSET,
/*45*/ DEFAULT_CHARSET,
/*46*/ DEFAULT_CHARSET,
/*47*/ DEFAULT_CHARSET,
/*48*/ DEFAULT_CHARSET,
/*49*/ DEFAULT_CHARSET,
/*50*/ DEFAULT_CHARSET,
/*51*/ DEFAULT_CHARSET,
/*52*/ DEFAULT_CHARSET,
/*53*/ DEFAULT_CHARSET,
/*54*/ DEFAULT_CHARSET,
/*55*/ DEFAULT_CHARSET,
/*56*/ DEFAULT_CHARSET,
/*57*/ DEFAULT_CHARSET,
/*58*/ DEFAULT_CHARSET,
/*59*/ DEFAULT_CHARSET,
/*60*/ DEFAULT_CHARSET,
/*61*/ DEFAULT_CHARSET,
/*62*/ DEFAULT_CHARSET,
/*63*/ DEFAULT_CHARSET
};
enum nsCharSet
{
eCharSet_DEFAULT = 0,
eCharSet_ANSI,
eCharSet_EASTEUROPE,
eCharSet_RUSSIAN,
eCharSet_GREEK,
eCharSet_TURKISH,
eCharSet_HEBREW,
eCharSet_ARABIC,
eCharSet_BALTIC,
eCharSet_THAI,
eCharSet_SHIFTJIS,
eCharSet_GB2312,
eCharSet_HANGEUL,
eCharSet_CHINESEBIG5,
eCharSet_JOHAB,
eCharSet_COUNT
};
static nsCharSet gCharSetToIndex[256] =
{
/* 000 */ eCharSet_ANSI,
/* 001 */ eCharSet_DEFAULT,
/* 002 */ eCharSet_DEFAULT, // SYMBOL
/* 003 */ eCharSet_DEFAULT,
/* 004 */ eCharSet_DEFAULT,
/* 005 */ eCharSet_DEFAULT,
/* 006 */ eCharSet_DEFAULT,
/* 007 */ eCharSet_DEFAULT,
/* 008 */ eCharSet_DEFAULT,
/* 009 */ eCharSet_DEFAULT,
/* 010 */ eCharSet_DEFAULT,
/* 011 */ eCharSet_DEFAULT,
/* 012 */ eCharSet_DEFAULT,
/* 013 */ eCharSet_DEFAULT,
/* 014 */ eCharSet_DEFAULT,
/* 015 */ eCharSet_DEFAULT,
/* 016 */ eCharSet_DEFAULT,
/* 017 */ eCharSet_DEFAULT,
/* 018 */ eCharSet_DEFAULT,
/* 019 */ eCharSet_DEFAULT,
/* 020 */ eCharSet_DEFAULT,
/* 021 */ eCharSet_DEFAULT,
/* 022 */ eCharSet_DEFAULT,
/* 023 */ eCharSet_DEFAULT,
/* 024 */ eCharSet_DEFAULT,
/* 025 */ eCharSet_DEFAULT,
/* 026 */ eCharSet_DEFAULT,
/* 027 */ eCharSet_DEFAULT,
/* 028 */ eCharSet_DEFAULT,
/* 029 */ eCharSet_DEFAULT,
/* 030 */ eCharSet_DEFAULT,
/* 031 */ eCharSet_DEFAULT,
/* 032 */ eCharSet_DEFAULT,
/* 033 */ eCharSet_DEFAULT,
/* 034 */ eCharSet_DEFAULT,
/* 035 */ eCharSet_DEFAULT,
/* 036 */ eCharSet_DEFAULT,
/* 037 */ eCharSet_DEFAULT,
/* 038 */ eCharSet_DEFAULT,
/* 039 */ eCharSet_DEFAULT,
/* 040 */ eCharSet_DEFAULT,
/* 041 */ eCharSet_DEFAULT,
/* 042 */ eCharSet_DEFAULT,
/* 043 */ eCharSet_DEFAULT,
/* 044 */ eCharSet_DEFAULT,
/* 045 */ eCharSet_DEFAULT,
/* 046 */ eCharSet_DEFAULT,
/* 047 */ eCharSet_DEFAULT,
/* 048 */ eCharSet_DEFAULT,
/* 049 */ eCharSet_DEFAULT,
/* 050 */ eCharSet_DEFAULT,
/* 051 */ eCharSet_DEFAULT,
/* 052 */ eCharSet_DEFAULT,
/* 053 */ eCharSet_DEFAULT,
/* 054 */ eCharSet_DEFAULT,
/* 055 */ eCharSet_DEFAULT,
/* 056 */ eCharSet_DEFAULT,
/* 057 */ eCharSet_DEFAULT,
/* 058 */ eCharSet_DEFAULT,
/* 059 */ eCharSet_DEFAULT,
/* 060 */ eCharSet_DEFAULT,
/* 061 */ eCharSet_DEFAULT,
/* 062 */ eCharSet_DEFAULT,
/* 063 */ eCharSet_DEFAULT,
/* 064 */ eCharSet_DEFAULT,
/* 065 */ eCharSet_DEFAULT,
/* 066 */ eCharSet_DEFAULT,
/* 067 */ eCharSet_DEFAULT,
/* 068 */ eCharSet_DEFAULT,
/* 069 */ eCharSet_DEFAULT,
/* 070 */ eCharSet_DEFAULT,
/* 071 */ eCharSet_DEFAULT,
/* 072 */ eCharSet_DEFAULT,
/* 073 */ eCharSet_DEFAULT,
/* 074 */ eCharSet_DEFAULT,
/* 075 */ eCharSet_DEFAULT,
/* 076 */ eCharSet_DEFAULT,
/* 077 */ eCharSet_DEFAULT, // MAC
/* 078 */ eCharSet_DEFAULT,
/* 079 */ eCharSet_DEFAULT,
/* 080 */ eCharSet_DEFAULT,
/* 081 */ eCharSet_DEFAULT,
/* 082 */ eCharSet_DEFAULT,
/* 083 */ eCharSet_DEFAULT,
/* 084 */ eCharSet_DEFAULT,
/* 085 */ eCharSet_DEFAULT,
/* 086 */ eCharSet_DEFAULT,
/* 087 */ eCharSet_DEFAULT,
/* 088 */ eCharSet_DEFAULT,
/* 089 */ eCharSet_DEFAULT,
/* 090 */ eCharSet_DEFAULT,
/* 091 */ eCharSet_DEFAULT,
/* 092 */ eCharSet_DEFAULT,
/* 093 */ eCharSet_DEFAULT,
/* 094 */ eCharSet_DEFAULT,
/* 095 */ eCharSet_DEFAULT,
/* 096 */ eCharSet_DEFAULT,
/* 097 */ eCharSet_DEFAULT,
/* 098 */ eCharSet_DEFAULT,
/* 099 */ eCharSet_DEFAULT,
/* 100 */ eCharSet_DEFAULT,
/* 101 */ eCharSet_DEFAULT,
/* 102 */ eCharSet_DEFAULT,
/* 103 */ eCharSet_DEFAULT,
/* 104 */ eCharSet_DEFAULT,
/* 105 */ eCharSet_DEFAULT,
/* 106 */ eCharSet_DEFAULT,
/* 107 */ eCharSet_DEFAULT,
/* 108 */ eCharSet_DEFAULT,
/* 109 */ eCharSet_DEFAULT,
/* 110 */ eCharSet_DEFAULT,
/* 111 */ eCharSet_DEFAULT,
/* 112 */ eCharSet_DEFAULT,
/* 113 */ eCharSet_DEFAULT,
/* 114 */ eCharSet_DEFAULT,
/* 115 */ eCharSet_DEFAULT,
/* 116 */ eCharSet_DEFAULT,
/* 117 */ eCharSet_DEFAULT,
/* 118 */ eCharSet_DEFAULT,
/* 119 */ eCharSet_DEFAULT,
/* 120 */ eCharSet_DEFAULT,
/* 121 */ eCharSet_DEFAULT,
/* 122 */ eCharSet_DEFAULT,
/* 123 */ eCharSet_DEFAULT,
/* 124 */ eCharSet_DEFAULT,
/* 125 */ eCharSet_DEFAULT,
/* 126 */ eCharSet_DEFAULT,
/* 127 */ eCharSet_DEFAULT,
/* 128 */ eCharSet_SHIFTJIS,
/* 129 */ eCharSet_HANGEUL,
/* 130 */ eCharSet_JOHAB,
/* 131 */ eCharSet_DEFAULT,
/* 132 */ eCharSet_DEFAULT,
/* 133 */ eCharSet_DEFAULT,
/* 134 */ eCharSet_GB2312,
/* 135 */ eCharSet_DEFAULT,
/* 136 */ eCharSet_CHINESEBIG5,
/* 137 */ eCharSet_DEFAULT,
/* 138 */ eCharSet_DEFAULT,
/* 139 */ eCharSet_DEFAULT,
/* 140 */ eCharSet_DEFAULT,
/* 141 */ eCharSet_DEFAULT,
/* 142 */ eCharSet_DEFAULT,
/* 143 */ eCharSet_DEFAULT,
/* 144 */ eCharSet_DEFAULT,
/* 145 */ eCharSet_DEFAULT,
/* 146 */ eCharSet_DEFAULT,
/* 147 */ eCharSet_DEFAULT,
/* 148 */ eCharSet_DEFAULT,
/* 149 */ eCharSet_DEFAULT,
/* 150 */ eCharSet_DEFAULT,
/* 151 */ eCharSet_DEFAULT,
/* 152 */ eCharSet_DEFAULT,
/* 153 */ eCharSet_DEFAULT,
/* 154 */ eCharSet_DEFAULT,
/* 155 */ eCharSet_DEFAULT,
/* 156 */ eCharSet_DEFAULT,
/* 157 */ eCharSet_DEFAULT,
/* 158 */ eCharSet_DEFAULT,
/* 159 */ eCharSet_DEFAULT,
/* 160 */ eCharSet_DEFAULT,
/* 161 */ eCharSet_GREEK,
/* 162 */ eCharSet_TURKISH,
/* 163 */ eCharSet_DEFAULT, // VIETNAMESE
/* 164 */ eCharSet_DEFAULT,
/* 165 */ eCharSet_DEFAULT,
/* 166 */ eCharSet_DEFAULT,
/* 167 */ eCharSet_DEFAULT,
/* 168 */ eCharSet_DEFAULT,
/* 169 */ eCharSet_DEFAULT,
/* 170 */ eCharSet_DEFAULT,
/* 171 */ eCharSet_DEFAULT,
/* 172 */ eCharSet_DEFAULT,
/* 173 */ eCharSet_DEFAULT,
/* 174 */ eCharSet_DEFAULT,
/* 175 */ eCharSet_DEFAULT,
/* 176 */ eCharSet_DEFAULT,
/* 177 */ eCharSet_HEBREW,
/* 178 */ eCharSet_ARABIC,
/* 179 */ eCharSet_DEFAULT,
/* 180 */ eCharSet_DEFAULT,
/* 181 */ eCharSet_DEFAULT,
/* 182 */ eCharSet_DEFAULT,
/* 183 */ eCharSet_DEFAULT,
/* 184 */ eCharSet_DEFAULT,
/* 185 */ eCharSet_DEFAULT,
/* 186 */ eCharSet_BALTIC,
/* 187 */ eCharSet_DEFAULT,
/* 188 */ eCharSet_DEFAULT,
/* 189 */ eCharSet_DEFAULT,
/* 190 */ eCharSet_DEFAULT,
/* 191 */ eCharSet_DEFAULT,
/* 192 */ eCharSet_DEFAULT,
/* 193 */ eCharSet_DEFAULT,
/* 194 */ eCharSet_DEFAULT,
/* 195 */ eCharSet_DEFAULT,
/* 196 */ eCharSet_DEFAULT,
/* 197 */ eCharSet_DEFAULT,
/* 198 */ eCharSet_DEFAULT,
/* 199 */ eCharSet_DEFAULT,
/* 200 */ eCharSet_DEFAULT,
/* 201 */ eCharSet_DEFAULT,
/* 202 */ eCharSet_DEFAULT,
/* 203 */ eCharSet_DEFAULT,
/* 204 */ eCharSet_RUSSIAN,
/* 205 */ eCharSet_DEFAULT,
/* 206 */ eCharSet_DEFAULT,
/* 207 */ eCharSet_DEFAULT,
/* 208 */ eCharSet_DEFAULT,
/* 209 */ eCharSet_DEFAULT,
/* 210 */ eCharSet_DEFAULT,
/* 211 */ eCharSet_DEFAULT,
/* 212 */ eCharSet_DEFAULT,
/* 213 */ eCharSet_DEFAULT,
/* 214 */ eCharSet_DEFAULT,
/* 215 */ eCharSet_DEFAULT,
/* 216 */ eCharSet_DEFAULT,
/* 217 */ eCharSet_DEFAULT,
/* 218 */ eCharSet_DEFAULT,
/* 219 */ eCharSet_DEFAULT,
/* 220 */ eCharSet_DEFAULT,
/* 221 */ eCharSet_DEFAULT,
/* 222 */ eCharSet_THAI,
/* 223 */ eCharSet_DEFAULT,
/* 224 */ eCharSet_DEFAULT,
/* 225 */ eCharSet_DEFAULT,
/* 226 */ eCharSet_DEFAULT,
/* 227 */ eCharSet_DEFAULT,
/* 228 */ eCharSet_DEFAULT,
/* 229 */ eCharSet_DEFAULT,
/* 230 */ eCharSet_DEFAULT,
/* 231 */ eCharSet_DEFAULT,
/* 232 */ eCharSet_DEFAULT,
/* 233 */ eCharSet_DEFAULT,
/* 234 */ eCharSet_DEFAULT,
/* 235 */ eCharSet_DEFAULT,
/* 236 */ eCharSet_DEFAULT,
/* 237 */ eCharSet_DEFAULT,
/* 238 */ eCharSet_EASTEUROPE,
/* 239 */ eCharSet_DEFAULT,
/* 240 */ eCharSet_DEFAULT,
/* 241 */ eCharSet_DEFAULT,
/* 242 */ eCharSet_DEFAULT,
/* 243 */ eCharSet_DEFAULT,
/* 244 */ eCharSet_DEFAULT,
/* 245 */ eCharSet_DEFAULT,
/* 246 */ eCharSet_DEFAULT,
/* 247 */ eCharSet_DEFAULT,
/* 248 */ eCharSet_DEFAULT,
/* 249 */ eCharSet_DEFAULT,
/* 250 */ eCharSet_DEFAULT,
/* 251 */ eCharSet_DEFAULT,
/* 252 */ eCharSet_DEFAULT,
/* 253 */ eCharSet_DEFAULT,
/* 254 */ eCharSet_DEFAULT,
/* 255 */ eCharSet_DEFAULT // OEM
};
typedef struct nsCharSetInfo nsCharSetInfo;
struct nsCharSetInfo
{
char* mName;
PRUint16 mCodePage;
void (*GenerateMap)(nsCharSetInfo* aSelf);
PRUint8* mMap;
};
static void
GenerateDefault(nsCharSetInfo* aSelf)
{
printf("%s defaulted\n", aSelf->mName);
PRUint8* map = aSelf->mMap;
for (int i = 0; i < 8192; i++) {
map[i] = 0xFF;
}
}
static void
GenerateSingleByte(nsCharSetInfo* aSelf)
{
PRUint8 mb[256];
PRUint16 wc[256];
int i;
for (i = 0; i < 256; i++) {
mb[i] = i;
}
int len = MultiByteToWideChar(aSelf->mCodePage, 0, (char*) mb, 256, wc, 256);
if (len != 256) {
printf("%s: MultiByteToWideChar returned %d\n", aSelf->mName, len);
}
PRUint8* map = aSelf->mMap;
for (i = 0; i < 256; i++) {
ADD_GLYPH(map, wc[i]);
}
}
static void
GenerateMultiByte(nsCharSetInfo* aSelf)
{
PRUint8* map = aSelf->mMap;
for (PRUint16 c = 0; c < 0xFFFF; c++) {
BOOL defaulted = FALSE;
WideCharToMultiByte(aSelf->mCodePage, 0, &c, 1, nsnull, 0, nsnull,
&defaulted);
if (!defaulted) {
ADD_GLYPH(map, c);
}
}
}
static nsCharSetInfo gCharSetInfo[eCharSet_COUNT] =
{
{ "DEFAULT", 0, GenerateDefault },
{ "ANSI", 1252, GenerateSingleByte },
{ "EASTEUROPE", 1250, GenerateSingleByte },
{ "RUSSIAN", 1251, GenerateSingleByte },
{ "GREEK", 1253, GenerateSingleByte },
{ "TURKISH", 1254, GenerateSingleByte },
{ "HEBREW", 1255, GenerateSingleByte },
{ "ARABIC", 1256, GenerateSingleByte },
{ "BALTIC", 1257, GenerateSingleByte },
{ "THAI", 874, GenerateSingleByte },
{ "SHIFTJIS", 932, GenerateMultiByte },
{ "GB2312", 936, GenerateMultiByte },
{ "HANGEUL", 949, GenerateMultiByte },
{ "CHINESEBIG5", 950, GenerateMultiByte },
{ "JOHAB", 1361, GenerateMultiByte }
};
static int
HaveConverterFor(PRUint8 aCharSet)
{
PRUint16 wc = 'a';
char mb[8];
if (WideCharToMultiByte(gCharSetInfo[gCharSetToIndex[aCharSet]].mCodePage, 0,
&wc, 1, mb, sizeof(mb), nsnull, nsnull)) {
return 1;
}
// remove from table, since we can't support it anyway
for (int i = 0; i < sizeof(bitToCharSet); i++) {
if (bitToCharSet[i] == aCharSet) {
bitToCharSet[i] = DEFAULT_CHARSET;
}
}
return 0;
}
int
nsFontWinA::GetSubsets(HDC aDC)
{
FONTSIGNATURE signature;
if (::GetTextCharsetInfo(aDC, &signature, 0) == DEFAULT_CHARSET) {
return 0;
}
int dword;
DWORD* array = signature.fsCsb;
mSubsetsCount = 0;
int i = 0;
for (dword = 0; dword < 2; dword++) {
for (int bit = 0; bit < sizeof(DWORD) * 8; bit++) {
if ((array[dword] >> bit) & 1) {
PRUint8 charSet = bitToCharSet[i];
#ifdef DEBUG_FONT_SIGNATURE
printf(" %02d %s\n", i, gCharSetInfo[gCharSetToIndex[charSet]].mName);
#endif
if (charSet != DEFAULT_CHARSET) {
if (HaveConverterFor(charSet)) {
mSubsetsCount++;
}
}
}
i++;
}
}
mSubsets = (nsFontSubset*) PR_Calloc(mSubsetsCount, sizeof(nsFontSubset));
if (!mSubsets) {
mSubsetsCount = 0;
return 0;
}
i = 0;
int j = 0;
for (dword = 0; dword < 2; dword++) {
for (int bit = 0; bit < sizeof(DWORD) * 8; bit++) {
if ((array[dword] >> bit) & 1) {
PRUint8 charSet = bitToCharSet[i];
if (charSet != DEFAULT_CHARSET) {
if (HaveConverterFor(charSet)) {
mSubsets[j].mCharSet = charSet;
j++;
}
}
}
i++;
}
}
return 1;
}
static void
FreeFont(nsFontWinA* aFont)
{
nsFontSubset* subset = aFont->mSubsets;
nsFontSubset* endSubsets = &(aFont->mSubsets[aFont->mSubsetsCount]);
while (subset < endSubsets) {
if (subset->mFont) {
::DeleteObject(subset->mFont);
}
if (subset->mMap) {
PR_Free(subset->mMap);
}
subset++;
}
PR_Free(aFont->mSubsets);
if (aFont->mFont) {
::DeleteObject(aFont->mFont);
}
delete aFont;
}
nsFontMetricsWinA::~nsFontMetricsWinA()
{
if (nsnull != mFont) {
delete mFont;
mFont = nsnull;
}
mFontHandle = nsnull; // released below
if (mFonts) {
delete [] mFonts;
mFonts = nsnull;
}
if (mLoadedFonts) {
nsFontWinA** font = (nsFontWinA**) mLoadedFonts;
nsFontWinA** end = (nsFontWinA**) &mLoadedFonts[mLoadedFontsCount];
while (font < end) {
FreeFont(*font);
font++;
}
PR_Free(mLoadedFonts);
mLoadedFonts = nsnull;
}
mDeviceContext = nsnull;
}
nsFontWin*
nsFontMetricsWinA::LoadFont(HDC aDC, nsString* aName)
{
LOGFONT logFont;
PRUint16 weightTable = LookForFontWeightTable(aDC, aName);
PRInt32 weight = GetFontWeight(mFont->weight, weightTable);
FillLogFont(&logFont, weight);
// XXX need to preserve Unicode chars in face name (use LOGFONTW) -- erik
aName->ToCString(logFont.lfFaceName, LF_FACESIZE);
HFONT hfont = ::CreateFontIndirect(&logFont);
if (hfont) {
if (mLoadedFontsCount == mLoadedFontsAlloc) {
int newSize = 2 * (mLoadedFontsAlloc ? mLoadedFontsAlloc : 1);
nsFontWinA** newPointer = (nsFontWinA**) PR_Realloc(mLoadedFonts,
newSize * sizeof(nsFontWinA*));
if (newPointer) {
mLoadedFonts = (nsFontWin**) newPointer;
mLoadedFontsAlloc = newSize;
}
else {
::DeleteObject(hfont);
return nsnull;
}
}
nsFontWinA* font = new nsFontWinA;
if (!font) {
::DeleteObject(hfont);
return nsnull;
}
mLoadedFonts[mLoadedFontsCount++] = (nsFontWin*) font;
HFONT oldFont = (HFONT) ::SelectObject(aDC, (HGDIOBJ) hfont);
font->mFont = hfont;
font->mLogFont = logFont;
#ifdef DEBUG_FONT_SIGNATURE
printf("%s\n", logFont.lfFaceName);
#endif
if (!font->GetSubsets(aDC)) {
mLoadedFontsCount--;
::SelectObject(aDC, (HGDIOBJ) oldFont);
::DeleteObject(hfont);
return nsnull;
}
font->mMap = GetCMAP(aDC);
if (!font->mMap) {
mLoadedFontsCount--;
::SelectObject(aDC, (HGDIOBJ) oldFont);
::DeleteObject(hfont);
return nsnull;
}
::SelectObject(aDC, (HGDIOBJ) oldFont);
return (nsFontWin*) font;
}
return nsnull;
}
int
nsFontSubset::Load(nsFontWinA* aFont)
{
LOGFONT* logFont = &aFont->mLogFont;
logFont->lfCharSet = mCharSet;
HFONT hfont = ::CreateFontIndirect(logFont);
if (hfont) {
int i = gCharSetToIndex[mCharSet];
PRUint8* charSetMap = gCharSetInfo[i].mMap;
if (!charSetMap) {
charSetMap = (PRUint8*) PR_Calloc(8192, 1);
if (charSetMap) {
gCharSetInfo[i].mMap = charSetMap;
gCharSetInfo[i].GenerateMap(&gCharSetInfo[i]);
}
else {
::DeleteObject(hfont);
return 0;
}
}
mMap = (PRUint8*) PR_Malloc(8192);
if (!mMap) {
::DeleteObject(hfont);
return 0;
}
PRUint8* fontMap = aFont->mMap;
for (int j = 0; j < 8192; j++) {
mMap[j] = (charSetMap[j] & fontMap[j]);
}
mCodePage = gCharSetInfo[i].mCodePage;
mFont = hfont;
return 1;
}
return 0;
}
nsFontWin*
nsFontMetricsWinA::FindLocalFont(HDC aDC, PRUnichar aChar)
{
if (!gFamilyNames) {
if (!InitializeFamilyNames()) {
return nsnull;
}
}
while (mFontsIndex < mFontsCount) {
nsString* name = &mFonts[mFontsIndex++];
nsString* low = new nsString(*name);
if (low) {
low->ToLowerCase();
nsString* winName = (nsString*) PL_HashTableLookup(gFamilyNames, low);
delete low;
if (!winName) {
winName = name;
}
nsFontWinA* font = (nsFontWinA*) LoadFont(aDC, winName);
if (font && FONT_HAS_GLYPH(font->mMap, aChar)) {
nsFontSubset* subset = font->mSubsets;
nsFontSubset* endSubsets = &(font->mSubsets[font->mSubsetsCount]);
while (subset < endSubsets) {
if (!subset->mMap) {
if (!subset->Load(font)) {
subset++;
continue;
}
}
if (FONT_HAS_GLYPH(subset->mMap, aChar)) {
return (nsFontWin*) subset;
}
subset++;
}
}
}
}
return nsnull;
}
nsFontWin*
nsFontMetricsWinA::FindGlobalFont(HDC aDC, PRUnichar c)
{
if (!gGlobalFonts) {
if (!InitializeGlobalFonts(aDC)) {
return nsnull;
}
}
for (int i = 0; i < gGlobalFontsCount; i++) {
if (!gGlobalFonts[i].skip) {
if (!gGlobalFonts[i].map) {
HFONT font = ::CreateFontIndirect(&gGlobalFonts[i].logFont);
if (!font) {
continue;
}
HFONT oldFont = (HFONT) ::SelectObject(aDC, font);
gGlobalFonts[i].map = GetCMAP(aDC);
::SelectObject(aDC, oldFont);
::DeleteObject(font);
if (!gGlobalFonts[i].map) {
continue;
}
if (SameAsPreviousMap(i)) {
continue;
}
}
if (FONT_HAS_GLYPH(gGlobalFonts[i].map, c)) {
nsFontWinA* font = (nsFontWinA*) LoadFont(aDC, gGlobalFonts[i].name);
if (font) {
nsFontSubset* subset = font->mSubsets;
nsFontSubset* endSubsets = &(font->mSubsets[font->mSubsetsCount]);
while (subset < endSubsets) {
if (!subset->mMap) {
if (!subset->Load(font)) {
subset++;
continue;
}
}
if (FONT_HAS_GLYPH(subset->mMap, c)) {
return (nsFontWin*) subset;
}
subset++;
}
mLoadedFontsCount--;
FreeFont(font);
}
}
}
}
return nsnull;
}