GDIFontEntry* GDIFontEntry::CreateFontEntry(const nsAString& aName, gfxWindowsFontType aFontType, PRBool aItalic, PRUint16 aWeight, gfxUserFontData* aUserFontData) { // jtdfix - need to set charset, unicode ranges, pitch/family GDIFontEntry *fe = new GDIFontEntry(aName, aFontType, aItalic, aWeight, aUserFontData); // ReadCMAP may change the values of mUnicodeFont and mSymbolFont if (NS_FAILED(fe->ReadCMAP())) { // Type1 fonts aren't necessarily Unicode but // this is the best guess we can make here if (fe->IsType1()) fe->mUnicodeFont = PR_TRUE; else fe->mUnicodeFont = PR_FALSE; // For fonts where we failed to read the character map, // we can take a slow path to look up glyphs character by character fe->mUnknownCMAP = PR_TRUE; } return fe; }
void gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize, bool aUseGDIFakeItalic) { GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry()); uint16_t weight; if (fe->IsUserFont()) { if (fe->IsLocalUserFont()) { // for local user fonts, don't change the original weight // in the entry's logfont, because that could alter the // choice of actual face used (bug 724231) weight = 0; } else { // avoid GDI synthetic bold which occurs when weight // specified is >= font data weight + 200 weight = mNeedsBold ? 700 : 200; } } else { weight = mNeedsBold ? 700 : fe->Weight(); } fe->FillLogFont(&aLogFont, weight, aSize, (mAntialiasOption == kAntialiasSubpixel) ? true : false); // If GDI synthetic italic is wanted, force the lfItalic field to true if (aUseGDIFakeItalic) { aLogFont.lfItalic = 1; } }
void gfxGDIFont::FillLogFont(LOGFONTW& aLogFont, gfxFloat aSize) { GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry()); PRUint16 weight = mNeedsBold ? 700 : fe->Weight(); PRBool italic = (mStyle.style & (FONT_STYLE_ITALIC | FONT_STYLE_OBLIQUE)); // if user font, disable italics/bold if defined to be italics/bold face // this avoids unwanted synthetic italics/bold if (fe->mIsUserFont) { if (fe->IsItalic()) italic = PR_FALSE; // avoid synthetic italic if (fe->IsBold() || !mNeedsBold) { // avoid GDI synthetic bold which occurs when weight // specified is >= font data weight + 200 weight = 200; } } fe->FillLogFont(&aLogFont, italic, weight, aSize, (mAntialiasOption == kAntialiasSubpixel) ? PR_TRUE : PR_FALSE); }
int CALLBACK GDIFontFamily::FamilyAddStylesProc(const ENUMLOGFONTEXW *lpelfe, const NEWTEXTMETRICEXW *nmetrics, DWORD fontType, LPARAM data) { const NEWTEXTMETRICW& metrics = nmetrics->ntmTm; LOGFONTW logFont = lpelfe->elfLogFont; GDIFontFamily *ff = reinterpret_cast<GDIFontFamily*>(data); // Some fonts claim to support things > 900, but we don't so clamp the sizes logFont.lfWeight = clamped(logFont.lfWeight, LONG(100), LONG(900)); gfxWindowsFontType feType = GDIFontEntry::DetermineFontType(metrics, fontType); GDIFontEntry *fe = nullptr; for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get()); if (feType > fe->mFontType) { // if the new type is better than the old one, remove the old entries ff->mAvailableFonts.RemoveElementAt(i); --i; } else if (feType < fe->mFontType) { // otherwise if the new type is worse, skip it return 1; } } for (uint32_t i = 0; i < ff->mAvailableFonts.Length(); ++i) { fe = static_cast<GDIFontEntry*>(ff->mAvailableFonts[i].get()); // check if we already know about this face if (fe->mWeight == logFont.lfWeight && fe->mItalic == (logFont.lfItalic == 0xFF)) { // update the charset bit here since this could be different fe->mCharset.set(metrics.tmCharSet); return 1; } } // We can't set the hasItalicFace flag correctly here, // because we might not have seen the family's italic face(s) yet. // So we'll set that flag for all members after loading all the faces. fe = GDIFontEntry::CreateFontEntry(nsDependentString(lpelfe->elfFullName), feType, (logFont.lfItalic == 0xFF), (uint16_t) (logFont.lfWeight), 0, nullptr, false); if (!fe) return 1; ff->AddFontEntry(fe); // mark the charset bit fe->mCharset.set(metrics.tmCharSet); fe->mWindowsFamily = logFont.lfPitchAndFamily & 0xF0; fe->mWindowsPitch = logFont.lfPitchAndFamily & 0x0F; if (nmetrics->ntmFontSig.fsUsb[0] != 0x00000000 && nmetrics->ntmFontSig.fsUsb[1] != 0x00000000 && nmetrics->ntmFontSig.fsUsb[2] != 0x00000000 && nmetrics->ntmFontSig.fsUsb[3] != 0x00000000) { // set the unicode ranges uint32_t x = 0; for (uint32_t i = 0; i < 4; ++i) { DWORD range = nmetrics->ntmFontSig.fsUsb[i]; for (uint32_t k = 0; k < 32; ++k) { fe->mUnicodeRanges.set(x++, (range & (1 << k)) != 0); } } } #ifdef PR_LOGGING if (LOG_FONTLIST_ENABLED()) { LOG_FONTLIST(("(fontlist) added (%s) to family (%s)" " with style: %s weight: %d stretch: %d", NS_ConvertUTF16toUTF8(fe->Name()).get(), NS_ConvertUTF16toUTF8(ff->Name()).get(), (logFont.lfItalic == 0xff) ? "italic" : "normal", logFont.lfWeight, fe->Stretch())); } #endif return 1; }
void gfxGDIFont::Initialize() { NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak"); LOGFONTW logFont; // Figure out if we want to do synthetic oblique styling. GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry()); bool wantFakeItalic = (mStyle.style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) && !fe->IsItalic(); // If the font's family has an actual italic face (but font matching // didn't choose it), we have to use a cairo transform instead of asking // GDI to italicize, because that would use a different face and result // in a possible glyph ID mismatch between shaping and rendering. // // We use the mFamilyHasItalicFace flag in the entry in case of user fonts, // where the *CSS* family may not know about italic faces that are present // in the *GDI* family, and which GDI would use if we asked it to perform // the "italicization". bool useCairoFakeItalic = wantFakeItalic && fe->mFamilyHasItalicFace; if (mAdjustedSize == 0.0) { mAdjustedSize = mStyle.size; if (mStyle.sizeAdjust != 0.0 && mAdjustedSize > 0.0) { // to implement font-size-adjust, we first create the "unadjusted" font FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic); mFont = ::CreateFontIndirectW(&logFont); // initialize its metrics so we can calculate size adjustment Initialize(); // calculate the properly adjusted size, and then proceed // to recreate mFont and recalculate metrics gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight; mAdjustedSize = mStyle.GetAdjustedSize(aspect); // delete the temporary font and metrics ::DeleteObject(mFont); mFont = nullptr; delete mMetrics; mMetrics = nullptr; } } // (bug 724231) for local user fonts, we don't use GDI's synthetic bold, // as it could lead to a different, incompatible face being used // but instead do our own multi-striking if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) { mApplySyntheticBold = true; } // this may end up being zero mAdjustedSize = ROUND(mAdjustedSize); FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic); mFont = ::CreateFontIndirectW(&logFont); mMetrics = new gfxFont::Metrics; ::memset(mMetrics, 0, sizeof(*mMetrics)); AutoDC dc; SetGraphicsMode(dc.GetDC(), GM_ADVANCED); AutoSelectFont selectFont(dc.GetDC(), mFont); // Get font metrics if size > 0 if (mAdjustedSize > 0.0) { OUTLINETEXTMETRIC oMetrics; TEXTMETRIC& metrics = oMetrics.otmTextMetrics; if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) { mMetrics->superscriptOffset = (double)oMetrics.otmptSuperscriptOffset.y; // Some fonts have wrong sign on their subscript offset, bug 410917. mMetrics->subscriptOffset = fabs((double)oMetrics.otmptSubscriptOffset.y); mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize; mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition; mMetrics->underlineSize = (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.GetDC(), PRUnichar('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix); if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) { // 56% of ascent, best guess for true type mMetrics->xHeight = ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR); } else { mMetrics->xHeight = gm.gmptGlyphOrigin.y; } mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading; gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent; mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight); mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent; if (oMetrics.otmEMSquare > 0) { mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare); } } else { // Make a best-effort guess at extended metrics // this is based on general typographic guidelines // GetTextMetrics can fail if the font file has been removed // or corrupted recently. BOOL result = GetTextMetrics(dc.GetDC(), &metrics); if (!result) { NS_WARNING("Missing or corrupt font data, fasten your seatbelt"); mIsValid = false; memset(mMetrics, 0, sizeof(*mMetrics)); return; } mMetrics->xHeight = ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR); mMetrics->superscriptOffset = mMetrics->xHeight; mMetrics->subscriptOffset = mMetrics->xHeight; mMetrics->strikeoutSize = 1; mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight mMetrics->underlineSize = 1; mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading; mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading; mMetrics->emDescent = metrics.tmDescent; } mMetrics->internalLeading = metrics.tmInternalLeading; mMetrics->externalLeading = metrics.tmExternalLeading; mMetrics->maxHeight = metrics.tmHeight; mMetrics->maxAscent = metrics.tmAscent; mMetrics->maxDescent = metrics.tmDescent; mMetrics->maxAdvance = metrics.tmMaxCharWidth; mMetrics->aveCharWidth = NS_MAX<gfxFloat>(1, metrics.tmAveCharWidth); // The font is monospace when TMPF_FIXED_PITCH is *not* set! // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) { mMetrics->maxAdvance = mMetrics->aveCharWidth; } // Cache the width of a single space. SIZE size; GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size); mMetrics->spaceWidth = ROUND(size.cx); // Cache the width of digit zero. // XXX MSDN (http://msdn.microsoft.com/en-us/library/ms534223.aspx) // does not say what the failure modes for GetTextExtentPoint32 are - // is it safe to assume it will fail iff the font has no '0'? if (GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size)) { mMetrics->zeroOrAveCharWidth = ROUND(size.cx); } else { mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; } mSpaceGlyph = 0; if (metrics.tmPitchAndFamily & TMPF_TRUETYPE) { WORD glyph; DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS); if (ret != GDI_ERROR && glyph != 0xFFFF) { mSpaceGlyph = glyph; } } SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont); } if (IsSyntheticBold()) { mMetrics->aveCharWidth += GetSyntheticBoldOffset(); mMetrics->maxAdvance += GetSyntheticBoldOffset(); } mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont, mFont); cairo_matrix_t sizeMatrix, ctm; cairo_matrix_init_identity(&ctm); cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize); if (useCairoFakeItalic) { // Skew the matrix to do fake italic if it wasn't already applied // via the LOGFONT double skewfactor = OBLIQUE_SKEW_FACTOR; cairo_matrix_t style; cairo_matrix_init(&style, 1, //xx 0, //yx -1 * skewfactor, //xy 1, //yy 0, //x0 0); //y0 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); } cairo_font_options_t *fontOptions = cairo_font_options_create(); if (mAntialiasOption != kAntialiasDefault) { cairo_font_options_set_antialias(fontOptions, GetCairoAntialiasOption(mAntialiasOption)); } mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix, &ctm, fontOptions); cairo_font_options_destroy(fontOptions); if (!mScaledFont || cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) { #ifdef DEBUG char warnBuf[1024]; sprintf(warnBuf, "Failed to create scaled font: %s status: %d", NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0); NS_WARNING(warnBuf); #endif mIsValid = false; } else { mIsValid = true; } #if 0 printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this, NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no")); printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent); printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance); printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading); printf(" spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->xHeight); printf(" uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n", mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize, mMetrics->superscriptOffset, mMetrics->subscriptOffset); #endif }
bool gfxGDIFont::ShapeWord(gfxContext *aContext, gfxShapedWord *aShapedWord, const PRUnichar *aString, bool aPreferPlatformShaping) { if (!mMetrics) { Initialize(); } if (!mIsValid) { NS_WARNING("invalid font! expect incorrect text rendering"); return false; } bool ok = false; // Ensure the cairo font is set up, so there's no risk it'll fall back to // creating a "toy" font internally (see bug 544617). // We must check that this succeeded, otherwise we risk cairo creating the // wrong kind of font internally as a fallback (bug 744480). if (!SetupCairoFont(aContext)) { return false; } #ifdef MOZ_GRAPHITE if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { ok = mGraphiteShaper->ShapeWord(aContext, aShapedWord, aString); } #endif if (!ok && mHarfBuzzShaper) { if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aShapedWord->Script())) { ok = mHarfBuzzShaper->ShapeWord(aContext, aShapedWord, aString); } } if (!ok) { GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry()); bool preferUniscribe = (!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI; if (preferUniscribe || UseUniscribe(aShapedWord, aString)) { // first try Uniscribe if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString); if (ok) { return true; } // fallback to GDI shaping if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString); } else { // first use GDI if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->ShapeWord(aContext, aShapedWord, aString); if (ok) { return true; } // try Uniscribe if GDI failed if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } // use Uniscribe shaping ok = mUniscribeShaper->ShapeWord(aContext, aShapedWord, aString); } #if DEBUG if (!ok) { NS_ConvertUTF16toUTF8 name(GetName()); char msg[256]; sprintf(msg, "text shaping with both uniscribe and GDI failed for" " font: %s", name.get()); NS_WARNING(msg); } #endif } if (ok && IsSyntheticBold()) { float synBoldOffset = GetSyntheticBoldOffset() * CalcXScale(aContext); aShapedWord->AdjustAdvancesForSyntheticBold(synBoldOffset); } return ok; }
PRBool gfxGDIFont::InitTextRun(gfxContext *aContext, gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32 aRunStart, PRUint32 aRunLength, PRInt32 aRunScript) { if (!mMetrics) { Initialize(); } if (!mIsValid) { NS_WARNING("invalid font! expect incorrect text rendering"); return PR_FALSE; } PRBool ok = PR_FALSE; if (mHarfBuzzShaper) { if (gfxPlatform::GetPlatform()->UseHarfBuzzLevel() >= gfxUnicodeProperties::ScriptShapingLevel(aRunScript)) { ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } } if (!ok) { GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry()); PRBool useUniscribeOnly = !fe->IsTrueType() || fe->IsSymbolFont(); if (useUniscribeOnly || (UseUniscribe(aTextRun, aString, aRunStart, aRunLength) && !fe->mForceGDI)) { // first try Uniscribe if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); if (ok) { return PR_TRUE; } // fallback to GDI shaping if (!useUniscribeOnly) { if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } } else { // first use GDI if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); if (ok) { return PR_TRUE; } // first try Uniscribe if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } // use Uniscribe shaping ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } #if DEBUG if (!ok) { NS_ConvertUTF16toUTF8 name(GetName()); char msg[256]; sprintf(msg, "text shaping with both uniscribe and GDI failed for" " font: %s", name.get()); NS_WARNING(msg); } #endif } return ok; }
bool gfxGDIFont::InitTextRun(gfxContext *aContext, gfxTextRun *aTextRun, const PRUnichar *aString, PRUint32 aRunStart, PRUint32 aRunLength, PRInt32 aRunScript, bool aPreferPlatformShaping) { if (!mMetrics) { Initialize(); } if (!mIsValid) { NS_WARNING("invalid font! expect incorrect text rendering"); return false; } bool ok = false; // ensure the cairo font is set up, so there's no risk it'll fall back to // creating a "toy" font internally (see bug 544617) SetupCairoFont(aContext); if (mHarfBuzzShaper) { if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aRunScript)) { ok = mHarfBuzzShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } } if (!ok) { GDIFontEntry *fe = static_cast<GDIFontEntry*>(GetFontEntry()); bool preferUniscribe = (!fe->IsTrueType() || fe->IsSymbolFont()) && !fe->mForceGDI; if (preferUniscribe || UseUniscribe(aTextRun, aString, aRunStart, aRunLength)) { // first try Uniscribe if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); if (ok) { return true; } // fallback to GDI shaping if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } else { // first use GDI if (!mPlatformShaper) { CreatePlatformShaper(); } ok = mPlatformShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); if (ok) { return true; } // try Uniscribe if GDI failed if (!mUniscribeShaper) { mUniscribeShaper = new gfxUniscribeShaper(this); } // use Uniscribe shaping ok = mUniscribeShaper->InitTextRun(aContext, aTextRun, aString, aRunStart, aRunLength, aRunScript); } #if DEBUG if (!ok) { NS_ConvertUTF16toUTF8 name(GetName()); char msg[256]; sprintf(msg, "text shaping with both uniscribe and GDI failed for" " font: %s", name.get()); NS_WARNING(msg); } #endif } return ok; }
void gfxGDIFont::Initialize() { NS_ASSERTION(!mMetrics, "re-creating metrics? this will leak"); LOGFONTW logFont; // Figure out if we want to do synthetic oblique styling. GDIFontEntry* fe = static_cast<GDIFontEntry*>(GetFontEntry()); bool wantFakeItalic = mStyle.style != NS_FONT_STYLE_NORMAL && fe->IsUpright() && mStyle.allowSyntheticStyle; // If the font's family has an actual italic face (but font matching // didn't choose it), we have to use a cairo transform instead of asking // GDI to italicize, because that would use a different face and result // in a possible glyph ID mismatch between shaping and rendering. // // We use the mFamilyHasItalicFace flag in the entry in case of user fonts, // where the *CSS* family may not know about italic faces that are present // in the *GDI* family, and which GDI would use if we asked it to perform // the "italicization". bool useCairoFakeItalic = wantFakeItalic && fe->mFamilyHasItalicFace; if (mAdjustedSize == 0.0) { mAdjustedSize = mStyle.size; if (mStyle.sizeAdjust > 0.0 && mAdjustedSize > 0.0) { // to implement font-size-adjust, we first create the "unadjusted" font FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic); mFont = ::CreateFontIndirectW(&logFont); // initialize its metrics so we can calculate size adjustment Initialize(); // Unless the font was so small that GDI metrics rounded to zero, // calculate the properly adjusted size, and then proceed // to recreate mFont and recalculate metrics if (mMetrics->xHeight > 0.0 && mMetrics->emHeight > 0.0) { gfxFloat aspect = mMetrics->xHeight / mMetrics->emHeight; mAdjustedSize = mStyle.GetAdjustedSize(aspect); } // delete the temporary font and metrics ::DeleteObject(mFont); mFont = nullptr; delete mMetrics; mMetrics = nullptr; } else if (mStyle.sizeAdjust == 0.0) { mAdjustedSize = 0.0; } } // (bug 724231) for local user fonts, we don't use GDI's synthetic bold, // as it could lead to a different, incompatible face being used // but instead do our own multi-striking if (mNeedsBold && GetFontEntry()->IsLocalUserFont()) { mApplySyntheticBold = true; } // this may end up being zero mAdjustedSize = ROUND(mAdjustedSize); FillLogFont(logFont, mAdjustedSize, wantFakeItalic && !useCairoFakeItalic); mFont = ::CreateFontIndirectW(&logFont); mMetrics = new gfxFont::Metrics; ::memset(mMetrics, 0, sizeof(*mMetrics)); if (!mFont) { NS_WARNING("Failed creating GDI font"); mIsValid = false; return; } AutoDC dc; SetGraphicsMode(dc.GetDC(), GM_ADVANCED); AutoSelectFont selectFont(dc.GetDC(), mFont); // Get font metrics if size > 0 if (mAdjustedSize > 0.0) { OUTLINETEXTMETRIC oMetrics; TEXTMETRIC& metrics = oMetrics.otmTextMetrics; if (0 < GetOutlineTextMetrics(dc.GetDC(), sizeof(oMetrics), &oMetrics)) { mMetrics->strikeoutSize = (double)oMetrics.otmsStrikeoutSize; mMetrics->strikeoutOffset = (double)oMetrics.otmsStrikeoutPosition; mMetrics->underlineSize = (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.GetDC(), char16_t('x'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix); if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) { // 56% of ascent, best guess for true type mMetrics->xHeight = ROUND((double)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR); } else { mMetrics->xHeight = gm.gmptGlyphOrigin.y; } len = GetGlyphOutlineW(dc.GetDC(), char16_t('H'), GGO_METRICS, &gm, 0, nullptr, &kIdentityMatrix); if (len == GDI_ERROR || gm.gmptGlyphOrigin.y <= 0) { mMetrics->capHeight = metrics.tmAscent - metrics.tmInternalLeading; } else { mMetrics->capHeight = gm.gmptGlyphOrigin.y; } mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading; gfxFloat typEmHeight = (double)oMetrics.otmAscent - (double)oMetrics.otmDescent; mMetrics->emAscent = ROUND(mMetrics->emHeight * (double)oMetrics.otmAscent / typEmHeight); mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent; if (oMetrics.otmEMSquare > 0) { mFUnitsConvFactor = float(mAdjustedSize / oMetrics.otmEMSquare); } } else { // Make a best-effort guess at extended metrics // this is based on general typographic guidelines // GetTextMetrics can fail if the font file has been removed // or corrupted recently. BOOL result = GetTextMetrics(dc.GetDC(), &metrics); if (!result) { NS_WARNING("Missing or corrupt font data, fasten your seatbelt"); mIsValid = false; memset(mMetrics, 0, sizeof(*mMetrics)); return; } mMetrics->xHeight = ROUND((float)metrics.tmAscent * DEFAULT_XHEIGHT_FACTOR); mMetrics->strikeoutSize = 1; mMetrics->strikeoutOffset = ROUND(mMetrics->xHeight * 0.5f); // 50% of xHeight mMetrics->underlineSize = 1; mMetrics->underlineOffset = -ROUND((float)metrics.tmDescent * 0.30f); // 30% of descent mMetrics->emHeight = metrics.tmHeight - metrics.tmInternalLeading; mMetrics->emAscent = metrics.tmAscent - metrics.tmInternalLeading; mMetrics->emDescent = metrics.tmDescent; mMetrics->capHeight = mMetrics->emAscent; } mMetrics->internalLeading = metrics.tmInternalLeading; mMetrics->externalLeading = metrics.tmExternalLeading; mMetrics->maxHeight = metrics.tmHeight; mMetrics->maxAscent = metrics.tmAscent; mMetrics->maxDescent = metrics.tmDescent; mMetrics->maxAdvance = metrics.tmMaxCharWidth; mMetrics->aveCharWidth = std::max<gfxFloat>(1, metrics.tmAveCharWidth); // The font is monospace when TMPF_FIXED_PITCH is *not* set! // See http://msdn2.microsoft.com/en-us/library/ms534202(VS.85).aspx if (!(metrics.tmPitchAndFamily & TMPF_FIXED_PITCH)) { mMetrics->maxAdvance = mMetrics->aveCharWidth; } // For fonts with USE_TYPO_METRICS set in the fsSelection field, // let the OS/2 sTypo* metrics override the previous values. // (see http://www.microsoft.com/typography/otspec/os2.htm#fss) // Using the equivalent values from oMetrics provides inconsistent // results with CFF fonts, so we instead rely on OS2Table. gfxFontEntry::AutoTable os2Table(mFontEntry, TRUETYPE_TAG('O','S','/','2')); if (os2Table) { uint32_t len; const OS2Table *os2 = reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len)); if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) { const uint16_t kUseTypoMetricsMask = 1 << 7; if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask)) { double ascent = int16_t(os2->sTypoAscender); double descent = int16_t(os2->sTypoDescender); double lineGap = int16_t(os2->sTypoLineGap); mMetrics->maxAscent = ROUND(ascent * mFUnitsConvFactor); mMetrics->maxDescent = -ROUND(descent * mFUnitsConvFactor); mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent; mMetrics->internalLeading = mMetrics->maxHeight - mMetrics->emHeight; gfxFloat lineHeight = ROUND((ascent - descent + lineGap) * mFUnitsConvFactor); lineHeight = std::max(lineHeight, mMetrics->maxHeight); mMetrics->externalLeading = lineHeight - mMetrics->maxHeight; } } // although sxHeight and sCapHeight are signed fields, we consider // negative values to be erroneous and just ignore them if (uint16_t(os2->version) >= 2) { // version 2 and later includes the x-height and cap-height fields if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) && int16_t(os2->sxHeight) > 0) { mMetrics->xHeight = ROUND(int16_t(os2->sxHeight) * mFUnitsConvFactor); } if (len >= offsetof(OS2Table, sCapHeight) + sizeof(int16_t) && int16_t(os2->sCapHeight) > 0) { mMetrics->capHeight = ROUND(int16_t(os2->sCapHeight) * mFUnitsConvFactor); } } } WORD glyph; SIZE size; DWORD ret = GetGlyphIndicesW(dc.GetDC(), L" ", 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS); if (ret != GDI_ERROR && glyph != 0xFFFF) { mSpaceGlyph = glyph; // Cache the width of a single space. GetTextExtentPoint32W(dc.GetDC(), L" ", 1, &size); mMetrics->spaceWidth = ROUND(size.cx); } else { mMetrics->spaceWidth = mMetrics->aveCharWidth; } // Cache the width of digit zero, if available. ret = GetGlyphIndicesW(dc.GetDC(), L"0", 1, &glyph, GGI_MARK_NONEXISTING_GLYPHS); if (ret != GDI_ERROR && glyph != 0xFFFF) { GetTextExtentPoint32W(dc.GetDC(), L"0", 1, &size); mMetrics->zeroOrAveCharWidth = ROUND(size.cx); } else { mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; } SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont); } else { mFUnitsConvFactor = 0.0; // zero-sized font: all values scale to zero } if (IsSyntheticBold()) { mMetrics->aveCharWidth += GetSyntheticBoldOffset(); mMetrics->maxAdvance += GetSyntheticBoldOffset(); } mFontFace = cairo_win32_font_face_create_for_logfontw_hfont(&logFont, mFont); cairo_matrix_t sizeMatrix, ctm; cairo_matrix_init_identity(&ctm); cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize); if (useCairoFakeItalic) { // Skew the matrix to do fake italic if it wasn't already applied // via the LOGFONT double skewfactor = OBLIQUE_SKEW_FACTOR; cairo_matrix_t style; cairo_matrix_init(&style, 1, //xx 0, //yx -1 * skewfactor, //xy 1, //yy 0, //x0 0); //y0 cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); } cairo_font_options_t *fontOptions = cairo_font_options_create(); if (mAntialiasOption != kAntialiasDefault) { cairo_font_options_set_antialias(fontOptions, GetCairoAntialiasOption(mAntialiasOption)); } mScaledFont = cairo_scaled_font_create(mFontFace, &sizeMatrix, &ctm, fontOptions); cairo_font_options_destroy(fontOptions); if (!mScaledFont || cairo_scaled_font_status(mScaledFont) != CAIRO_STATUS_SUCCESS) { #ifdef DEBUG char warnBuf[1024]; SprintfLiteral(warnBuf, "Failed to create scaled font: %s status: %d", NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), mScaledFont ? cairo_scaled_font_status(mScaledFont) : 0); NS_WARNING(warnBuf); #endif mIsValid = false; } else { mIsValid = true; } #if 0 printf("Font: %p (%s) size: %f adjusted size: %f valid: %s\n", this, NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size, mAdjustedSize, (mIsValid ? "yes" : "no")); printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent); printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance); printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading); printf(" spaceWidth: %f aveCharWidth: %f\n", mMetrics->spaceWidth, mMetrics->aveCharWidth); printf(" xHeight: %f capHeight: %f\n", mMetrics->xHeight, mMetrics->capHeight); printf(" uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize); #endif }