Backout bug 377950 due to startup crashes.

This commit is contained in:
benjamin@smedbergs.us 2007-05-29 08:09:23 -07:00
parent 05b111e301
commit a57f4060b6
5 changed files with 340 additions and 470 deletions

View File

@ -124,7 +124,7 @@ public:
/**
* Create the appropriate platform font group
*/
virtual gfxFontGroup *CreateFontGroup(const nsAString& aFamilies,
virtual gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
const gfxFontStyle *aStyle) = 0;
/* Returns PR_TRUE if the given block of ARGB32 data really has alpha, otherwise PR_FALSE */

View File

@ -60,6 +60,45 @@
#define NO_RANGE_FOUND 126 // bit 126 in the font unicode ranges is required to be 0
/** @description Font Weights
* Each available font weight is stored as as single bit inside a bitset.
* e.g. The binary value 0000000000001000 indcates font weight 400 is available.
* while the binary value 0000000000001001 indicates both font weight 100 and 400 are available
*
* The font weights which will be represented include {100, 200, 300, 400, 500, 600, 700, 800, 900}
* The font weight specified in the mFont->weight may include values which are not an even multiple of 100.
* If so, the font weight mod 100 indicates the number steps to lighten are make bolder.
* This corresponds to the CSS lighter and bolder property values. If bolder is applied twice to the font which has
* a font weight of 400 then the mFont->weight will contain the value 402.
* If lighter is applied twice to a font of weight 400 then the mFont->weight will contain the value 398.
* Only nine steps of bolder or lighter are allowed by the CSS XPCODE.
*/
// XXX change this from using a bitset to something cleaner eventually
class WeightTable
{
public:
THEBES_INLINE_DECL_REFCOUNTING(WeightTable)
WeightTable() : mWeights(0) {}
~WeightTable() {
}
PRBool TriedWeight(PRUint8 aWeight) {
return mWeights[aWeight - 1 + 10];
}
PRBool HasWeight(PRUint8 aWeight) {
return mWeights[aWeight - 1];
}
void SetWeight(PRUint8 aWeight, PRBool aValue) {
mWeights[aWeight - 1] = (aValue == PR_TRUE);
mWeights[aWeight - 1 + 10] = PR_TRUE;
}
private:
std::bitset<20> mWeights;
};
/* Unicode subrange table
* from: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/intl/unicode_63ub.asp
*
@ -240,58 +279,6 @@ static PRUint8 CharRangeBit(PRUint32 ch) {
return NO_RANGE_FOUND;
}
class gfxSparseBitSet {
public:
enum { BLOCK_SIZE = 32 };
PRBool test(PRUint32 aIndex) {
PRUint32 blockIndex = aIndex/(BLOCK_SIZE*8);
if (blockIndex >= mBlocks.Length())
return PR_FALSE;
Block *block = mBlocks[blockIndex];
if (!block)
return PR_FALSE;
return ((block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)]) & (1 << (aIndex & 0x7))) != 0;
}
void set(PRUint32 aIndex) {
PRUint32 blockIndex = aIndex/(BLOCK_SIZE*8);
if (blockIndex >= mBlocks.Length()) {
nsAutoPtr<Block> *blocks = mBlocks.AppendElements(blockIndex + 1 - mBlocks.Length());
if (!blocks) // OOM
return;
}
Block *block = mBlocks[blockIndex];
if (!block) {
block = new Block;
if (!block) // OOM
return;
memset(block, 0, sizeof(Block));
mBlocks[blockIndex] = block;
}
block->mBits[(aIndex/8) & (BLOCK_SIZE - 1)] |= 1 << (aIndex & 0x7);
}
PRUint32 getsize() {
PRUint32 size = 0;
for (PRUint32 i = 0; i < mBlocks.Length(); i++)
if (mBlocks[i])
size += sizeof(Block);
return size;
}
// If you want to set a lot of bits very fast, we could have Set variants
// that set a lot of bits at once, e.g. Set(PRUint32 aStart, PRUint32 aLength)
private:
struct Block {
PRUint8 mBits[BLOCK_SIZE];
};
nsTArray< nsAutoPtr<Block> > mBlocks;
};
/**
* FontEntry is a class that describes one of the fonts on the users system
* It contains information such as the name, font type, charset table and unicode ranges.
@ -304,8 +291,8 @@ public:
THEBES_INLINE_DECL_REFCOUNTING(FontEntry)
FontEntry(const nsAString& aName, PRUint16 aFontType) :
mName(aName), mFontType(aFontType), mDefaultWeight(0),
mUnicodeFont(PR_FALSE), mCharset(0), mUnicodeRanges(0)
mName(aName), mFontType(aFontType), mUnicodeFont(PR_FALSE),
mCharset(0), mUnicodeRanges(0)
{
}
@ -398,32 +385,10 @@ public:
return mUnicodeRanges[range];
}
class WeightTable
{
public:
THEBES_INLINE_DECL_REFCOUNTING(WeightTable)
WeightTable() : mWeights(0) {}
~WeightTable() {}
PRBool TriedWeight(PRUint8 aWeight) {
return mWeights[aWeight - 1 + 10];
}
PRBool HasWeight(PRUint8 aWeight) {
return mWeights[aWeight - 1];
}
void SetWeight(PRUint8 aWeight, PRBool aValue) {
mWeights[aWeight - 1] = aValue;
mWeights[aWeight - 1 + 10] = PR_TRUE;
}
private:
std::bitset<20> mWeights;
};
// The family name of the font
nsString mName;
PRUint16 mFontType;
PRUint16 mDefaultWeight;
PRUint8 mFamily;
PRUint8 mPitch;
@ -431,10 +396,6 @@ public:
std::bitset<256> mCharset;
std::bitset<128> mUnicodeRanges;
WeightTable mWeightTable;
gfxSparseBitSet mCharacterMap;
};
@ -468,8 +429,6 @@ public:
return mSpaceGlyph;
};
FontEntry *GetFontEntry() { return mFontEntry; }
protected:
HFONT MakeHFONT();
cairo_font_face_t *MakeCairoFontFace();
@ -493,7 +452,7 @@ private:
LOGFONTW mLogFont;
nsRefPtr<FontEntry> mFontEntry;
nsRefPtr<WeightTable> mWeightTable;
virtual void SetupCairoFont(cairo_t *aCR);
};

View File

@ -72,17 +72,11 @@ public:
gfxFontGroup *CreateFontGroup(const nsAString &aFamilies,
const gfxFontStyle *aStyle);
/* Given a string and a font we already have find the font that
* supports the most code points and most closely resembles aFont
*
* this involves looking at the fonts on your machine and seeing which
* code points they support as well as looking at things like the font
* family, style, weight, etc.
*/
FontEntry *FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont);
/* local methods */
void FindOtherFonts(const PRUnichar *aString, PRUint32 aLength, const char *aLangGroup, const char *aGeneric, nsString& array);
/* Find a FontEntry object that represents a font on your system given a name */
FontEntry *FindFontEntry(const nsAString& aName);
WeightTable *GetFontWeightTable(const nsAString& aName);
void PutFontWeightTable(const nsAString& aName, WeightTable *aWeightTable);
private:
void Init();
@ -91,10 +85,6 @@ private:
const NEWTEXTMETRICEXW *metrics,
DWORD fontType, LPARAM data);
static PLDHashOperator PR_CALLBACK FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg);
static int CALLBACK FontResolveProc(const ENUMLOGFONTEXW *lpelfe,
const NEWTEXTMETRICEXW *metrics,
DWORD fontType, LPARAM data);
@ -103,11 +93,12 @@ private:
nsRefPtr<FontEntry>& aData,
void* userArg);
static PLDHashOperator PR_CALLBACK FindFontForStringProc(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg);
static PLDHashOperator PR_CALLBACK FindFontForChar(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg);
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFonts;
nsDataHashtable<nsStringHashKey, nsRefPtr<WeightTable> > mFontWeights;
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontAliases;
nsDataHashtable<nsStringHashKey, nsRefPtr<FontEntry> > mFontSubstitutes;
nsStringArray mNonExistingFonts;

View File

@ -97,9 +97,6 @@ gfxWindowsFont::gfxWindowsFont(const nsAString& aName, const gfxFontStyle *aFont
mFontFace(nsnull), mScaledFont(nsnull),
mMetrics(nsnull)
{
// XXX we should work to get this passed in rather than having to find it again.
mFontEntry = gfxWindowsPlatform::GetPlatform()->FindFontEntry(aName);
NS_ASSERTION(mFontEntry, "Unable to find font entry for font. Something is whack.");
}
gfxWindowsFont::~gfxWindowsFont()
@ -178,6 +175,19 @@ gfxWindowsFont::MakeHFONT()
if (mFont)
return mFont;
if (!mWeightTable) {
nsString name(mName);
ToLowerCase(name);
gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
mWeightTable = platform->GetFontWeightTable(name);
if (!mWeightTable) {
mWeightTable = new WeightTable();
platform->PutFontWeightTable(name, mWeightTable);
}
}
PRInt8 baseWeight, weightDistance;
GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
@ -188,10 +198,10 @@ gfxWindowsFont::MakeHFONT()
if (weightDistance >= 0) {
for (PRUint8 i = baseWeight, k = 0; i < 10; i++) {
if (mFontEntry->mWeightTable.HasWeight(i)) {
if (mWeightTable->HasWeight(i)) {
k++;
chosenWeight = i * 100;
} else if (mFontEntry->mWeightTable.TriedWeight(i)) {
} else if (mWeightTable->TriedWeight(i)) {
continue;
} else {
const PRUint32 tryWeight = i * 100;
@ -206,7 +216,7 @@ gfxWindowsFont::MakeHFONT()
GetTextMetrics(dc, &metrics);
PRBool hasWeight = (metrics.tmWeight == tryWeight);
mFontEntry->mWeightTable.SetWeight(i, hasWeight);
mWeightTable->SetWeight(i, hasWeight);
if (hasWeight) {
chosenWeight = i * 100;
k++;
@ -318,7 +328,7 @@ gfxWindowsFont::ComputeMetrics()
mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition;
mMetrics->underlineSize = PR_MAX(1, (double)oMetrics.otmsUnderscoreSize);
mMetrics->underlineOffset = (double)oMetrics.otmsUnderscorePosition;
const MAT2 kIdentityMatrix = { {0, 1}, {0, 0}, {0, 0}, {0, 1} };
GLYPHMETRICS gm;
DWORD len = GetGlyphOutlineW(dc, PRUnichar('x'), GGO_METRICS, &gm, 0, nsnull, &kIdentityMatrix);
@ -664,7 +674,7 @@ SetupTextRunFromGlyphs(gfxTextRun *aRun, WCHAR *aGlyphs, HDC aDC,
PRUint32 length = aRun->GetLength();
if (IsAnyGlyphMissing(aGlyphs, length))
return PR_FALSE;
SIZE size;
nsAutoTArray<int,500> partialWidthArray;
if (!partialWidthArray.AppendElements(length))
@ -926,6 +936,11 @@ public:
free(mAlternativeString);
}
const PRUnichar *GetString() const { return mString; }
const PRUint32 GetStringLength() const { return mLength; }
static PRBool AddFontCallback(const nsAString& aName,
const nsACString& aGenericName,
void *closure) {
@ -940,26 +955,53 @@ public:
if (aName.Equals(item->mFonts[i]->GetName()))
return PR_TRUE;
nsRefPtr<gfxWindowsFont> font = GetOrMakeFont(aName, item->mGroup->GetStyle());
nsRefPtr<gfxWindowsFont> font =
GetOrMakeFont(aName, item->mGroup->GetStyle());
if (font) {
item->mFonts.AppendElement(font);
}
return PR_TRUE;
}
#ifdef DEBUG_pavlov
HRESULT Break() {
HRESULT rv;
SCRIPT_LOGATTR *logAttrs = (SCRIPT_LOGATTR*)malloc(sizeof(SCRIPT_LOGATTR) * mLength);
rv = ScriptBreak(mString, mLength, &mScriptItem->a, logAttrs);
for (PRUint32 i = 0; i < mLength; ++i) {
PR_LOG(gFontLog, PR_LOG_DEBUG, ("0x%04x - %d %d %d %d %d",
mString[i],
logAttrs[i].fSoftBreak,
logAttrs[i].fWhiteSpace,
logAttrs[i].fCharStop,
logAttrs[i].fWordStop,
logAttrs[i].fInvalid));
}
free(logAttrs);
return rv;
}
#endif
/* possible return values:
* S_OK - things succeeded
* GDI_ERROR - things failed to shape. Might want to try again after calling DisableShaping()
*/
E_PENDING -- means script cache lookup failed, DC needs to be set and font selected in to it.
USP_E_SCRIPT_NOT_IN_FONT -- this font doesn't support this text. keep trying new fonts.
if you try all possible fonts, then go back to font 0 after calling DisableShaping and try again
through all the fonts
*/
HRESULT Shape() {
HRESULT rv;
HDC shapeDC = nsnull;
const PRUnichar *str = mAlternativeString ? mAlternativeString : mString;
while (PR_TRUE) {
const PRUnichar *str =
mAlternativeString ? mAlternativeString : mString;
mScriptItem->a.fLogicalOrder = PR_TRUE;
mScriptItem->a.s.fDisplayZWG = PR_TRUE;
rv = ScriptShape(shapeDC, mCurrentFont->ScriptCache(),
str, mLength,
@ -981,6 +1023,10 @@ public:
continue;
}
#if 0 // debugging only
if (rv != USP_E_SCRIPT_NOT_IN_FONT && !shapeDC)
printf("skipped select-shape %d\n", rv);
#endif
return rv;
}
}
@ -997,19 +1043,30 @@ public:
GenerateAlternativeString();
}
/* this should only be called if there are no surrogates
* in the string */
PRBool IsMissingGlyphsCMap() {
nsRefPtr<FontEntry> fe = GetCurrentFont()->GetFontEntry();
for (PRUint32 i = 0; i < mLength; i++) {
PRUint32 ch = mString[i];
if ((i+1 < mLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(mString[i+1])) {
i++;
ch = SURROGATE_TO_UCS4(ch, mString[i]);
}
if (!fe->mCharacterMap.test(ch))
return PR_TRUE;
}
HRESULT rv;
HDC cmapDC = nsnull;
return PR_FALSE;
while (PR_TRUE) {
rv = ScriptGetCMap(cmapDC, mCurrentFont->ScriptCache(),
mString, mLength, 0, mGlyphs);
if (rv == E_PENDING) {
SelectFont();
cmapDC = mDC;
continue;
}
if (rv == S_OK)
return PR_FALSE;
PR_LOG(gFontLog, PR_LOG_WARNING, ("cmap is missing a glyph"));
for (PRUint32 i = 0; i < mLength; i++)
PR_LOG(gFontLog, PR_LOG_WARNING, (" - %d", mGlyphs[i]));
return PR_TRUE;
}
}
PRBool IsGlyphMissing(SCRIPT_FONTPROPERTIES *aSFP, PRUint32 aGlyphIndex) {
@ -1018,6 +1075,22 @@ public:
return PR_FALSE;
}
PRBool IsMissingGlyphs() {
SCRIPT_FONTPROPERTIES sfp;
ScriptFontProperties(&sfp);
PRUint32 charIndex = 0;
for (int i = 0; i < mNumGlyphs; ++i) {
if (IsGlyphMissing(&sfp, i))
return PR_TRUE;
#ifdef DEBUG_pavlov // excess debugging code
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x %04x %04x", sfp.wgBlank, sfp.wgDefault, sfp.wgInvalid));
PR_LOG(gFontLog, PR_LOG_DEBUG, ("glyph%d - 0x%04x", i, mGlyphs[i]));
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%04x -- %04x -- %04x", ScriptProperties()->fInvalidGlyph, mScriptItem->a.fNoGlyphIndex, mAttr[i].fZeroWidth));
#endif
}
return PR_FALSE;
}
HRESULT Place() {
HRESULT rv;
@ -1213,6 +1286,7 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
goto TRY_AGAIN_HOPE_FOR_THE_BEST_2;
} else if (!mTriedOtherFonts) {
mTriedOtherFonts = PR_TRUE;
nsString fonts;
gfxWindowsPlatform *platform = gfxWindowsPlatform::GetPlatform();
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG)) {
@ -1228,16 +1302,22 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - 0x%04x", ch));
}
}
nsRefPtr<FontEntry> newFont = platform->FindFontForString(mString, mLength, mFonts[0]);
if (newFont) {
platform->FindOtherFonts(mString, mLength,
nsPromiseFlatCString(mGroup->GetStyle()->langGroup).get(),
nsPromiseFlatCString(mGroup->GetGenericFamily()).get(),
fonts);
if (!fonts.IsEmpty()) {
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
PR_LOG(gFontLog, PR_LOG_DEBUG, ("Got back: %s", NS_LossyConvertUTF16toASCII(newFont->mName).get()));
AddFontCallback(newFont->mName, EmptyCString(), this);
PR_LOG(gFontLog, PR_LOG_DEBUG, ("Got back: %s", NS_LossyConvertUTF16toASCII(fonts).get()));
gfxFontGroup::ForEachFont(fonts, EmptyCString(), UniscribeItem::AddFontCallback, this);
}
goto TRY_AGAIN_HOPE_FOR_THE_BEST_2;
} else {
// const SCRIPT_PROPERTIES *sp = item->ScriptProperties();
// We should try to look up the font based on sp.langgroup
// if it isn't ambiguious. Would save us time over doing
// character by character lookups at least for pref fonts
}
return nsnull;
@ -1273,6 +1353,7 @@ TRY_AGAIN_HOPE_FOR_THE_BEST_2:
mFontSelected = PR_TRUE;
}
private:
static PRInt32 GetCJKLangGroupIndex(const char *aLangGroup) {
PRInt32 i;
@ -1360,7 +1441,6 @@ private:
mAlternativeString[i] = PRUnichar(0xFFFD);
}
}
private:
nsRefPtr<gfxContext> mContext;
HDC mDC;
@ -1401,6 +1481,7 @@ class Uniscribe
public:
Uniscribe(gfxContext *aContext, HDC aDC, const PRUnichar *aString, PRUint32 aLength, PRBool aIsRTL) :
mContext(aContext), mDC(aDC), mString(aString), mLength(aLength), mIsRTL(aIsRTL),
mIsComplex(ScriptIsComplex(aString, aLength, SIC_COMPLEX) == S_OK),
mItems(nsnull) {
}
~Uniscribe() {
@ -1460,6 +1541,8 @@ private:
const PRUnichar *mString;
const PRUint32 mLength;
const PRBool mIsRTL;
// XXX not used
const PRBool mIsComplex;
SCRIPT_CONTROL mControl;
SCRIPT_STATE mState;
@ -1467,9 +1550,10 @@ private:
int mNumItems;
};
void
gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun, const PRUnichar *aString,
PRUint32 aLength)
PRUint32 aLength)
{
nsRefPtr<gfxASurface> surf = aContext->CurrentSurface();
HDC aDC = GetDCFromSurface(surf);
@ -1484,7 +1568,7 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun
/* itemize the string */
int numItems = us.Itemize();
for (int i = 0; i < numItems; ++i) {
for (int i=0; i < numItems; ++i) {
PRUint32 fontIndex = 0;
SaveDC(aDC);
@ -1500,46 +1584,59 @@ gfxWindowsFontGroup::InitTextRunUniscribe(gfxContext *aContext, gfxTextRun *aRun
if (font) {
/* set the curret font on the item */
item->SetCurrentFont(font);
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
PR_LOG(gFontLog, PR_LOG_DEBUG, ("trying: %s", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
if (!giveUp && item->IsMissingGlyphsCMap()) {
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - missing glyphs in cmap"));
continue;
PRBool cmapHasGlyphs = PR_FALSE; // false means "maybe" here
if (!giveUp && !(aRun->GetFlags() & TEXT_HAS_SURROGATES)) {
if (item->IsMissingGlyphsCMap())
continue;
else
cmapHasGlyphs = PR_TRUE;
}
SCRIPT_SHAPE:
rv = item->Shape();
if (giveUp) {
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - gave up", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
goto SCRIPT_PLACE;
}
if (FAILED(rv)) {
PR_LOG(gFontLog, PR_LOG_DEBUG, (" - shaping failed"));
// we know we have the glyphs to display this font already
// so Uniscribe just doesn't know how to shape the script.
// Render the glyphs without shaping.
item->DisableShaping();
goto SCRIPT_SHAPE;
}
if (FAILED(rv))
continue;
if (!cmapHasGlyphs && item->IsMissingGlyphs())
continue;
} else {
#if 0
/* code to try all the fonts again without shaping on.
in general, if we couldn't shape we should probably just give up */
if (item->ShapingEnabled()) {
item->DisableShaping();
item->ResetFontIndex();
continue;
}
#endif
giveUp = PR_TRUE;
item->DisableShaping();
item->ResetFontIndex();
continue;
}
if (PR_LOG_TEST(gFontLog, PR_LOG_DEBUG))
PR_LOG(gFontLog, PR_LOG_DEBUG, ("%s - worked", NS_LossyConvertUTF16toASCII(font->GetName()).get()));
SCRIPT_PLACE:
NS_ASSERTION(SUCCEEDED(rv), "Failed to shape -- we should never hit this");
#ifdef DEBUG_pavlov
item->Break();
#endif
rv = item->Place();
if (FAILED(rv)) {
if (PR_LOG_TEST(gFontLog, PR_LOG_WARNING))
@ -1551,7 +1648,6 @@ SCRIPT_PLACE:
}
item->SaveGlyphs(aRun);
delete item;
RestoreDC(aDC, -1);

View File

@ -54,21 +54,10 @@
#include <string>
//#define DEBUG_CMAP_SIZE 1
/* Define this if we want to update the unicode range bitsets based
* on the actual characters a font supports.
*
* Doing this can result in very large lists of fonts being returned.
* Not doing this can let us prioritize fonts that do have the bit set
* as they are more likely to provide better glyphs (in theory).
*/
//#define UPDATE_RANGES
gfxWindowsPlatform::gfxWindowsPlatform()
{
mFonts.Init(200);
mFontWeights.Init(200);
mFontAliases.Init(20);
mFontSubstitutes.Init(50);
UpdateFontList();
@ -114,11 +103,13 @@ gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
// mark the charset bit
fe->mCharset[metrics.tmCharSet] = 1;
// put the default weight in the weight table
fe->mWeightTable.SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
// store the default font weight
fe->mDefaultWeight = metrics.tmWeight;
// put this in the weight table
nsRefPtr<WeightTable> wt;
if (!thisp->mFontWeights.Get(name, &wt)) {
wt = new WeightTable();
wt->SetWeight(PR_MAX(1, PR_MIN(9, metrics.tmWeight / 100)), PR_TRUE);
thisp->mFontWeights.Put(name, wt);
}
fe->mFamily = logFont.lfPitchAndFamily & 0xF0;
fe->mPitch = logFont.lfPitchAndFamily & 0x0F;
@ -145,227 +136,6 @@ gfxWindowsPlatform::FontEnumProc(const ENUMLOGFONTEXW *lpelfe,
return 1;
}
static inline PRUint16
ReadShortAt(const PRUint8 *aBuf, PRUint32 aIndex)
{
return (aBuf[aIndex] << 8) | aBuf[aIndex + 1];
}
static inline PRUint32
ReadLongAt(const PRUint8 *aBuf, PRUint32 aIndex)
{
return ((aBuf[aIndex] << 24) | (aBuf[aIndex + 1] << 16) | (aBuf[aIndex + 2] << 8) | (aBuf[aIndex + 3]));
}
static nsresult
ReadCMAPTableFormat12(PRUint8 *aBuf, PRInt32 len, FontEntry *aFontEntry)
{
enum {
OffsetFormat = 0,
OffsetReserved = 2,
OffsetTableLength = 4,
OffsetLanguage = 8,
OffsetNumberGroups = 12,
OffsetGroups = 16,
SizeOfGroup = 12,
GroupOffsetStartCode = 0,
GroupOffsetEndCode = 4
};
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 12, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetReserved) == 0, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetTableLength) != 0, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(ReadLongAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
const PRUint32 numGroups = ReadLongAt(aBuf, OffsetNumberGroups);
const PRUint8 *groups = aBuf + OffsetGroups;
for (PRUint32 i = 0; i < numGroups; i++, groups += SizeOfGroup) {
const PRUint32 startCharCode = ReadLongAt(groups, GroupOffsetStartCode);
const PRUint32 endCharCode = ReadLongAt(groups, GroupOffsetEndCode);
for (PRUint32 c = startCharCode; c <= endCharCode; ++c) {
// XXX we should use a range setting functions on gfxSparseBitset
// which could be a lot faster
aFontEntry->mCharacterMap.set(c);
#ifdef UPDATE_RANGES
PRUint16 b = CharRangeBit(c);
if (b != NO_RANGE_FOUND)
aFontEntry->mUnicodeRanges.set(b, true);
#endif
}
}
return NS_OK;
}
static nsresult
ReadCMAPTableFormat4(PRUint8 *aBuf, PRInt32 aLength, FontEntry *aFontEntry)
{
enum {
OffsetFormat = 0,
OffsetLength = 2,
OffsetLanguage = 4,
OffsetSegCountX2 = 6,
};
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetFormat) == 4, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetLength) != 0, NS_ERROR_FAILURE);
NS_ENSURE_TRUE(ReadShortAt(aBuf, OffsetLanguage) == 0, NS_ERROR_FAILURE);
PRUint16 segCountX2 = ReadShortAt(aBuf, OffsetSegCountX2);
const PRUint8 *endCounts = aBuf + 14;
const PRUint8 *startCounts = endCounts + segCountX2 + 2;
const PRUint8 *idDeltas = startCounts + segCountX2;
const PRUint8 *idRangeOffsets = idDeltas + segCountX2;
for (PRUint16 i = 0; i < segCountX2; i += 2) {
const PRUint16 endCount = ReadShortAt(endCounts, i);
const PRUint16 startCount = ReadShortAt(startCounts, i);
const PRUint16 idRangeOffset = ReadShortAt(idRangeOffsets, i);
if (idRangeOffset == 0) {
for (PRUint32 c = startCount; c <= endCount; c++) {
aFontEntry->mCharacterMap.set(c);
#ifdef UPDATE_RANGES
PRUint16 b = CharRangeBit(c);
if (b != NO_RANGE_FOUND)
aFontEntry->mUnicodeRanges.set(b, true);
#endif
}
} else {
const PRUint8 *gdata = idRangeOffsets + i + idRangeOffset;
for (PRUint16 c = startCount; c <= endCount; ++c, gdata += 2) {
// make sure we have a glyph
if (PRUint16 g = ReadShortAt(gdata, 0)) {
aFontEntry->mCharacterMap.set(c);
#ifdef UPDATE_RANGES
PRUint16 b = CharRangeBit(c);
if (b != NO_RANGE_FOUND)
aFontEntry->mUnicodeRanges.set(b, true);
#endif
}
}
}
}
return NS_OK;
}
static nsresult
ReadCMAP(HDC hdc, FontEntry *aFontEntry)
{
const PRUint32 kCMAP = (('c') | ('m' << 8) | ('a' << 16) | ('p' << 24));
DWORD len = GetFontData(hdc, kCMAP, 0, nsnull, 0);
NS_ENSURE_TRUE(len != GDI_ERROR && len != 0, NS_ERROR_FAILURE);
nsAutoTArray<PRUint8,16384> buffer;
if (!buffer.AppendElements(len))
return NS_ERROR_OUT_OF_MEMORY;
PRUint8 *buf = buffer.Elements();
DWORD newLen = GetFontData(hdc, kCMAP, 0, buf, len);
NS_ENSURE_TRUE(newLen == len, NS_ERROR_FAILURE);
enum {
OffsetVersion = 0,
OffsetNumTables = 2,
SizeOfHeader = 4,
TableOffsetPlatformID = 0,
TableOffsetEncodingID = 2,
TableOffsetOffset = 4,
SizeOfTable = 8,
SubtableOffsetFormat = 0,
};
enum {
PlatformIDMicrosoft = 3
};
enum {
EncodingIDMicrosoft = 1,
EncodingIDUCS4 = 10
};
PRUint16 version = ReadShortAt(buf, OffsetVersion);
PRUint16 numTables = ReadShortAt(buf, OffsetNumTables);
// save the format and offset we want here
PRUint32 keepOffset;
PRUint32 keepFormat;
PRUint8 *table = buf + SizeOfHeader;
for (PRUint16 i = 0; i < numTables; ++i, table += SizeOfTable) {
const PRUint16 platformID = ReadShortAt(table, TableOffsetPlatformID);
if (platformID != PlatformIDMicrosoft)
continue;
const PRUint16 encodingID = ReadShortAt(table, TableOffsetEncodingID);
const PRUint32 offset = ReadLongAt(table, TableOffsetOffset);
const PRUint8 *subtable = buf + offset;
const PRUint16 format = ReadShortAt(subtable, SubtableOffsetFormat);
if (format == 4 && encodingID == EncodingIDMicrosoft) {
keepFormat = format;
keepOffset = offset;
}
else if (format == 12 && encodingID == EncodingIDUCS4) {
keepFormat = format;
keepOffset = offset;
break; // we don't want to try anything else when this format is available.
}
}
nsresult rv = NS_ERROR_FAILURE;
if (keepFormat == 12)
rv = ReadCMAPTableFormat12(buf + keepOffset, len - keepOffset, aFontEntry);
else if (keepFormat == 4)
rv = ReadCMAPTableFormat4(buf + keepOffset, len - keepOffset, aFontEntry);
return rv;
}
PLDHashOperator PR_CALLBACK
gfxWindowsPlatform::FontGetCMapDataProc(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg)
{
if (aFontEntry->IsCrappyFont())
return PL_DHASH_NEXT;
HDC hdc = GetDC(nsnull);
LOGFONTW logFont;
memset(&logFont, 0, sizeof(LOGFONTW));
logFont.lfCharSet = DEFAULT_CHARSET;
logFont.lfPitchAndFamily = 0;
PRUint32 l = PR_MIN(aFontEntry->mName.Length(), LF_FACESIZE - 1);
memcpy(logFont.lfFaceName,
nsPromiseFlatString(aFontEntry->mName).get(),
l * sizeof(PRUnichar));
logFont.lfFaceName[l] = 0;
HFONT font = CreateFontIndirectW(&logFont);
if (font) {
HFONT oldFont = (HFONT)SelectObject(hdc, font);
nsresult rv = ReadCMAP(hdc, aFontEntry);
if (NS_FAILED(rv))
aFontEntry->mUnicodeFont = PR_FALSE;
SelectObject(hdc, oldFont);
DeleteObject(font);
}
ReleaseDC(nsnull, hdc);
return PL_DHASH_NEXT;
}
struct FontListData {
FontListData(const nsACString& aLangGroup, const nsACString& aGenericFamily, nsStringArray& aListOfFonts) :
mLangGroup(aLangGroup), mGenericFamily(aGenericFamily), mStringArray(aListOfFonts) {}
@ -437,17 +207,17 @@ gfxWindowsPlatform::UpdateFontList()
EnumFontFamiliesExW(dc, &logFont, (FONTENUMPROCW)gfxWindowsPlatform::FontEnumProc, (LPARAM)this, 0);
::ReleaseDC(nsnull, dc);
// Update all the fonts cmaps
mFonts.Enumerate(gfxWindowsPlatform::FontGetCMapDataProc, nsnull);
// Create the list of FontSubstitutes
nsCOMPtr<nsIWindowsRegKey> regKey = do_CreateInstance("@mozilla.org/windows-registry-key;1");
nsCOMPtr<nsIWindowsRegKey> regKey =
do_CreateInstance("@mozilla.org/windows-registry-key;1");
if (!regKey)
return NS_ERROR_FAILURE;
NS_NAMED_LITERAL_STRING(kFontSubstitutesKey, "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
NS_NAMED_LITERAL_STRING(kFontSubstitutesKey,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\FontSubstitutes");
nsresult rv = regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
nsresult rv =
regKey->Open(nsIWindowsRegKey::ROOT_KEY_LOCAL_MACHINE,
kFontSubstitutesKey, nsIWindowsRegKey::ACCESS_READ);
if (NS_FAILED(rv))
return rv;
@ -581,86 +351,155 @@ gfxWindowsPlatform::FontResolveProc(const ENUMLOGFONTEXW *lpelfe,
// XXX If the font has font link, we should add the linked font.
}
struct FontMatch {
FontMatch() : rank(0) {}
PRBool operator <(const FontMatch& other) const { return (rank < other.rank); }
PRBool operator >(const FontMatch& other) const { return (rank > other.rank); }
PRBool operator ==(const FontMatch& other) const { return (rank == other.rank); }
nsRefPtr<FontEntry> fontEntry;
PRInt8 rank;
};
struct FontSearch {
FontSearch(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont) :
string(aString), length(aLength), fontToMatch(aFont), matchRank(0) {
FontSearch(PRUnichar aCh, PRUint8 aRange, const char *aLangGroup, const char *aFamily) :
ch(aCh), langGroup(aLangGroup), family(aFamily), range(aRange), highestRank(0), fontMatches(25) {
}
const PRUnichar *string;
const PRUint32 length;
nsRefPtr<gfxWindowsFont> fontToMatch;
PRInt32 matchRank;
nsRefPtr<FontEntry> bestMatch;
PRBool RankIsOK(PRUint32 rank) {
return (rank >= (highestRank / 2) + 1);
}
const PRUint32 ch;
const char *langGroup;
const char *family;
const PRUint8 range;
PRInt8 highestRank;
nsTArray<FontMatch> fontMatches;
};
PLDHashOperator PR_CALLBACK
gfxWindowsPlatform::FindFontForStringProc(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg)
gfxWindowsPlatform::FindFontForChar(nsStringHashKey::KeyType aKey,
nsRefPtr<FontEntry>& aFontEntry,
void* userArg)
{
// bitmap fonts suck
if (aFontEntry->IsCrappyFont())
return PL_DHASH_NEXT;
FontSearch *data = (FontSearch*)userArg;
FontMatch fm;
if (aFontEntry->SupportsRange(data->range))
fm.rank += 20;
PRInt32 rank = 0;
if (aFontEntry->SupportsLangGroup(nsDependentCString(data->langGroup)))
fm.rank += 10;
for (PRUint32 i = 0; i < data->length; ++i) {
PRUint32 ch = data->string[i];
if (data->family && aFontEntry->MatchesGenericFamily(nsDependentCString(data->family)))
fm.rank += 5;
if ((i+1 < data->length) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(data->string[i+1])) {
i++;
ch = SURROGATE_TO_UCS4(ch, data->string[i]);
}
// XXX this code doesn't really work like I would hope
// we really just want to avoid non-unicode fonts.. i.e. wingdings, etc
// find something better to replace it with
/* rank symbol fonts lower than other stuff -- this might be a bad idea, but should
* avoid things like wingdings showing up while rendering hindi scripts
*/
// if (aFontEntry->SupportsLangGroup(NS_LITERAL_CSTRING("x-symbol")))
// fm.rank -= 5;
if (aFontEntry->mCharacterMap.test(ch)) {
rank += 20;
/* This will allow us to cut out some of the fonts, but not all
* since some might get in early before we find the real highest rank.
*/
if (fm.rank > data->highestRank)
data->highestRank = fm.rank;
// fonts that claim to support the range are more
// likely to be "better fonts" than ones that don't... (in theory)
if (aFontEntry->SupportsRange(CharRangeBit(ch)))
rank += 1;
}
}
// if we didn't match any characters don't bother wasting more time.
if (rank == 0)
if (!data->RankIsOK(fm.rank))
return PL_DHASH_NEXT;
if (aFontEntry->SupportsLangGroup(data->fontToMatch->GetStyle()->langGroup))
rank += 10;
if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mFamily)
rank += 5;
if (data->fontToMatch->GetFontEntry()->mFamily == aFontEntry->mPitch)
rank += 5;
/* weight */
PRInt8 baseWeight, weightDistance;
data->fontToMatch->GetStyle()->ComputeWeightAndOffset(&baseWeight, &weightDistance);
PRUint16 targetWeight = (baseWeight * 100) + (weightDistance * 100);
if (targetWeight == aFontEntry->mDefaultWeight)
rank += 5;
if (data->matchRank == 0 || rank > data->matchRank) {
data->bestMatch = aFontEntry;
data->matchRank = rank;
if (fm.rank > 0) {
fm.fontEntry = aFontEntry;
data->fontMatches.AppendElement(fm);
}
return PL_DHASH_NEXT;
}
FontEntry *
gfxWindowsPlatform::FindFontForString(const PRUnichar *aString, PRUint32 aLength, gfxWindowsFont *aFont)
void
gfxWindowsPlatform::FindOtherFonts(const PRUnichar* aString, PRUint32 aLength, const char *aLangGroup, const char *aGeneric, nsString& fonts)
{
FontSearch data(aString, aLength, aFont);
fonts.Truncate();
// find fonts that support the character
mFonts.Enumerate(gfxWindowsPlatform::FindFontForStringProc, &data);
PRBool surrogates = PR_FALSE;
return data.bestMatch;
std::bitset<128> ranges(0);
for (PRUint32 z = 0; z < aLength; ++z) {
PRUint32 ch = aString[z];
if ((z+1 < aLength) && NS_IS_HIGH_SURROGATE(ch) && NS_IS_LOW_SURROGATE(aString[z+1])) {
z++;
ch = SURROGATE_TO_UCS4(ch, aString[z]);
surrogates = PR_TRUE;
}
PRUint8 range = CharRangeBit(ch);
if (range != NO_RANGE_FOUND && !ranges[range]) {
FontSearch data(ch, CharRangeBit(ch), aLangGroup, aGeneric);
mFonts.Enumerate(gfxWindowsPlatform::FindFontForChar, &data);
data.fontMatches.Sort();
PRUint32 nmatches = data.fontMatches.Length();
if (nmatches > 0) {
//printf("%d matches for 0x%04x\n", nmatches, ch);
for (PRUint32 i = nmatches - 1; i > 0; i--) {
const FontMatch& fm = data.fontMatches[i];
if (data.RankIsOK(fm.rank)) {
if (!fonts.IsEmpty())
fonts.AppendLiteral(", ");
fonts.Append(fm.fontEntry->mName);
}
}
}
ranges[range] = PR_TRUE;
}
}
if (surrogates) {
// append fonts that support surrogates on to the list
FontSearch data(0xd801, 57, aLangGroup, aGeneric);
mFonts.Enumerate(gfxWindowsPlatform::FindFontForChar, &data);
data.fontMatches.Sort();
PRUint32 nmatches = data.fontMatches.Length();
if (nmatches > 0) {
for (PRUint32 i = nmatches - 1; i > 0; i--) {
const FontMatch& fm = data.fontMatches[i];
if (data.RankIsOK(fm.rank)) {
if (!fonts.IsEmpty())
fonts.AppendLiteral(", ");
fonts.Append(fm.fontEntry->mName);
}
}
}
}
}
WeightTable *
gfxWindowsPlatform::GetFontWeightTable(const nsAString& aName)
{
nsRefPtr<WeightTable> wt;
if (!mFontWeights.Get(aName, &wt)) {
return nsnull;
}
return wt;
}
void
gfxWindowsPlatform::PutFontWeightTable(const nsAString& aName, WeightTable *aWeightTable)
{
mFontWeights.Put(aName, aWeightTable);
}
gfxFontGroup *
@ -669,18 +508,3 @@ gfxWindowsPlatform::CreateFontGroup(const nsAString &aFamilies,
{
return new gfxWindowsFontGroup(aFamilies, aStyle);
}
FontEntry *
gfxWindowsPlatform::FindFontEntry(const nsAString& aName)
{
nsString name(aName);
ToLowerCase(name);
nsRefPtr<FontEntry> fe;
if (!mFonts.Get(name, &fe) &&
!mFontSubstitutes.Get(name, &fe) &&
!mFontAliases.Get(name, &fe)) {
return nsnull;
}
return fe.get();
}