michael@0: /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "gfxDWriteFonts.h" michael@0: michael@0: #include "mozilla/MemoryReporting.h" michael@0: michael@0: #include "gfxDWriteShaper.h" michael@0: #include "gfxHarfBuzzShaper.h" michael@0: #include michael@0: #include "gfxGraphiteShaper.h" michael@0: #include "gfxDWriteFontList.h" michael@0: #include "gfxContext.h" michael@0: #include michael@0: michael@0: #include "gfxDWriteTextAnalysis.h" michael@0: michael@0: #include "harfbuzz/hb.h" michael@0: michael@0: // Chosen this as to resemble DWrite's own oblique face style. michael@0: #define OBLIQUE_SKEW_FACTOR 0.3 michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::gfx; michael@0: michael@0: // This is also in gfxGDIFont.cpp. Would be nice to put it somewhere common, michael@0: // but we can't declare it in the gfxFont.h or gfxFontUtils.h headers michael@0: // because those are exported, and the cairo headers aren't. michael@0: static inline cairo_antialias_t michael@0: GetCairoAntialiasOption(gfxFont::AntialiasOption anAntialiasOption) michael@0: { michael@0: switch (anAntialiasOption) { michael@0: default: michael@0: case gfxFont::kAntialiasDefault: michael@0: return CAIRO_ANTIALIAS_DEFAULT; michael@0: case gfxFont::kAntialiasNone: michael@0: return CAIRO_ANTIALIAS_NONE; michael@0: case gfxFont::kAntialiasGrayscale: michael@0: return CAIRO_ANTIALIAS_GRAY; michael@0: case gfxFont::kAntialiasSubpixel: michael@0: return CAIRO_ANTIALIAS_SUBPIXEL; michael@0: } michael@0: } michael@0: michael@0: // Code to determine whether Windows is set to use ClearType font smoothing; michael@0: // based on private functions in cairo-win32-font.c michael@0: michael@0: #ifndef SPI_GETFONTSMOOTHINGTYPE michael@0: #define SPI_GETFONTSMOOTHINGTYPE 0x200a michael@0: #endif michael@0: #ifndef FE_FONTSMOOTHINGCLEARTYPE michael@0: #define FE_FONTSMOOTHINGCLEARTYPE 2 michael@0: #endif michael@0: michael@0: static bool michael@0: UsingClearType() michael@0: { michael@0: BOOL fontSmoothing; michael@0: if (!SystemParametersInfo(SPI_GETFONTSMOOTHING, 0, &fontSmoothing, 0) || michael@0: !fontSmoothing) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: UINT type; michael@0: if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &type, 0) && michael@0: type == FE_FONTSMOOTHINGCLEARTYPE) michael@0: { michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: //////////////////////////////////////////////////////////////////////////////// michael@0: // gfxDWriteFont michael@0: gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry, michael@0: const gfxFontStyle *aFontStyle, michael@0: bool aNeedsBold, michael@0: AntialiasOption anAAOption) michael@0: : gfxFont(aFontEntry, aFontStyle, anAAOption) michael@0: , mCairoFontFace(nullptr) michael@0: , mMetrics(nullptr) michael@0: , mNeedsOblique(false) michael@0: , mNeedsBold(aNeedsBold) michael@0: , mUseSubpixelPositions(false) michael@0: , mAllowManualShowGlyphs(true) michael@0: { michael@0: gfxDWriteFontEntry *fe = michael@0: static_cast(aFontEntry); michael@0: nsresult rv; michael@0: DWRITE_FONT_SIMULATIONS sims = DWRITE_FONT_SIMULATIONS_NONE; michael@0: if ((GetStyle()->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) && michael@0: !fe->IsItalic()) { michael@0: // For this we always use the font_matrix for uniformity. Not the michael@0: // DWrite simulation. michael@0: mNeedsOblique = true; michael@0: } michael@0: if (aNeedsBold) { michael@0: sims |= DWRITE_FONT_SIMULATIONS_BOLD; michael@0: } michael@0: michael@0: rv = fe->CreateFontFace(getter_AddRefs(mFontFace), sims); michael@0: michael@0: if (NS_FAILED(rv)) { michael@0: mIsValid = false; michael@0: return; michael@0: } michael@0: michael@0: ComputeMetrics(anAAOption); michael@0: michael@0: if (FontCanSupportGraphite()) { michael@0: mGraphiteShaper = new gfxGraphiteShaper(this); michael@0: } michael@0: michael@0: if (FontCanSupportHarfBuzz()) { michael@0: mHarfBuzzShaper = new gfxHarfBuzzShaper(this); michael@0: } michael@0: } michael@0: michael@0: gfxDWriteFont::~gfxDWriteFont() michael@0: { michael@0: if (mCairoFontFace) { michael@0: cairo_font_face_destroy(mCairoFontFace); michael@0: } michael@0: if (mScaledFont) { michael@0: cairo_scaled_font_destroy(mScaledFont); michael@0: } michael@0: delete mMetrics; michael@0: } michael@0: michael@0: gfxFont* michael@0: gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption) michael@0: { michael@0: return new gfxDWriteFont(static_cast(mFontEntry.get()), michael@0: &mStyle, mNeedsBold, anAAOption); michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFont::CreatePlatformShaper() michael@0: { michael@0: mPlatformShaper = new gfxDWriteShaper(this); michael@0: } michael@0: michael@0: const gfxFont::Metrics& michael@0: gfxDWriteFont::GetMetrics() michael@0: { michael@0: return *mMetrics; michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFont::GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics) michael@0: { michael@0: gfxFontStyle style(mStyle); michael@0: style.weight = 700; michael@0: bool needsBold; michael@0: michael@0: gfxFontEntry* fe = michael@0: gfxPlatformFontList::PlatformFontList()-> michael@0: FindFontForFamily(NS_LITERAL_STRING("Arial"), &style, needsBold); michael@0: if (!fe || fe == mFontEntry) { michael@0: return false; michael@0: } michael@0: michael@0: nsRefPtr font = fe->FindOrMakeFont(&style, needsBold); michael@0: gfxDWriteFont *dwFont = static_cast(font.get()); michael@0: dwFont->mFontFace->GetMetrics(aFontMetrics); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) michael@0: { michael@0: DWRITE_FONT_METRICS fontMetrics; michael@0: if (!(mFontEntry->Weight() == 900 && michael@0: !mFontEntry->IsUserFont() && michael@0: mFontEntry->Name().EqualsLiteral("Arial Black") && michael@0: GetFakeMetricsForArialBlack(&fontMetrics))) michael@0: { michael@0: mFontFace->GetMetrics(&fontMetrics); michael@0: } michael@0: michael@0: if (mStyle.sizeAdjust != 0.0) { michael@0: gfxFloat aspect = (gfxFloat)fontMetrics.xHeight / michael@0: fontMetrics.designUnitsPerEm; michael@0: mAdjustedSize = mStyle.GetAdjustedSize(aspect); michael@0: } else { michael@0: mAdjustedSize = mStyle.size; michael@0: } michael@0: michael@0: // Note that GetMeasuringMode depends on mAdjustedSize michael@0: if ((anAAOption == gfxFont::kAntialiasDefault && michael@0: UsingClearType() && michael@0: GetMeasuringMode() == DWRITE_MEASURING_MODE_NATURAL) || michael@0: anAAOption == gfxFont::kAntialiasSubpixel) michael@0: { michael@0: mUseSubpixelPositions = true; michael@0: // note that this may be reset to FALSE if we determine that a bitmap michael@0: // strike is going to be used michael@0: } michael@0: michael@0: gfxDWriteFontEntry *fe = michael@0: static_cast(mFontEntry.get()); michael@0: if (fe->IsCJKFont() && HasBitmapStrikeForSize(NS_lround(mAdjustedSize))) { michael@0: mAdjustedSize = NS_lround(mAdjustedSize); michael@0: mUseSubpixelPositions = false; michael@0: // if we have bitmaps, we need to tell Cairo NOT to use subpixel AA, michael@0: // to avoid the manual-subpixel codepath in cairo-d2d-surface.cpp michael@0: // which fails to render bitmap glyphs (see bug 626299). michael@0: // This option will be passed to the cairo_dwrite_scaled_font_t michael@0: // after creation. michael@0: mAllowManualShowGlyphs = false; michael@0: } michael@0: michael@0: mMetrics = new gfxFont::Metrics; michael@0: ::memset(mMetrics, 0, sizeof(*mMetrics)); michael@0: michael@0: mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm); michael@0: michael@0: mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor; michael@0: michael@0: mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor); michael@0: mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor); michael@0: mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent; michael@0: michael@0: mMetrics->emHeight = mAdjustedSize; michael@0: mMetrics->emAscent = mMetrics->emHeight * michael@0: mMetrics->maxAscent / mMetrics->maxHeight; michael@0: mMetrics->emDescent = mMetrics->emHeight - mMetrics->emAscent; michael@0: michael@0: mMetrics->maxAdvance = mAdjustedSize; michael@0: michael@0: // try to get the true maxAdvance value from 'hhea' michael@0: gfxFontEntry::AutoTable hheaTable(GetFontEntry(), michael@0: TRUETYPE_TAG('h','h','e','a')); michael@0: if (hheaTable) { michael@0: uint32_t len; michael@0: const HheaTable* hhea = michael@0: reinterpret_cast(hb_blob_get_data(hheaTable, &len)); michael@0: if (len >= sizeof(HheaTable)) { michael@0: mMetrics->maxAdvance = michael@0: uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor; michael@0: } michael@0: } michael@0: michael@0: mMetrics->internalLeading = std::max(mMetrics->maxHeight - mMetrics->emHeight, 0.0); michael@0: mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor); michael@0: michael@0: UINT16 glyph = (uint16_t)GetSpaceGlyph(); michael@0: mMetrics->spaceWidth = MeasureGlyphWidth(glyph); michael@0: michael@0: // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x' michael@0: // if the table is not available or if using hinted/pixel-snapped widths michael@0: if (mUseSubpixelPositions) { michael@0: mMetrics->aveCharWidth = 0; michael@0: gfxFontEntry::AutoTable os2Table(GetFontEntry(), michael@0: TRUETYPE_TAG('O','S','/','2')); michael@0: if (os2Table) { michael@0: uint32_t len; michael@0: const OS2Table* os2 = michael@0: reinterpret_cast(hb_blob_get_data(os2Table, &len)); michael@0: if (len >= 4) { michael@0: // Not checking against sizeof(mozilla::OS2Table) here because older michael@0: // versions of the table have different sizes; we only need the first michael@0: // two 16-bit fields here. michael@0: mMetrics->aveCharWidth = michael@0: int16_t(os2->xAvgCharWidth) * mFUnitsConvFactor; michael@0: } michael@0: } michael@0: } michael@0: michael@0: UINT32 ucs; michael@0: if (mMetrics->aveCharWidth < 1) { michael@0: ucs = L'x'; michael@0: if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph))) { michael@0: mMetrics->aveCharWidth = MeasureGlyphWidth(glyph); michael@0: } michael@0: if (mMetrics->aveCharWidth < 1) { michael@0: // Let's just assume the X is square. michael@0: mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor; michael@0: } michael@0: } michael@0: michael@0: ucs = L'0'; michael@0: if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph))) { michael@0: mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph); michael@0: } michael@0: if (mMetrics->zeroOrAveCharWidth < 1) { michael@0: mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; michael@0: } michael@0: michael@0: mMetrics->underlineOffset = michael@0: fontMetrics.underlinePosition * mFUnitsConvFactor; michael@0: mMetrics->underlineSize = michael@0: fontMetrics.underlineThickness * mFUnitsConvFactor; michael@0: mMetrics->strikeoutOffset = michael@0: fontMetrics.strikethroughPosition * mFUnitsConvFactor; michael@0: mMetrics->strikeoutSize = michael@0: fontMetrics.strikethroughThickness * mFUnitsConvFactor; michael@0: mMetrics->superscriptOffset = 0; michael@0: mMetrics->subscriptOffset = 0; michael@0: michael@0: SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont); michael@0: michael@0: #if 0 michael@0: printf("Font: %p (%s) size: %f\n", this, michael@0: NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size); michael@0: printf(" emHeight: %f emAscent: %f emDescent: %f\n", mMetrics->emHeight, mMetrics->emAscent, mMetrics->emDescent); michael@0: printf(" maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics->maxAscent, mMetrics->maxDescent, mMetrics->maxAdvance); michael@0: printf(" internalLeading: %f externalLeading: %f\n", mMetrics->internalLeading, mMetrics->externalLeading); michael@0: printf(" spaceWidth: %f aveCharWidth: %f zeroOrAve: %f xHeight: %f\n", michael@0: mMetrics->spaceWidth, mMetrics->aveCharWidth, mMetrics->zeroOrAveCharWidth, mMetrics->xHeight); michael@0: printf(" uOff: %f uSize: %f stOff: %f stSize: %f supOff: %f subOff: %f\n", michael@0: mMetrics->underlineOffset, mMetrics->underlineSize, mMetrics->strikeoutOffset, mMetrics->strikeoutSize, michael@0: mMetrics->superscriptOffset, mMetrics->subscriptOffset); michael@0: #endif michael@0: } michael@0: michael@0: using namespace mozilla; // for AutoSwap_* types michael@0: michael@0: struct EBLCHeader { michael@0: AutoSwap_PRUint32 version; michael@0: AutoSwap_PRUint32 numSizes; michael@0: }; michael@0: michael@0: struct SbitLineMetrics { michael@0: int8_t ascender; michael@0: int8_t descender; michael@0: uint8_t widthMax; michael@0: int8_t caretSlopeNumerator; michael@0: int8_t caretSlopeDenominator; michael@0: int8_t caretOffset; michael@0: int8_t minOriginSB; michael@0: int8_t minAdvanceSB; michael@0: int8_t maxBeforeBL; michael@0: int8_t minAfterBL; michael@0: int8_t pad1; michael@0: int8_t pad2; michael@0: }; michael@0: michael@0: struct BitmapSizeTable { michael@0: AutoSwap_PRUint32 indexSubTableArrayOffset; michael@0: AutoSwap_PRUint32 indexTablesSize; michael@0: AutoSwap_PRUint32 numberOfIndexSubTables; michael@0: AutoSwap_PRUint32 colorRef; michael@0: SbitLineMetrics hori; michael@0: SbitLineMetrics vert; michael@0: AutoSwap_PRUint16 startGlyphIndex; michael@0: AutoSwap_PRUint16 endGlyphIndex; michael@0: uint8_t ppemX; michael@0: uint8_t ppemY; michael@0: uint8_t bitDepth; michael@0: uint8_t flags; michael@0: }; michael@0: michael@0: typedef EBLCHeader EBSCHeader; michael@0: michael@0: struct BitmapScaleTable { michael@0: SbitLineMetrics hori; michael@0: SbitLineMetrics vert; michael@0: uint8_t ppemX; michael@0: uint8_t ppemY; michael@0: uint8_t substitutePpemX; michael@0: uint8_t substitutePpemY; michael@0: }; michael@0: michael@0: bool michael@0: gfxDWriteFont::HasBitmapStrikeForSize(uint32_t aSize) michael@0: { michael@0: uint8_t *tableData; michael@0: uint32_t len; michael@0: void *tableContext; michael@0: BOOL exists; michael@0: HRESULT hr = michael@0: mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'L', 'C'), michael@0: (const void**)&tableData, &len, michael@0: &tableContext, &exists); michael@0: if (FAILED(hr)) { michael@0: return false; michael@0: } michael@0: michael@0: bool hasStrike = false; michael@0: // not really a loop, but this lets us use 'break' to skip out of the block michael@0: // as soon as we know the answer, and skips it altogether if the table is michael@0: // not present michael@0: while (exists) { michael@0: if (len < sizeof(EBLCHeader)) { michael@0: break; michael@0: } michael@0: const EBLCHeader *hdr = reinterpret_cast(tableData); michael@0: if (hdr->version != 0x00020000) { michael@0: break; michael@0: } michael@0: uint32_t numSizes = hdr->numSizes; michael@0: if (numSizes > 0xffff) { // sanity-check, prevent overflow below michael@0: break; michael@0: } michael@0: if (len < sizeof(EBLCHeader) + numSizes * sizeof(BitmapSizeTable)) { michael@0: break; michael@0: } michael@0: const BitmapSizeTable *sizeTable = michael@0: reinterpret_cast(hdr + 1); michael@0: for (uint32_t i = 0; i < numSizes; ++i, ++sizeTable) { michael@0: if (sizeTable->ppemX == aSize && sizeTable->ppemY == aSize) { michael@0: // we ignore a strike that contains fewer than 4 glyphs, michael@0: // as that probably indicates a font such as Courier New michael@0: // that provides bitmaps ONLY for the "shading" characters michael@0: // U+2591..2593 michael@0: hasStrike = (uint16_t(sizeTable->endGlyphIndex) >= michael@0: uint16_t(sizeTable->startGlyphIndex) + 3); michael@0: break; michael@0: } michael@0: } michael@0: // if we reach here, we didn't find a strike; unconditionally break michael@0: // out of the while-loop block michael@0: break; michael@0: } michael@0: mFontFace->ReleaseFontTable(tableContext); michael@0: michael@0: if (hasStrike) { michael@0: return true; michael@0: } michael@0: michael@0: // if we didn't find a real strike, check if the font calls for scaling michael@0: // another bitmap to this size michael@0: hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('E', 'B', 'S', 'C'), michael@0: (const void**)&tableData, &len, michael@0: &tableContext, &exists); michael@0: if (FAILED(hr)) { michael@0: return false; michael@0: } michael@0: michael@0: while (exists) { michael@0: if (len < sizeof(EBSCHeader)) { michael@0: break; michael@0: } michael@0: const EBSCHeader *hdr = reinterpret_cast(tableData); michael@0: if (hdr->version != 0x00020000) { michael@0: break; michael@0: } michael@0: uint32_t numSizes = hdr->numSizes; michael@0: if (numSizes > 0xffff) { michael@0: break; michael@0: } michael@0: if (len < sizeof(EBSCHeader) + numSizes * sizeof(BitmapScaleTable)) { michael@0: break; michael@0: } michael@0: const BitmapScaleTable *scaleTable = michael@0: reinterpret_cast(hdr + 1); michael@0: for (uint32_t i = 0; i < numSizes; ++i, ++scaleTable) { michael@0: if (scaleTable->ppemX == aSize && scaleTable->ppemY == aSize) { michael@0: hasStrike = true; michael@0: break; michael@0: } michael@0: } michael@0: break; michael@0: } michael@0: mFontFace->ReleaseFontTable(tableContext); michael@0: michael@0: return hasStrike; michael@0: } michael@0: michael@0: uint32_t michael@0: gfxDWriteFont::GetSpaceGlyph() michael@0: { michael@0: UINT32 ucs = L' '; michael@0: UINT16 glyph; michael@0: HRESULT hr; michael@0: hr = mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph); michael@0: if (FAILED(hr)) { michael@0: return 0; michael@0: } michael@0: return glyph; michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFont::SetupCairoFont(gfxContext *aContext) michael@0: { michael@0: cairo_scaled_font_t *scaledFont = GetCairoScaledFont(); michael@0: if (cairo_scaled_font_status(scaledFont) != CAIRO_STATUS_SUCCESS) { michael@0: // Don't cairo_set_scaled_font as that would propagate the error to michael@0: // the cairo_t, precluding any further drawing. michael@0: return false; michael@0: } michael@0: cairo_set_scaled_font(aContext->GetCairo(), scaledFont); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFont::IsValid() michael@0: { michael@0: return mFontFace != nullptr; michael@0: } michael@0: michael@0: IDWriteFontFace* michael@0: gfxDWriteFont::GetFontFace() michael@0: { michael@0: return mFontFace.get(); michael@0: } michael@0: michael@0: cairo_font_face_t * michael@0: gfxDWriteFont::CairoFontFace() michael@0: { michael@0: if (!mCairoFontFace) { michael@0: #ifdef CAIRO_HAS_DWRITE_FONT michael@0: mCairoFontFace = michael@0: cairo_dwrite_font_face_create_for_dwrite_fontface( michael@0: ((gfxDWriteFontEntry*)mFontEntry.get())->mFont, mFontFace); michael@0: #endif michael@0: } michael@0: return mCairoFontFace; michael@0: } michael@0: michael@0: michael@0: cairo_scaled_font_t * michael@0: gfxDWriteFont::GetCairoScaledFont() michael@0: { michael@0: if (!mScaledFont) { michael@0: cairo_matrix_t sizeMatrix; michael@0: cairo_matrix_t identityMatrix; michael@0: michael@0: cairo_matrix_init_scale(&sizeMatrix, mAdjustedSize, mAdjustedSize); michael@0: cairo_matrix_init_identity(&identityMatrix); michael@0: michael@0: cairo_font_options_t *fontOptions = cairo_font_options_create(); michael@0: if (mNeedsOblique) { michael@0: double skewfactor = OBLIQUE_SKEW_FACTOR; michael@0: michael@0: cairo_matrix_t style; michael@0: cairo_matrix_init(&style, michael@0: 1, //xx michael@0: 0, //yx michael@0: -1 * skewfactor, //xy michael@0: 1, //yy michael@0: 0, //x0 michael@0: 0); //y0 michael@0: cairo_matrix_multiply(&sizeMatrix, &sizeMatrix, &style); michael@0: } michael@0: michael@0: if (mAntialiasOption != kAntialiasDefault) { michael@0: cairo_font_options_set_antialias(fontOptions, michael@0: GetCairoAntialiasOption(mAntialiasOption)); michael@0: } michael@0: michael@0: mScaledFont = cairo_scaled_font_create(CairoFontFace(), michael@0: &sizeMatrix, michael@0: &identityMatrix, michael@0: fontOptions); michael@0: cairo_font_options_destroy(fontOptions); michael@0: michael@0: cairo_dwrite_scaled_font_allow_manual_show_glyphs(mScaledFont, michael@0: mAllowManualShowGlyphs); michael@0: michael@0: gfxDWriteFontEntry *fe = michael@0: static_cast(mFontEntry.get()); michael@0: cairo_dwrite_scaled_font_set_force_GDI_classic(mScaledFont, michael@0: GetForceGDIClassic()); michael@0: } michael@0: michael@0: NS_ASSERTION(mAdjustedSize == 0.0 || michael@0: cairo_scaled_font_status(mScaledFont) michael@0: == CAIRO_STATUS_SUCCESS, michael@0: "Failed to make scaled font"); michael@0: michael@0: return mScaledFont; michael@0: } michael@0: michael@0: gfxFont::RunMetrics michael@0: gfxDWriteFont::Measure(gfxTextRun *aTextRun, michael@0: uint32_t aStart, uint32_t aEnd, michael@0: BoundingBoxType aBoundingBoxType, michael@0: gfxContext *aRefContext, michael@0: Spacing *aSpacing) michael@0: { michael@0: gfxFont::RunMetrics metrics = michael@0: gfxFont::Measure(aTextRun, aStart, aEnd, michael@0: aBoundingBoxType, aRefContext, aSpacing); michael@0: michael@0: // if aBoundingBoxType is LOOSE_INK_EXTENTS michael@0: // and the underlying cairo font may be antialiased, michael@0: // we can't trust Windows to have considered all the pixels michael@0: // so we need to add "padding" to the bounds. michael@0: // (see bugs 475968, 439831, compare also bug 445087) michael@0: if (aBoundingBoxType == LOOSE_INK_EXTENTS && michael@0: mAntialiasOption != kAntialiasNone && michael@0: GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_CLASSIC && michael@0: metrics.mBoundingBox.width > 0) { michael@0: metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit(); michael@0: metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 3; michael@0: } michael@0: michael@0: return metrics; michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFont::ProvidesGlyphWidths() michael@0: { michael@0: return !mUseSubpixelPositions || michael@0: (mFontFace->GetSimulations() & DWRITE_FONT_SIMULATIONS_BOLD); michael@0: } michael@0: michael@0: int32_t michael@0: gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, uint16_t aGID) michael@0: { michael@0: if (!mGlyphWidths) { michael@0: mGlyphWidths = new nsDataHashtable(200); michael@0: } michael@0: michael@0: int32_t width = -1; michael@0: if (mGlyphWidths->Get(aGID, &width)) { michael@0: return width; michael@0: } michael@0: michael@0: width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0); michael@0: mGlyphWidths->Put(aGID, width); michael@0: return width; michael@0: } michael@0: michael@0: TemporaryRef michael@0: gfxDWriteFont::GetGlyphRenderingOptions() michael@0: { michael@0: if (UsingClearType()) { michael@0: return Factory::CreateDWriteGlyphRenderingOptions( michael@0: gfxWindowsPlatform::GetPlatform()->GetRenderingParams(GetForceGDIClassic() ? michael@0: gfxWindowsPlatform::TEXT_RENDERING_GDI_CLASSIC : gfxWindowsPlatform::TEXT_RENDERING_NORMAL)); michael@0: } else { michael@0: return Factory::CreateDWriteGlyphRenderingOptions(gfxWindowsPlatform::GetPlatform()-> michael@0: GetRenderingParams(gfxWindowsPlatform::TEXT_RENDERING_NO_CLEARTYPE)); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: gfxDWriteFont::GetForceGDIClassic() michael@0: { michael@0: return static_cast(mFontEntry.get())->GetForceGDIClassic() && michael@0: cairo_dwrite_get_cleartype_rendering_mode() < 0 && michael@0: GetAdjustedSize() <= michael@0: gfxDWriteFontList::PlatformFontList()->GetForceGDIClassicMaxFontSize(); michael@0: } michael@0: michael@0: DWRITE_MEASURING_MODE michael@0: gfxDWriteFont::GetMeasuringMode() michael@0: { michael@0: return GetForceGDIClassic() michael@0: ? DWRITE_MEASURING_MODE_GDI_CLASSIC michael@0: : gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode(); michael@0: } michael@0: michael@0: gfxFloat michael@0: gfxDWriteFont::MeasureGlyphWidth(uint16_t aGlyph) michael@0: { michael@0: DWRITE_GLYPH_METRICS metrics; michael@0: HRESULT hr; michael@0: if (mUseSubpixelPositions) { michael@0: hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE); michael@0: if (SUCCEEDED(hr)) { michael@0: return metrics.advanceWidth * mFUnitsConvFactor; michael@0: } michael@0: } else { michael@0: hr = mFontFace->GetGdiCompatibleGlyphMetrics( michael@0: FLOAT(mAdjustedSize), 1.0f, nullptr, michael@0: GetMeasuringMode() == DWRITE_MEASURING_MODE_GDI_NATURAL, michael@0: &aGlyph, 1, &metrics, FALSE); michael@0: if (SUCCEEDED(hr)) { michael@0: return NS_lround(metrics.advanceWidth * mFUnitsConvFactor); michael@0: } michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const michael@0: { michael@0: gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: aSizes->mFontInstances += aMallocSizeOf(mMetrics); michael@0: if (mGlyphWidths) { michael@0: aSizes->mFontInstances += michael@0: mGlyphWidths->SizeOfExcludingThis(nullptr, aMallocSizeOf); michael@0: } michael@0: } michael@0: michael@0: void michael@0: gfxDWriteFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, michael@0: FontCacheSizes* aSizes) const michael@0: { michael@0: aSizes->mFontInstances += aMallocSizeOf(this); michael@0: AddSizeOfExcludingThis(aMallocSizeOf, aSizes); michael@0: } michael@0: michael@0: TemporaryRef michael@0: gfxDWriteFont::GetScaledFont(mozilla::gfx::DrawTarget *aTarget) michael@0: { michael@0: bool wantCairo = aTarget->GetType() == BackendType::CAIRO; michael@0: if (mAzureScaledFont && mAzureScaledFontIsCairo == wantCairo) { michael@0: return mAzureScaledFont; michael@0: } michael@0: michael@0: NativeFont nativeFont; michael@0: nativeFont.mType = NativeFontType::DWRITE_FONT_FACE; michael@0: nativeFont.mFont = GetFontFace(); michael@0: michael@0: if (wantCairo) { michael@0: mAzureScaledFont = Factory::CreateScaledFontWithCairo(nativeFont, michael@0: GetAdjustedSize(), michael@0: GetCairoScaledFont()); michael@0: } else { michael@0: mAzureScaledFont = Factory::CreateScaledFontForNativeFont(nativeFont, michael@0: GetAdjustedSize()); michael@0: } michael@0: michael@0: mAzureScaledFontIsCairo = wantCairo; michael@0: michael@0: return mAzureScaledFont; michael@0: }