1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/thebes/gfxFont.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,7446 @@ 1.4 +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#include "mozilla/DebugOnly.h" 1.10 +#include "mozilla/MathAlgorithms.h" 1.11 + 1.12 +#ifdef MOZ_LOGGING 1.13 +#define FORCE_PR_LOG /* Allow logging in the release build */ 1.14 +#endif 1.15 +#include "prlog.h" 1.16 + 1.17 +#include "nsServiceManagerUtils.h" 1.18 +#include "nsExpirationTracker.h" 1.19 +#include "nsILanguageAtomService.h" 1.20 +#include "nsITimer.h" 1.21 + 1.22 +#include "gfxFont.h" 1.23 +#include "gfxPlatform.h" 1.24 +#include "nsGkAtoms.h" 1.25 + 1.26 +#include "gfxTypes.h" 1.27 +#include "gfxContext.h" 1.28 +#include "gfxFontMissingGlyphs.h" 1.29 +#include "gfxHarfBuzzShaper.h" 1.30 +#include "gfxUserFontSet.h" 1.31 +#include "gfxPlatformFontList.h" 1.32 +#include "gfxScriptItemizer.h" 1.33 +#include "nsUnicodeProperties.h" 1.34 +#include "nsMathUtils.h" 1.35 +#include "nsBidiUtils.h" 1.36 +#include "nsUnicodeRange.h" 1.37 +#include "nsStyleConsts.h" 1.38 +#include "mozilla/FloatingPoint.h" 1.39 +#include "mozilla/Likely.h" 1.40 +#include "mozilla/MemoryReporting.h" 1.41 +#include "mozilla/Preferences.h" 1.42 +#include "mozilla/Services.h" 1.43 +#include "mozilla/Telemetry.h" 1.44 +#include "gfxSVGGlyphs.h" 1.45 +#include "gfxMathTable.h" 1.46 +#include "gfx2DGlue.h" 1.47 + 1.48 +#if defined(XP_MACOSX) 1.49 +#include "nsCocoaFeatures.h" 1.50 +#endif 1.51 + 1.52 +#include "cairo.h" 1.53 +#include "gfxFontTest.h" 1.54 + 1.55 +#include "harfbuzz/hb.h" 1.56 +#include "harfbuzz/hb-ot.h" 1.57 +#include "graphite2/Font.h" 1.58 + 1.59 +#include "nsCRT.h" 1.60 +#include "GeckoProfiler.h" 1.61 +#include "gfxFontConstants.h" 1.62 + 1.63 +#include <algorithm> 1.64 + 1.65 +using namespace mozilla; 1.66 +using namespace mozilla::gfx; 1.67 +using namespace mozilla::unicode; 1.68 +using mozilla::services::GetObserverService; 1.69 + 1.70 +gfxFontCache *gfxFontCache::gGlobalCache = nullptr; 1.71 + 1.72 +static const char16_t kEllipsisChar[] = { 0x2026, 0x0 }; 1.73 +static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 }; 1.74 + 1.75 +#ifdef DEBUG_roc 1.76 +#define DEBUG_TEXT_RUN_STORAGE_METRICS 1.77 +#endif 1.78 + 1.79 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.80 +static uint32_t gTextRunStorageHighWaterMark = 0; 1.81 +static uint32_t gTextRunStorage = 0; 1.82 +static uint32_t gFontCount = 0; 1.83 +static uint32_t gGlyphExtentsCount = 0; 1.84 +static uint32_t gGlyphExtentsWidthsTotalSize = 0; 1.85 +static uint32_t gGlyphExtentsSetupEagerSimple = 0; 1.86 +static uint32_t gGlyphExtentsSetupEagerTight = 0; 1.87 +static uint32_t gGlyphExtentsSetupLazyTight = 0; 1.88 +static uint32_t gGlyphExtentsSetupFallBackToTight = 0; 1.89 +#endif 1.90 + 1.91 +#ifdef PR_LOGGING 1.92 +#define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \ 1.93 + PR_LOG_DEBUG, args) 1.94 +#define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \ 1.95 + gfxPlatform::GetLog(eGfxLog_fontinit), \ 1.96 + PR_LOG_DEBUG) 1.97 +#endif // PR_LOGGING 1.98 + 1.99 +void 1.100 +gfxCharacterMap::NotifyReleased() 1.101 +{ 1.102 + gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList(); 1.103 + if (mShared) { 1.104 + fontlist->RemoveCmap(this); 1.105 + } 1.106 + delete this; 1.107 +} 1.108 + 1.109 +gfxFontEntry::gfxFontEntry() : 1.110 + mItalic(false), mFixedPitch(false), 1.111 + mIsProxy(false), mIsValid(true), 1.112 + mIsBadUnderlineFont(false), 1.113 + mIsUserFont(false), 1.114 + mIsLocalUserFont(false), 1.115 + mStandardFace(false), 1.116 + mSymbolFont(false), 1.117 + mIgnoreGDEF(false), 1.118 + mIgnoreGSUB(false), 1.119 + mSVGInitialized(false), 1.120 + mMathInitialized(false), 1.121 + mHasSpaceFeaturesInitialized(false), 1.122 + mHasSpaceFeatures(false), 1.123 + mHasSpaceFeaturesKerning(false), 1.124 + mHasSpaceFeaturesNonKerning(false), 1.125 + mSkipDefaultFeatureSpaceCheck(false), 1.126 + mCheckedForGraphiteTables(false), 1.127 + mHasCmapTable(false), 1.128 + mGrFaceInitialized(false), 1.129 + mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL), 1.130 + mUVSOffset(0), mUVSData(nullptr), 1.131 + mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE), 1.132 + mUnitsPerEm(0), 1.133 + mHBFace(nullptr), 1.134 + mGrFace(nullptr), 1.135 + mGrFaceRefCnt(0) 1.136 +{ 1.137 + memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures)); 1.138 + memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures)); 1.139 +} 1.140 + 1.141 +gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) : 1.142 + mName(aName), mItalic(false), mFixedPitch(false), 1.143 + mIsProxy(false), mIsValid(true), 1.144 + mIsBadUnderlineFont(false), mIsUserFont(false), 1.145 + mIsLocalUserFont(false), mStandardFace(aIsStandardFace), 1.146 + mSymbolFont(false), 1.147 + mIgnoreGDEF(false), 1.148 + mIgnoreGSUB(false), 1.149 + mSVGInitialized(false), 1.150 + mMathInitialized(false), 1.151 + mHasSpaceFeaturesInitialized(false), 1.152 + mHasSpaceFeatures(false), 1.153 + mHasSpaceFeaturesKerning(false), 1.154 + mHasSpaceFeaturesNonKerning(false), 1.155 + mSkipDefaultFeatureSpaceCheck(false), 1.156 + mCheckedForGraphiteTables(false), 1.157 + mHasCmapTable(false), 1.158 + mGrFaceInitialized(false), 1.159 + mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL), 1.160 + mUVSOffset(0), mUVSData(nullptr), 1.161 + mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE), 1.162 + mUnitsPerEm(0), 1.163 + mHBFace(nullptr), 1.164 + mGrFace(nullptr), 1.165 + mGrFaceRefCnt(0) 1.166 +{ 1.167 + memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures)); 1.168 + memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures)); 1.169 +} 1.170 + 1.171 +gfxFontEntry::~gfxFontEntry() 1.172 +{ 1.173 + // For downloaded fonts, we need to tell the user font cache that this 1.174 + // entry is being deleted. 1.175 + if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) { 1.176 + gfxUserFontSet::UserFontCache::ForgetFont(this); 1.177 + } 1.178 + 1.179 + // By the time the entry is destroyed, all font instances that were 1.180 + // using it should already have been deleted, and so the HB and/or Gr 1.181 + // face objects should have been released. 1.182 + MOZ_ASSERT(!mHBFace); 1.183 + MOZ_ASSERT(!mGrFaceInitialized); 1.184 +} 1.185 + 1.186 +bool gfxFontEntry::IsSymbolFont() 1.187 +{ 1.188 + return mSymbolFont; 1.189 +} 1.190 + 1.191 +bool gfxFontEntry::TestCharacterMap(uint32_t aCh) 1.192 +{ 1.193 + if (!mCharacterMap) { 1.194 + ReadCMAP(); 1.195 + NS_ASSERTION(mCharacterMap, "failed to initialize character map"); 1.196 + } 1.197 + return mCharacterMap->test(aCh); 1.198 +} 1.199 + 1.200 +nsresult gfxFontEntry::InitializeUVSMap() 1.201 +{ 1.202 + // mUVSOffset will not be initialized 1.203 + // until cmap is initialized. 1.204 + if (!mCharacterMap) { 1.205 + ReadCMAP(); 1.206 + NS_ASSERTION(mCharacterMap, "failed to initialize character map"); 1.207 + } 1.208 + 1.209 + if (!mUVSOffset) { 1.210 + return NS_ERROR_FAILURE; 1.211 + } 1.212 + 1.213 + if (!mUVSData) { 1.214 + const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p'); 1.215 + AutoTable cmapTable(this, kCmapTag); 1.216 + if (!cmapTable) { 1.217 + mUVSOffset = 0; // don't bother to read the table again 1.218 + return NS_ERROR_FAILURE; 1.219 + } 1.220 + 1.221 + uint8_t* uvsData; 1.222 + unsigned int cmapLen; 1.223 + const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen); 1.224 + nsresult rv = gfxFontUtils::ReadCMAPTableFormat14( 1.225 + (const uint8_t*)cmapData + mUVSOffset, 1.226 + cmapLen - mUVSOffset, uvsData); 1.227 + 1.228 + if (NS_FAILED(rv)) { 1.229 + mUVSOffset = 0; // don't bother to read the table again 1.230 + return rv; 1.231 + } 1.232 + 1.233 + mUVSData = uvsData; 1.234 + } 1.235 + 1.236 + return NS_OK; 1.237 +} 1.238 + 1.239 +uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS) 1.240 +{ 1.241 + InitializeUVSMap(); 1.242 + 1.243 + if (mUVSData) { 1.244 + return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS); 1.245 + } 1.246 + 1.247 + return 0; 1.248 +} 1.249 + 1.250 +bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags) 1.251 +{ 1.252 + hb_face_t *face = GetHBFace(); 1.253 + if (!face) { 1.254 + return false; 1.255 + } 1.256 + 1.257 + unsigned int index; 1.258 + hb_tag_t chosenScript; 1.259 + bool found = 1.260 + hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'), 1.261 + aScriptTags, &index, &chosenScript); 1.262 + hb_face_destroy(face); 1.263 + 1.264 + return found && chosenScript != TRUETYPE_TAG('D','F','L','T'); 1.265 +} 1.266 + 1.267 +nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData) 1.268 +{ 1.269 + NS_ASSERTION(false, "using default no-op implementation of ReadCMAP"); 1.270 + mCharacterMap = new gfxCharacterMap(); 1.271 + return NS_OK; 1.272 +} 1.273 + 1.274 +nsString 1.275 +gfxFontEntry::RealFaceName() 1.276 +{ 1.277 + AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e')); 1.278 + if (nameTable) { 1.279 + nsAutoString name; 1.280 + nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name); 1.281 + if (NS_SUCCEEDED(rv)) { 1.282 + return name; 1.283 + } 1.284 + } 1.285 + return Name(); 1.286 +} 1.287 + 1.288 +already_AddRefed<gfxFont> 1.289 +gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold) 1.290 +{ 1.291 + // the font entry name is the psname, not the family name 1.292 + nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle); 1.293 + 1.294 + if (!font) { 1.295 + gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold); 1.296 + if (!newFont) 1.297 + return nullptr; 1.298 + if (!newFont->Valid()) { 1.299 + delete newFont; 1.300 + return nullptr; 1.301 + } 1.302 + font = newFont; 1.303 + gfxFontCache::GetCache()->AddNew(font); 1.304 + } 1.305 + return font.forget(); 1.306 +} 1.307 + 1.308 +uint16_t 1.309 +gfxFontEntry::UnitsPerEm() 1.310 +{ 1.311 + if (!mUnitsPerEm) { 1.312 + AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d')); 1.313 + if (headTable) { 1.314 + uint32_t len; 1.315 + const HeadTable* head = 1.316 + reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable, 1.317 + &len)); 1.318 + if (len >= sizeof(HeadTable)) { 1.319 + mUnitsPerEm = head->unitsPerEm; 1.320 + } 1.321 + } 1.322 + 1.323 + // if we didn't find a usable 'head' table, or if the value was 1.324 + // outside the valid range, record it as invalid 1.325 + if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) { 1.326 + mUnitsPerEm = kInvalidUPEM; 1.327 + } 1.328 + } 1.329 + return mUnitsPerEm; 1.330 +} 1.331 + 1.332 +bool 1.333 +gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId) 1.334 +{ 1.335 + NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first."); 1.336 + return mSVGGlyphs->HasSVGGlyph(aGlyphId); 1.337 +} 1.338 + 1.339 +bool 1.340 +gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId, 1.341 + gfxRect *aResult) 1.342 +{ 1.343 + NS_ABORT_IF_FALSE(mSVGInitialized, 1.344 + "SVG data has not yet been loaded. TryGetSVGData() first."); 1.345 + NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM, 1.346 + "font has invalid unitsPerEm"); 1.347 + 1.348 + gfxContextAutoSaveRestore matrixRestore(aContext); 1.349 + cairo_matrix_t fontMatrix; 1.350 + cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix); 1.351 + 1.352 + gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix); 1.353 + svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm); 1.354 + 1.355 + return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult); 1.356 +} 1.357 + 1.358 +bool 1.359 +gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId, 1.360 + int aDrawMode, gfxTextContextPaint *aContextPaint) 1.361 +{ 1.362 + NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first."); 1.363 + return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode), 1.364 + aContextPaint); 1.365 +} 1.366 + 1.367 +bool 1.368 +gfxFontEntry::TryGetSVGData(gfxFont* aFont) 1.369 +{ 1.370 + if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) { 1.371 + return false; 1.372 + } 1.373 + 1.374 + if (!mSVGInitialized) { 1.375 + mSVGInitialized = true; 1.376 + 1.377 + // If UnitsPerEm is not known/valid, we can't use SVG glyphs 1.378 + if (UnitsPerEm() == kInvalidUPEM) { 1.379 + return false; 1.380 + } 1.381 + 1.382 + // We don't use AutoTable here because we'll pass ownership of this 1.383 + // blob to the gfxSVGGlyphs, once we've confirmed the table exists 1.384 + hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' ')); 1.385 + if (!svgTable) { 1.386 + return false; 1.387 + } 1.388 + 1.389 + // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished 1.390 + // with it. 1.391 + mSVGGlyphs = new gfxSVGGlyphs(svgTable, this); 1.392 + } 1.393 + 1.394 + if (!mFontsUsingSVGGlyphs.Contains(aFont)) { 1.395 + mFontsUsingSVGGlyphs.AppendElement(aFont); 1.396 + } 1.397 + 1.398 + return !!mSVGGlyphs; 1.399 +} 1.400 + 1.401 +void 1.402 +gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont) 1.403 +{ 1.404 + mFontsUsingSVGGlyphs.RemoveElement(aFont); 1.405 +} 1.406 + 1.407 +void 1.408 +gfxFontEntry::NotifyGlyphsChanged() 1.409 +{ 1.410 + for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) { 1.411 + gfxFont* font = mFontsUsingSVGGlyphs[i]; 1.412 + font->NotifyGlyphsChanged(); 1.413 + } 1.414 +} 1.415 + 1.416 +bool 1.417 +gfxFontEntry::TryGetMathTable(gfxFont* aFont) 1.418 +{ 1.419 + if (!mMathInitialized) { 1.420 + mMathInitialized = true; 1.421 + 1.422 + // If UnitsPerEm is not known/valid, we can't use MATH table 1.423 + if (UnitsPerEm() == kInvalidUPEM) { 1.424 + return false; 1.425 + } 1.426 + 1.427 + // We don't use AutoTable here because we'll pass ownership of this 1.428 + // blob to the gfxMathTable, once we've confirmed the table exists 1.429 + hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H')); 1.430 + if (!mathTable) { 1.431 + return false; 1.432 + } 1.433 + 1.434 + // gfxMathTable will hb_blob_destroy() the table when it is finished 1.435 + // with it. 1.436 + mMathTable = new gfxMathTable(mathTable); 1.437 + if (!mMathTable->HasValidHeaders()) { 1.438 + mMathTable = nullptr; 1.439 + return false; 1.440 + } 1.441 + } 1.442 + 1.443 + return !!mMathTable; 1.444 +} 1.445 + 1.446 +gfxFloat 1.447 +gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant) 1.448 +{ 1.449 + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); 1.450 + gfxFloat value = mMathTable->GetMathConstant(aConstant); 1.451 + if (aConstant == gfxFontEntry::ScriptPercentScaleDown || 1.452 + aConstant == gfxFontEntry::ScriptScriptPercentScaleDown || 1.453 + aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) { 1.454 + return value / 100.0; 1.455 + } 1.456 + return value / mUnitsPerEm; 1.457 +} 1.458 + 1.459 +bool 1.460 +gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID, 1.461 + gfxFloat* aItalicCorrection) 1.462 +{ 1.463 + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); 1.464 + int16_t italicCorrection; 1.465 + if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) { 1.466 + return false; 1.467 + } 1.468 + *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm; 1.469 + return true; 1.470 +} 1.471 + 1.472 +uint32_t 1.473 +gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical, 1.474 + uint16_t aSize) 1.475 +{ 1.476 + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); 1.477 + return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize); 1.478 +} 1.479 + 1.480 +bool 1.481 +gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical, 1.482 + uint32_t aGlyphs[4]) 1.483 +{ 1.484 + NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first."); 1.485 + return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs); 1.486 +} 1.487 + 1.488 +/** 1.489 + * FontTableBlobData 1.490 + * 1.491 + * See FontTableHashEntry for the general strategy. 1.492 + */ 1.493 + 1.494 +class gfxFontEntry::FontTableBlobData { 1.495 +public: 1.496 + // Adopts the content of aBuffer. 1.497 + FontTableBlobData(FallibleTArray<uint8_t>& aBuffer) 1.498 + : mHashtable(nullptr), mHashKey(0) 1.499 + { 1.500 + MOZ_COUNT_CTOR(FontTableBlobData); 1.501 + mTableData.SwapElements(aBuffer); 1.502 + } 1.503 + 1.504 + ~FontTableBlobData() { 1.505 + MOZ_COUNT_DTOR(FontTableBlobData); 1.506 + if (mHashtable && mHashKey) { 1.507 + mHashtable->RemoveEntry(mHashKey); 1.508 + } 1.509 + } 1.510 + 1.511 + // Useful for creating blobs 1.512 + const char *GetTable() const 1.513 + { 1.514 + return reinterpret_cast<const char*>(mTableData.Elements()); 1.515 + } 1.516 + uint32_t GetTableLength() const { return mTableData.Length(); } 1.517 + 1.518 + // Tell this FontTableBlobData to remove the HashEntry when this is 1.519 + // destroyed. 1.520 + void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable, 1.521 + uint32_t aHashKey) 1.522 + { 1.523 + mHashtable = aHashtable; 1.524 + mHashKey = aHashKey; 1.525 + } 1.526 + 1.527 + // Disconnect from the HashEntry (because the blob has already been 1.528 + // removed from the hashtable). 1.529 + void ForgetHashEntry() 1.530 + { 1.531 + mHashtable = nullptr; 1.532 + mHashKey = 0; 1.533 + } 1.534 + 1.535 + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 1.536 + return mTableData.SizeOfExcludingThis(aMallocSizeOf); 1.537 + } 1.538 + size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 1.539 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.540 + } 1.541 + 1.542 +private: 1.543 + // The font table data block, owned (via adoption) 1.544 + FallibleTArray<uint8_t> mTableData; 1.545 + 1.546 + // The blob destroy function needs to know the owning hashtable 1.547 + // and the hashtable key, so that it can remove the entry. 1.548 + nsTHashtable<FontTableHashEntry> *mHashtable; 1.549 + uint32_t mHashKey; 1.550 + 1.551 + // not implemented 1.552 + FontTableBlobData(const FontTableBlobData&); 1.553 +}; 1.554 + 1.555 +hb_blob_t * 1.556 +gfxFontEntry::FontTableHashEntry:: 1.557 +ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable, 1.558 + nsTHashtable<FontTableHashEntry> *aHashtable) 1.559 +{ 1.560 + Clear(); 1.561 + // adopts elements of aTable 1.562 + mSharedBlobData = new FontTableBlobData(aTable); 1.563 + mBlob = hb_blob_create(mSharedBlobData->GetTable(), 1.564 + mSharedBlobData->GetTableLength(), 1.565 + HB_MEMORY_MODE_READONLY, 1.566 + mSharedBlobData, DeleteFontTableBlobData); 1.567 + if (!mSharedBlobData) { 1.568 + // The FontTableBlobData was destroyed during hb_blob_create(). 1.569 + // The (empty) blob is still be held in the hashtable with a strong 1.570 + // reference. 1.571 + return hb_blob_reference(mBlob); 1.572 + } 1.573 + 1.574 + // Tell the FontTableBlobData to remove this hash entry when destroyed. 1.575 + // The hashtable does not keep a strong reference. 1.576 + mSharedBlobData->ManageHashEntry(aHashtable, GetKey()); 1.577 + return mBlob; 1.578 +} 1.579 + 1.580 +void 1.581 +gfxFontEntry::FontTableHashEntry::Clear() 1.582 +{ 1.583 + // If the FontTableBlobData is managing the hash entry, then the blob is 1.584 + // not owned by this HashEntry; otherwise there is strong reference to the 1.585 + // blob that must be removed. 1.586 + if (mSharedBlobData) { 1.587 + mSharedBlobData->ForgetHashEntry(); 1.588 + mSharedBlobData = nullptr; 1.589 + } else if (mBlob) { 1.590 + hb_blob_destroy(mBlob); 1.591 + } 1.592 + mBlob = nullptr; 1.593 +} 1.594 + 1.595 +// a hb_destroy_func for hb_blob_create 1.596 + 1.597 +/* static */ void 1.598 +gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData) 1.599 +{ 1.600 + delete static_cast<FontTableBlobData*>(aBlobData); 1.601 +} 1.602 + 1.603 +hb_blob_t * 1.604 +gfxFontEntry::FontTableHashEntry::GetBlob() const 1.605 +{ 1.606 + return hb_blob_reference(mBlob); 1.607 +} 1.608 + 1.609 +bool 1.610 +gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob) 1.611 +{ 1.612 + if (!mFontTableCache) { 1.613 + // we do this here rather than on fontEntry construction 1.614 + // because not all shapers will access the table cache at all 1.615 + mFontTableCache = new nsTHashtable<FontTableHashEntry>(10); 1.616 + } 1.617 + 1.618 + FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag); 1.619 + if (!entry) { 1.620 + return false; 1.621 + } 1.622 + 1.623 + *aBlob = entry->GetBlob(); 1.624 + return true; 1.625 +} 1.626 + 1.627 +hb_blob_t * 1.628 +gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag, 1.629 + FallibleTArray<uint8_t>* aBuffer) 1.630 +{ 1.631 + if (MOZ_UNLIKELY(!mFontTableCache)) { 1.632 + // we do this here rather than on fontEntry construction 1.633 + // because not all shapers will access the table cache at all 1.634 + mFontTableCache = new nsTHashtable<FontTableHashEntry>(10); 1.635 + } 1.636 + 1.637 + FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag); 1.638 + if (MOZ_UNLIKELY(!entry)) { // OOM 1.639 + return nullptr; 1.640 + } 1.641 + 1.642 + if (!aBuffer) { 1.643 + // ensure the entry is null 1.644 + entry->Clear(); 1.645 + return nullptr; 1.646 + } 1.647 + 1.648 + return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache); 1.649 +} 1.650 + 1.651 +static int 1.652 +DirEntryCmp(const void* aKey, const void* aItem) 1.653 +{ 1.654 + int32_t tag = *static_cast<const int32_t*>(aKey); 1.655 + const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem); 1.656 + return tag - int32_t(entry->tag); 1.657 +} 1.658 + 1.659 +hb_blob_t* 1.660 +gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag) 1.661 +{ 1.662 + const SFNTHeader* header = 1.663 + reinterpret_cast<const SFNTHeader*>(aFontData); 1.664 + const TableDirEntry* dir = 1.665 + reinterpret_cast<const TableDirEntry*>(header + 1); 1.666 + dir = static_cast<const TableDirEntry*> 1.667 + (bsearch(&aTableTag, dir, uint16_t(header->numTables), 1.668 + sizeof(TableDirEntry), DirEntryCmp)); 1.669 + if (dir) { 1.670 + return hb_blob_create(reinterpret_cast<const char*>(aFontData) + 1.671 + dir->offset, dir->length, 1.672 + HB_MEMORY_MODE_READONLY, nullptr, nullptr); 1.673 + 1.674 + } 1.675 + return nullptr; 1.676 +} 1.677 + 1.678 +already_AddRefed<gfxCharacterMap> 1.679 +gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData, 1.680 + uint32_t& aUVSOffset, 1.681 + bool& aSymbolFont) 1.682 +{ 1.683 + if (!aFontInfoData || !aFontInfoData->mLoadCmaps) { 1.684 + return nullptr; 1.685 + } 1.686 + 1.687 + return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont); 1.688 +} 1.689 + 1.690 +hb_blob_t * 1.691 +gfxFontEntry::GetFontTable(uint32_t aTag) 1.692 +{ 1.693 + hb_blob_t *blob; 1.694 + if (GetExistingFontTable(aTag, &blob)) { 1.695 + return blob; 1.696 + } 1.697 + 1.698 + FallibleTArray<uint8_t> buffer; 1.699 + bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer)); 1.700 + 1.701 + return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr); 1.702 +} 1.703 + 1.704 +// callback for HarfBuzz to get a font table (in hb_blob_t form) 1.705 +// from the font entry (passed as aUserData) 1.706 +/*static*/ hb_blob_t * 1.707 +gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData) 1.708 +{ 1.709 + gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData); 1.710 + 1.711 + // bug 589682 - ignore the GDEF table in buggy fonts (applies to 1.712 + // Italic and BoldItalic faces of Times New Roman) 1.713 + if (aTag == TRUETYPE_TAG('G','D','E','F') && 1.714 + fontEntry->IgnoreGDEF()) { 1.715 + return nullptr; 1.716 + } 1.717 + 1.718 + // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto, 1.719 + // at least on some Android ICS devices; set in gfxFT2FontList.cpp) 1.720 + if (aTag == TRUETYPE_TAG('G','S','U','B') && 1.721 + fontEntry->IgnoreGSUB()) { 1.722 + return nullptr; 1.723 + } 1.724 + 1.725 + return fontEntry->GetFontTable(aTag); 1.726 +} 1.727 + 1.728 +/*static*/ void 1.729 +gfxFontEntry::HBFaceDeletedCallback(void *aUserData) 1.730 +{ 1.731 + gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData); 1.732 + fe->ForgetHBFace(); 1.733 +} 1.734 + 1.735 +void 1.736 +gfxFontEntry::ForgetHBFace() 1.737 +{ 1.738 + mHBFace = nullptr; 1.739 +} 1.740 + 1.741 +hb_face_t* 1.742 +gfxFontEntry::GetHBFace() 1.743 +{ 1.744 + if (!mHBFace) { 1.745 + mHBFace = hb_face_create_for_tables(HBGetTable, this, 1.746 + HBFaceDeletedCallback); 1.747 + return mHBFace; 1.748 + } 1.749 + return hb_face_reference(mHBFace); 1.750 +} 1.751 + 1.752 +/*static*/ const void* 1.753 +gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName, 1.754 + size_t *aLen) 1.755 +{ 1.756 + gfxFontEntry *fontEntry = 1.757 + static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle)); 1.758 + hb_blob_t *blob = fontEntry->GetFontTable(aName); 1.759 + if (blob) { 1.760 + unsigned int blobLength; 1.761 + const void *tableData = hb_blob_get_data(blob, &blobLength); 1.762 + fontEntry->mGrTableMap->Put(tableData, blob); 1.763 + *aLen = blobLength; 1.764 + return tableData; 1.765 + } 1.766 + *aLen = 0; 1.767 + return nullptr; 1.768 +} 1.769 + 1.770 +/*static*/ void 1.771 +gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle, 1.772 + const void *aTableBuffer) 1.773 +{ 1.774 + gfxFontEntry *fontEntry = 1.775 + static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle)); 1.776 + void *data; 1.777 + if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) { 1.778 + fontEntry->mGrTableMap->Remove(aTableBuffer); 1.779 + hb_blob_destroy(static_cast<hb_blob_t*>(data)); 1.780 + } 1.781 +} 1.782 + 1.783 +gr_face* 1.784 +gfxFontEntry::GetGrFace() 1.785 +{ 1.786 + if (!mGrFaceInitialized) { 1.787 + gr_face_ops faceOps = { 1.788 + sizeof(gr_face_ops), 1.789 + GrGetTable, 1.790 + GrReleaseTable 1.791 + }; 1.792 + mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>; 1.793 + mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default); 1.794 + mGrFaceInitialized = true; 1.795 + } 1.796 + ++mGrFaceRefCnt; 1.797 + return mGrFace; 1.798 +} 1.799 + 1.800 +void 1.801 +gfxFontEntry::ReleaseGrFace(gr_face *aFace) 1.802 +{ 1.803 + MOZ_ASSERT(aFace == mGrFace); // sanity-check 1.804 + MOZ_ASSERT(mGrFaceRefCnt > 0); 1.805 + if (--mGrFaceRefCnt == 0) { 1.806 + gr_face_destroy(mGrFace); 1.807 + mGrFace = nullptr; 1.808 + mGrFaceInitialized = false; 1.809 + delete mGrTableMap; 1.810 + mGrTableMap = nullptr; 1.811 + } 1.812 +} 1.813 + 1.814 +void 1.815 +gfxFontEntry::DisconnectSVG() 1.816 +{ 1.817 + if (mSVGInitialized && mSVGGlyphs) { 1.818 + mSVGGlyphs = nullptr; 1.819 + mSVGInitialized = false; 1.820 + } 1.821 +} 1.822 + 1.823 +bool 1.824 +gfxFontEntry::HasFontTable(uint32_t aTableTag) 1.825 +{ 1.826 + AutoTable table(this, aTableTag); 1.827 + return table && hb_blob_get_length(table) > 0; 1.828 +} 1.829 + 1.830 +void 1.831 +gfxFontEntry::CheckForGraphiteTables() 1.832 +{ 1.833 + mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f')); 1.834 +} 1.835 + 1.836 +/* static */ size_t 1.837 +gfxFontEntry::FontTableHashEntry::SizeOfEntryExcludingThis 1.838 + (FontTableHashEntry *aEntry, 1.839 + MallocSizeOf aMallocSizeOf, 1.840 + void* aUserArg) 1.841 +{ 1.842 + size_t n = 0; 1.843 + if (aEntry->mBlob) { 1.844 + n += aMallocSizeOf(aEntry->mBlob); 1.845 + } 1.846 + if (aEntry->mSharedBlobData) { 1.847 + n += aEntry->mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf); 1.848 + } 1.849 + return n; 1.850 +} 1.851 + 1.852 +void 1.853 +gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 1.854 + FontListSizes* aSizes) const 1.855 +{ 1.856 + aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 1.857 + 1.858 + // cmaps are shared so only non-shared cmaps are included here 1.859 + if (mCharacterMap && mCharacterMap->mBuildOnTheFly) { 1.860 + aSizes->mCharMapsSize += 1.861 + mCharacterMap->SizeOfIncludingThis(aMallocSizeOf); 1.862 + } 1.863 + if (mFontTableCache) { 1.864 + aSizes->mFontTableCacheSize += 1.865 + mFontTableCache->SizeOfExcludingThis( 1.866 + FontTableHashEntry::SizeOfEntryExcludingThis, 1.867 + aMallocSizeOf); 1.868 + } 1.869 +} 1.870 + 1.871 +void 1.872 +gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 1.873 + FontListSizes* aSizes) const 1.874 +{ 1.875 + aSizes->mFontListSize += aMallocSizeOf(this); 1.876 + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.877 +} 1.878 + 1.879 +////////////////////////////////////////////////////////////////////////////// 1.880 +// 1.881 +// class gfxFontFamily 1.882 +// 1.883 +////////////////////////////////////////////////////////////////////////////// 1.884 + 1.885 +// we consider faces with mStandardFace == true to be "greater than" those with false, 1.886 +// because during style matching, later entries will replace earlier ones 1.887 +class FontEntryStandardFaceComparator { 1.888 + public: 1.889 + bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const { 1.890 + return a->mStandardFace == b->mStandardFace; 1.891 + } 1.892 + bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const { 1.893 + return (a->mStandardFace == false && b->mStandardFace == true); 1.894 + } 1.895 +}; 1.896 + 1.897 +void 1.898 +gfxFontFamily::SortAvailableFonts() 1.899 +{ 1.900 + mAvailableFonts.Sort(FontEntryStandardFaceComparator()); 1.901 +} 1.902 + 1.903 +bool 1.904 +gfxFontFamily::HasOtherFamilyNames() 1.905 +{ 1.906 + // need to read in other family names to determine this 1.907 + if (!mOtherFamilyNamesInitialized) { 1.908 + ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames 1.909 + } 1.910 + return mHasOtherFamilyNames; 1.911 +} 1.912 + 1.913 +gfxFontEntry* 1.914 +gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, 1.915 + bool& aNeedsSyntheticBold) 1.916 +{ 1.917 + if (!mHasStyles) 1.918 + FindStyleVariations(); // collect faces for the family, if not already done 1.919 + 1.920 + NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!"); 1.921 + 1.922 + aNeedsSyntheticBold = false; 1.923 + 1.924 + int8_t baseWeight = aFontStyle.ComputeWeight(); 1.925 + bool wantBold = baseWeight >= 6; 1.926 + 1.927 + // If the family has only one face, we simply return it; no further checking needed 1.928 + if (mAvailableFonts.Length() == 1) { 1.929 + gfxFontEntry *fe = mAvailableFonts[0]; 1.930 + aNeedsSyntheticBold = wantBold && !fe->IsBold(); 1.931 + return fe; 1.932 + } 1.933 + 1.934 + bool wantItalic = (aFontStyle.style & 1.935 + (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; 1.936 + 1.937 + // Most families are "simple", having just Regular/Bold/Italic/BoldItalic, 1.938 + // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts, 1.939 + // stored in the above order; note that some of the entries may be nullptr. 1.940 + // We can then pick the required entry based on whether the request is for 1.941 + // bold or non-bold, italic or non-italic, without running the more complex 1.942 + // matching algorithm used for larger families with many weights and/or widths. 1.943 + 1.944 + if (mIsSimpleFamily) { 1.945 + // Family has no more than the "standard" 4 faces, at fixed indexes; 1.946 + // calculate which one we want. 1.947 + // Note that we cannot simply return it as not all 4 faces are necessarily present. 1.948 + uint8_t faceIndex = (wantItalic ? kItalicMask : 0) | 1.949 + (wantBold ? kBoldMask : 0); 1.950 + 1.951 + // if the desired style is available, return it directly 1.952 + gfxFontEntry *fe = mAvailableFonts[faceIndex]; 1.953 + if (fe) { 1.954 + // no need to set aNeedsSyntheticBold here as we matched the boldness request 1.955 + return fe; 1.956 + } 1.957 + 1.958 + // order to check fallback faces in a simple family, depending on requested style 1.959 + static const uint8_t simpleFallbacks[4][3] = { 1.960 + { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular 1.961 + { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold 1.962 + { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic 1.963 + { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic 1.964 + }; 1.965 + const uint8_t *order = simpleFallbacks[faceIndex]; 1.966 + 1.967 + for (uint8_t trial = 0; trial < 3; ++trial) { 1.968 + // check remaining faces in order of preference to find the first that actually exists 1.969 + fe = mAvailableFonts[order[trial]]; 1.970 + if (fe) { 1.971 + aNeedsSyntheticBold = wantBold && !fe->IsBold(); 1.972 + return fe; 1.973 + } 1.974 + } 1.975 + 1.976 + // this can't happen unless we have totally broken the font-list manager! 1.977 + NS_NOTREACHED("no face found in simple font family!"); 1.978 + return nullptr; 1.979 + } 1.980 + 1.981 + // This is a large/rich font family, so we do full style- and weight-matching: 1.982 + // first collect a list of weights that are the best match for the requested 1.983 + // font-stretch and font-style, then pick the best weight match among those 1.984 + // available. 1.985 + 1.986 + gfxFontEntry *weightList[10] = { 0 }; 1.987 + bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch); 1.988 + if (!foundWeights) { 1.989 + return nullptr; 1.990 + } 1.991 + 1.992 + // First find a match for the best weight 1.993 + int8_t matchBaseWeight = 0; 1.994 + int8_t i = baseWeight; 1.995 + 1.996 + // Need to special case when normal face doesn't exist but medium does. 1.997 + // In that case, use medium otherwise weights < 400 1.998 + if (baseWeight == 4 && !weightList[4]) { 1.999 + i = 5; // medium 1.1000 + } 1.1001 + 1.1002 + // Loop through weights, since one exists loop will terminate 1.1003 + int8_t direction = (baseWeight > 5) ? 1 : -1; 1.1004 + for (; ; i += direction) { 1.1005 + if (weightList[i]) { 1.1006 + matchBaseWeight = i; 1.1007 + break; 1.1008 + } 1.1009 + 1.1010 + // If we've reached one side without finding a font, 1.1011 + // start over and go the other direction until we find a match 1.1012 + if (i == 1 || i == 9) { 1.1013 + i = baseWeight; 1.1014 + direction = -direction; 1.1015 + } 1.1016 + } 1.1017 + 1.1018 + NS_ASSERTION(matchBaseWeight != 0, 1.1019 + "weight mapping should always find at least one font in a family"); 1.1020 + 1.1021 + gfxFontEntry *matchFE = weightList[matchBaseWeight]; 1.1022 + 1.1023 + NS_ASSERTION(matchFE, 1.1024 + "weight mapping should always find at least one font in a family"); 1.1025 + 1.1026 + if (!matchFE->IsBold() && baseWeight >= 6) 1.1027 + { 1.1028 + aNeedsSyntheticBold = true; 1.1029 + } 1.1030 + 1.1031 + return matchFE; 1.1032 +} 1.1033 + 1.1034 +void 1.1035 +gfxFontFamily::CheckForSimpleFamily() 1.1036 +{ 1.1037 + // already checked this family 1.1038 + if (mIsSimpleFamily) { 1.1039 + return; 1.1040 + }; 1.1041 + 1.1042 + uint32_t count = mAvailableFonts.Length(); 1.1043 + if (count > 4 || count == 0) { 1.1044 + return; // can't be "simple" if there are >4 faces; 1.1045 + // if none then the family is unusable anyway 1.1046 + } 1.1047 + 1.1048 + if (count == 1) { 1.1049 + mIsSimpleFamily = true; 1.1050 + return; 1.1051 + } 1.1052 + 1.1053 + int16_t firstStretch = mAvailableFonts[0]->Stretch(); 1.1054 + 1.1055 + gfxFontEntry *faces[4] = { 0 }; 1.1056 + for (uint8_t i = 0; i < count; ++i) { 1.1057 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1058 + if (fe->Stretch() != firstStretch) { 1.1059 + return; // font-stretch doesn't match, don't treat as simple family 1.1060 + } 1.1061 + uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) | 1.1062 + (fe->Weight() >= 600 ? kBoldMask : 0); 1.1063 + if (faces[faceIndex]) { 1.1064 + return; // two faces resolve to the same slot; family isn't "simple" 1.1065 + } 1.1066 + faces[faceIndex] = fe; 1.1067 + } 1.1068 + 1.1069 + // we have successfully slotted the available faces into the standard 1.1070 + // 4-face framework 1.1071 + mAvailableFonts.SetLength(4); 1.1072 + for (uint8_t i = 0; i < 4; ++i) { 1.1073 + if (mAvailableFonts[i].get() != faces[i]) { 1.1074 + mAvailableFonts[i].swap(faces[i]); 1.1075 + } 1.1076 + } 1.1077 + 1.1078 + mIsSimpleFamily = true; 1.1079 +} 1.1080 + 1.1081 +static inline uint32_t 1.1082 +StyleDistance(gfxFontEntry *aFontEntry, 1.1083 + bool anItalic, int16_t aStretch) 1.1084 +{ 1.1085 + // Compute a measure of the "distance" between the requested style 1.1086 + // and the given fontEntry, 1.1087 + // considering italicness and font-stretch but not weight. 1.1088 + 1.1089 + int32_t distance = 0; 1.1090 + if (aStretch != aFontEntry->mStretch) { 1.1091 + // stretch values are in the range -4 .. +4 1.1092 + // if aStretch is positive, we prefer more-positive values; 1.1093 + // if zero or negative, prefer more-negative 1.1094 + if (aStretch > 0) { 1.1095 + distance = (aFontEntry->mStretch - aStretch) * 2; 1.1096 + } else { 1.1097 + distance = (aStretch - aFontEntry->mStretch) * 2; 1.1098 + } 1.1099 + // if the computed "distance" here is negative, it means that 1.1100 + // aFontEntry lies in the "non-preferred" direction from aStretch, 1.1101 + // so we treat that as larger than any preferred-direction distance 1.1102 + // (max possible is 8) by adding an extra 10 to the absolute value 1.1103 + if (distance < 0) { 1.1104 + distance = -distance + 10; 1.1105 + } 1.1106 + } 1.1107 + if (aFontEntry->IsItalic() != anItalic) { 1.1108 + distance += 1; 1.1109 + } 1.1110 + return uint32_t(distance); 1.1111 +} 1.1112 + 1.1113 +bool 1.1114 +gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[], 1.1115 + bool anItalic, int16_t aStretch) 1.1116 +{ 1.1117 + uint32_t foundWeights = 0; 1.1118 + uint32_t bestMatchDistance = 0xffffffff; 1.1119 + 1.1120 + uint32_t count = mAvailableFonts.Length(); 1.1121 + for (uint32_t i = 0; i < count; i++) { 1.1122 + // this is not called for "simple" families, and therefore it does not 1.1123 + // need to check the mAvailableFonts entries for nullptr. 1.1124 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1125 + uint32_t distance = StyleDistance(fe, anItalic, aStretch); 1.1126 + if (distance <= bestMatchDistance) { 1.1127 + int8_t wt = fe->mWeight / 100; 1.1128 + NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry"); 1.1129 + if (!aFontsForWeights[wt]) { 1.1130 + // record this as a possible candidate for weight matching 1.1131 + aFontsForWeights[wt] = fe; 1.1132 + ++foundWeights; 1.1133 + } else { 1.1134 + uint32_t prevDistance = 1.1135 + StyleDistance(aFontsForWeights[wt], anItalic, aStretch); 1.1136 + if (prevDistance >= distance) { 1.1137 + // replacing a weight we already found, 1.1138 + // so don't increment foundWeights 1.1139 + aFontsForWeights[wt] = fe; 1.1140 + } 1.1141 + } 1.1142 + bestMatchDistance = distance; 1.1143 + } 1.1144 + } 1.1145 + 1.1146 + NS_ASSERTION(foundWeights > 0, "Font family containing no faces?"); 1.1147 + 1.1148 + if (foundWeights == 1) { 1.1149 + // no need to cull entries if we only found one weight 1.1150 + return true; 1.1151 + } 1.1152 + 1.1153 + // we might have recorded some faces that were a partial style match, but later found 1.1154 + // others that were closer; in this case, we need to cull the poorer matches from the 1.1155 + // weight list we'll return 1.1156 + for (uint32_t i = 0; i < 10; ++i) { 1.1157 + if (aFontsForWeights[i] && 1.1158 + StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance) 1.1159 + { 1.1160 + aFontsForWeights[i] = 0; 1.1161 + } 1.1162 + } 1.1163 + 1.1164 + return (foundWeights > 0); 1.1165 +} 1.1166 + 1.1167 + 1.1168 +void gfxFontFamily::LocalizedName(nsAString& aLocalizedName) 1.1169 +{ 1.1170 + // just return the primary name; subclasses should override 1.1171 + aLocalizedName = mName; 1.1172 +} 1.1173 + 1.1174 +// metric for how close a given font matches a style 1.1175 +static int32_t 1.1176 +CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle) 1.1177 +{ 1.1178 + int32_t rank = 0; 1.1179 + if (aStyle) { 1.1180 + // italics 1.1181 + bool wantItalic = 1.1182 + (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0; 1.1183 + if (aFontEntry->IsItalic() == wantItalic) { 1.1184 + rank += 10; 1.1185 + } 1.1186 + 1.1187 + // measure of closeness of weight to the desired value 1.1188 + rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight()); 1.1189 + } else { 1.1190 + // if no font to match, prefer non-bold, non-italic fonts 1.1191 + if (!aFontEntry->IsItalic()) { 1.1192 + rank += 3; 1.1193 + } 1.1194 + if (!aFontEntry->IsBold()) { 1.1195 + rank += 2; 1.1196 + } 1.1197 + } 1.1198 + 1.1199 + return rank; 1.1200 +} 1.1201 + 1.1202 +#define RANK_MATCHED_CMAP 20 1.1203 + 1.1204 +void 1.1205 +gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData) 1.1206 +{ 1.1207 + if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) { 1.1208 + // none of the faces in the family support the required char, 1.1209 + // so bail out immediately 1.1210 + return; 1.1211 + } 1.1212 + 1.1213 + bool needsBold; 1.1214 + gfxFontStyle normal; 1.1215 + gfxFontEntry *fe = FindFontForStyle( 1.1216 + (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal, 1.1217 + needsBold); 1.1218 + 1.1219 + if (fe && !fe->SkipDuringSystemFallback()) { 1.1220 + int32_t rank = 0; 1.1221 + 1.1222 + if (fe->TestCharacterMap(aMatchData->mCh)) { 1.1223 + rank += RANK_MATCHED_CMAP; 1.1224 + aMatchData->mCount++; 1.1225 +#ifdef PR_LOGGING 1.1226 + PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun); 1.1227 + 1.1228 + if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) { 1.1229 + uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh); 1.1230 + uint32_t script = GetScriptCode(aMatchData->mCh); 1.1231 + PR_LOG(log, PR_LOG_DEBUG,\ 1.1232 + ("(textrun-systemfallback-fonts) char: u+%6.6x " 1.1233 + "unicode-range: %d script: %d match: [%s]\n", 1.1234 + aMatchData->mCh, 1.1235 + unicodeRange, script, 1.1236 + NS_ConvertUTF16toUTF8(fe->Name()).get())); 1.1237 + } 1.1238 +#endif 1.1239 + } 1.1240 + 1.1241 + aMatchData->mCmapsTested++; 1.1242 + if (rank == 0) { 1.1243 + return; 1.1244 + } 1.1245 + 1.1246 + // omitting from original windows code -- family name, lang group, pitch 1.1247 + // not available in current FontEntry implementation 1.1248 + rank += CalcStyleMatch(fe, aMatchData->mStyle); 1.1249 + 1.1250 + // xxx - add whether AAT font with morphing info for specific lang groups 1.1251 + 1.1252 + if (rank > aMatchData->mMatchRank 1.1253 + || (rank == aMatchData->mMatchRank && 1.1254 + Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) 1.1255 + { 1.1256 + aMatchData->mBestMatch = fe; 1.1257 + aMatchData->mMatchedFamily = this; 1.1258 + aMatchData->mMatchRank = rank; 1.1259 + } 1.1260 + } 1.1261 +} 1.1262 + 1.1263 +void 1.1264 +gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData) 1.1265 +{ 1.1266 + uint32_t i, numFonts = mAvailableFonts.Length(); 1.1267 + for (i = 0; i < numFonts; i++) { 1.1268 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1269 + if (fe && fe->TestCharacterMap(aMatchData->mCh)) { 1.1270 + int32_t rank = RANK_MATCHED_CMAP; 1.1271 + rank += CalcStyleMatch(fe, aMatchData->mStyle); 1.1272 + if (rank > aMatchData->mMatchRank 1.1273 + || (rank == aMatchData->mMatchRank && 1.1274 + Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0)) 1.1275 + { 1.1276 + aMatchData->mBestMatch = fe; 1.1277 + aMatchData->mMatchedFamily = this; 1.1278 + aMatchData->mMatchRank = rank; 1.1279 + } 1.1280 + } 1.1281 + } 1.1282 +} 1.1283 + 1.1284 +/*static*/ void 1.1285 +gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName, 1.1286 + const char *aNameData, 1.1287 + uint32_t aDataLength, 1.1288 + nsTArray<nsString>& aOtherFamilyNames, 1.1289 + bool useFullName) 1.1290 +{ 1.1291 + const gfxFontUtils::NameHeader *nameHeader = 1.1292 + reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData); 1.1293 + 1.1294 + uint32_t nameCount = nameHeader->count; 1.1295 + if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) { 1.1296 + NS_WARNING("invalid font (name records)"); 1.1297 + return; 1.1298 + } 1.1299 + 1.1300 + const gfxFontUtils::NameRecord *nameRecord = 1.1301 + reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader)); 1.1302 + uint32_t stringsBase = uint32_t(nameHeader->stringOffset); 1.1303 + 1.1304 + for (uint32_t i = 0; i < nameCount; i++, nameRecord++) { 1.1305 + uint32_t nameLen = nameRecord->length; 1.1306 + uint32_t nameOff = nameRecord->offset; // offset from base of string storage 1.1307 + 1.1308 + if (stringsBase + nameOff + nameLen > aDataLength) { 1.1309 + NS_WARNING("invalid font (name table strings)"); 1.1310 + return; 1.1311 + } 1.1312 + 1.1313 + uint16_t nameID = nameRecord->nameID; 1.1314 + if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) || 1.1315 + (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY || 1.1316 + nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) { 1.1317 + nsAutoString otherFamilyName; 1.1318 + bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff, 1.1319 + nameLen, 1.1320 + uint32_t(nameRecord->platformID), 1.1321 + uint32_t(nameRecord->encodingID), 1.1322 + uint32_t(nameRecord->languageID), 1.1323 + otherFamilyName); 1.1324 + // add if not same as canonical family name 1.1325 + if (ok && otherFamilyName != aFamilyName) { 1.1326 + aOtherFamilyNames.AppendElement(otherFamilyName); 1.1327 + } 1.1328 + } 1.1329 + } 1.1330 +} 1.1331 + 1.1332 +// returns true if other names were found, false otherwise 1.1333 +bool 1.1334 +gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList, 1.1335 + hb_blob_t *aNameTable, 1.1336 + bool useFullName) 1.1337 +{ 1.1338 + uint32_t dataLength; 1.1339 + const char *nameData = hb_blob_get_data(aNameTable, &dataLength); 1.1340 + nsAutoTArray<nsString,4> otherFamilyNames; 1.1341 + 1.1342 + ReadOtherFamilyNamesForFace(mName, nameData, dataLength, 1.1343 + otherFamilyNames, useFullName); 1.1344 + 1.1345 + uint32_t n = otherFamilyNames.Length(); 1.1346 + for (uint32_t i = 0; i < n; i++) { 1.1347 + aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]); 1.1348 + } 1.1349 + 1.1350 + return n != 0; 1.1351 +} 1.1352 + 1.1353 +void 1.1354 +gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList) 1.1355 +{ 1.1356 + if (mOtherFamilyNamesInitialized) 1.1357 + return; 1.1358 + mOtherFamilyNamesInitialized = true; 1.1359 + 1.1360 + FindStyleVariations(); 1.1361 + 1.1362 + // read in other family names for the first face in the list 1.1363 + uint32_t i, numFonts = mAvailableFonts.Length(); 1.1364 + const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); 1.1365 + 1.1366 + for (i = 0; i < numFonts; ++i) { 1.1367 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1368 + if (!fe) { 1.1369 + continue; 1.1370 + } 1.1371 + gfxFontEntry::AutoTable nameTable(fe, kNAME); 1.1372 + if (!nameTable) { 1.1373 + continue; 1.1374 + } 1.1375 + mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList, 1.1376 + nameTable); 1.1377 + break; 1.1378 + } 1.1379 + 1.1380 + // read in other names for the first face in the list with the assumption 1.1381 + // that if extra names don't exist in that face then they don't exist in 1.1382 + // other faces for the same font 1.1383 + if (!mHasOtherFamilyNames) 1.1384 + return; 1.1385 + 1.1386 + // read in names for all faces, needed to catch cases where fonts have 1.1387 + // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6) 1.1388 + for ( ; i < numFonts; i++) { 1.1389 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1390 + if (!fe) { 1.1391 + continue; 1.1392 + } 1.1393 + gfxFontEntry::AutoTable nameTable(fe, kNAME); 1.1394 + if (!nameTable) { 1.1395 + continue; 1.1396 + } 1.1397 + ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable); 1.1398 + } 1.1399 +} 1.1400 + 1.1401 +void 1.1402 +gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, 1.1403 + bool aNeedFullnamePostscriptNames, 1.1404 + FontInfoData *aFontInfoData) 1.1405 +{ 1.1406 + // if all needed names have already been read, skip 1.1407 + if (mOtherFamilyNamesInitialized && 1.1408 + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) 1.1409 + return; 1.1410 + 1.1411 + bool asyncFontLoaderDisabled = false; 1.1412 + 1.1413 +#if defined(XP_MACOSX) 1.1414 + // bug 975460 - async font loader crashes sometimes under 10.6, disable 1.1415 + if (!nsCocoaFeatures::OnLionOrLater()) { 1.1416 + asyncFontLoaderDisabled = true; 1.1417 + } 1.1418 +#endif 1.1419 + 1.1420 + if (!mOtherFamilyNamesInitialized && 1.1421 + aFontInfoData && 1.1422 + aFontInfoData->mLoadOtherNames && 1.1423 + !asyncFontLoaderDisabled) 1.1424 + { 1.1425 + nsAutoTArray<nsString,4> otherFamilyNames; 1.1426 + bool foundOtherNames = 1.1427 + aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames); 1.1428 + if (foundOtherNames) { 1.1429 + uint32_t i, n = otherFamilyNames.Length(); 1.1430 + for (i = 0; i < n; i++) { 1.1431 + aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]); 1.1432 + } 1.1433 + } 1.1434 + mOtherFamilyNamesInitialized = true; 1.1435 + } 1.1436 + 1.1437 + // if all needed data has been initialized, return 1.1438 + if (mOtherFamilyNamesInitialized && 1.1439 + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { 1.1440 + return; 1.1441 + } 1.1442 + 1.1443 + FindStyleVariations(aFontInfoData); 1.1444 + 1.1445 + // check again, as style enumeration code may have loaded names 1.1446 + if (mOtherFamilyNamesInitialized && 1.1447 + (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) { 1.1448 + return; 1.1449 + } 1.1450 + 1.1451 + uint32_t i, numFonts = mAvailableFonts.Length(); 1.1452 + const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e'); 1.1453 + 1.1454 + bool firstTime = true, readAllFaces = false; 1.1455 + for (i = 0; i < numFonts; ++i) { 1.1456 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1457 + if (!fe) { 1.1458 + continue; 1.1459 + } 1.1460 + 1.1461 + nsAutoString fullname, psname; 1.1462 + bool foundFaceNames = false; 1.1463 + if (!mFaceNamesInitialized && 1.1464 + aNeedFullnamePostscriptNames && 1.1465 + aFontInfoData && 1.1466 + aFontInfoData->mLoadFaceNames) { 1.1467 + aFontInfoData->GetFaceNames(fe->Name(), fullname, psname); 1.1468 + if (!fullname.IsEmpty()) { 1.1469 + aPlatformFontList->AddFullname(fe, fullname); 1.1470 + } 1.1471 + if (!psname.IsEmpty()) { 1.1472 + aPlatformFontList->AddPostscriptName(fe, psname); 1.1473 + } 1.1474 + foundFaceNames = true; 1.1475 + 1.1476 + // found everything needed? skip to next font 1.1477 + if (mOtherFamilyNamesInitialized) { 1.1478 + continue; 1.1479 + } 1.1480 + } 1.1481 + 1.1482 + // load directly from the name table 1.1483 + gfxFontEntry::AutoTable nameTable(fe, kNAME); 1.1484 + if (!nameTable) { 1.1485 + continue; 1.1486 + } 1.1487 + 1.1488 + if (aNeedFullnamePostscriptNames && !foundFaceNames) { 1.1489 + if (gfxFontUtils::ReadCanonicalName( 1.1490 + nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK) 1.1491 + { 1.1492 + aPlatformFontList->AddFullname(fe, fullname); 1.1493 + } 1.1494 + 1.1495 + if (gfxFontUtils::ReadCanonicalName( 1.1496 + nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK) 1.1497 + { 1.1498 + aPlatformFontList->AddPostscriptName(fe, psname); 1.1499 + } 1.1500 + } 1.1501 + 1.1502 + if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) { 1.1503 + bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList, 1.1504 + nameTable); 1.1505 + 1.1506 + // if the first face has a different name, scan all faces, otherwise 1.1507 + // assume the family doesn't have other names 1.1508 + if (firstTime && foundOtherName) { 1.1509 + mHasOtherFamilyNames = true; 1.1510 + readAllFaces = true; 1.1511 + } 1.1512 + firstTime = false; 1.1513 + } 1.1514 + 1.1515 + // if not reading in any more names, skip other faces 1.1516 + if (!readAllFaces && !aNeedFullnamePostscriptNames) { 1.1517 + break; 1.1518 + } 1.1519 + } 1.1520 + 1.1521 + mFaceNamesInitialized = true; 1.1522 + mOtherFamilyNamesInitialized = true; 1.1523 +} 1.1524 + 1.1525 + 1.1526 +gfxFontEntry* 1.1527 +gfxFontFamily::FindFont(const nsAString& aPostscriptName) 1.1528 +{ 1.1529 + // find the font using a simple linear search 1.1530 + uint32_t numFonts = mAvailableFonts.Length(); 1.1531 + for (uint32_t i = 0; i < numFonts; i++) { 1.1532 + gfxFontEntry *fe = mAvailableFonts[i].get(); 1.1533 + if (fe && fe->Name() == aPostscriptName) 1.1534 + return fe; 1.1535 + } 1.1536 + return nullptr; 1.1537 +} 1.1538 + 1.1539 +void 1.1540 +gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData) 1.1541 +{ 1.1542 + FindStyleVariations(aFontInfoData); 1.1543 + 1.1544 + uint32_t i, numFonts = mAvailableFonts.Length(); 1.1545 + for (i = 0; i < numFonts; i++) { 1.1546 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1547 + // don't try to load cmaps for downloadable fonts not yet loaded 1.1548 + if (!fe || fe->mIsProxy) { 1.1549 + continue; 1.1550 + } 1.1551 + fe->ReadCMAP(aFontInfoData); 1.1552 + mFamilyCharacterMap.Union(*(fe->mCharacterMap)); 1.1553 + } 1.1554 + mFamilyCharacterMap.Compact(); 1.1555 + mFamilyCharacterMapInitialized = true; 1.1556 +} 1.1557 + 1.1558 +void 1.1559 +gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 1.1560 + FontListSizes* aSizes) const 1.1561 +{ 1.1562 + aSizes->mFontListSize += 1.1563 + mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf); 1.1564 + aSizes->mCharMapsSize += 1.1565 + mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf); 1.1566 + 1.1567 + aSizes->mFontListSize += 1.1568 + mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf); 1.1569 + for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) { 1.1570 + gfxFontEntry *fe = mAvailableFonts[i]; 1.1571 + if (fe) { 1.1572 + fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes); 1.1573 + } 1.1574 + } 1.1575 +} 1.1576 + 1.1577 +void 1.1578 +gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 1.1579 + FontListSizes* aSizes) const 1.1580 +{ 1.1581 + aSizes->mFontListSize += aMallocSizeOf(this); 1.1582 + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.1583 +} 1.1584 + 1.1585 +/* 1.1586 + * gfxFontCache - global cache of gfxFont instances. 1.1587 + * Expires unused fonts after a short interval; 1.1588 + * notifies fonts to age their cached shaped-word records; 1.1589 + * observes memory-pressure notification and tells fonts to clear their 1.1590 + * shaped-word caches to free up memory. 1.1591 + */ 1.1592 + 1.1593 +MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf) 1.1594 + 1.1595 +NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter) 1.1596 + 1.1597 +NS_IMETHODIMP 1.1598 +gfxFontCache::MemoryReporter::CollectReports 1.1599 + (nsIMemoryReporterCallback* aCb, 1.1600 + nsISupports* aClosure) 1.1601 +{ 1.1602 + FontCacheSizes sizes; 1.1603 + 1.1604 + gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf, 1.1605 + &sizes); 1.1606 + 1.1607 + aCb->Callback(EmptyCString(), 1.1608 + NS_LITERAL_CSTRING("explicit/gfx/font-cache"), 1.1609 + KIND_HEAP, UNITS_BYTES, sizes.mFontInstances, 1.1610 + NS_LITERAL_CSTRING("Memory used for active font instances."), 1.1611 + aClosure); 1.1612 + 1.1613 + aCb->Callback(EmptyCString(), 1.1614 + NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"), 1.1615 + KIND_HEAP, UNITS_BYTES, sizes.mShapedWords, 1.1616 + NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."), 1.1617 + aClosure); 1.1618 + 1.1619 + return NS_OK; 1.1620 +} 1.1621 + 1.1622 +NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver) 1.1623 + 1.1624 +NS_IMETHODIMP 1.1625 +gfxFontCache::Observer::Observe(nsISupports *aSubject, 1.1626 + const char *aTopic, 1.1627 + const char16_t *someData) 1.1628 +{ 1.1629 + if (!nsCRT::strcmp(aTopic, "memory-pressure")) { 1.1630 + gfxFontCache *fontCache = gfxFontCache::GetCache(); 1.1631 + if (fontCache) { 1.1632 + fontCache->FlushShapedWordCaches(); 1.1633 + } 1.1634 + } else { 1.1635 + NS_NOTREACHED("unexpected notification topic"); 1.1636 + } 1.1637 + return NS_OK; 1.1638 +} 1.1639 + 1.1640 +nsresult 1.1641 +gfxFontCache::Init() 1.1642 +{ 1.1643 + NS_ASSERTION(!gGlobalCache, "Where did this come from?"); 1.1644 + gGlobalCache = new gfxFontCache(); 1.1645 + if (!gGlobalCache) { 1.1646 + return NS_ERROR_OUT_OF_MEMORY; 1.1647 + } 1.1648 + RegisterStrongMemoryReporter(new MemoryReporter()); 1.1649 + return NS_OK; 1.1650 +} 1.1651 + 1.1652 +void 1.1653 +gfxFontCache::Shutdown() 1.1654 +{ 1.1655 + delete gGlobalCache; 1.1656 + gGlobalCache = nullptr; 1.1657 + 1.1658 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.1659 + printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark); 1.1660 + printf("Total number of fonts=%d\n", gFontCount); 1.1661 + printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount, 1.1662 + int(gGlyphExtentsCount*sizeof(gfxGlyphExtents))); 1.1663 + printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize); 1.1664 + printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple); 1.1665 + printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight); 1.1666 + printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight); 1.1667 + printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight); 1.1668 +#endif 1.1669 +} 1.1670 + 1.1671 +gfxFontCache::gfxFontCache() 1.1672 + : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000) 1.1673 +{ 1.1674 + nsCOMPtr<nsIObserverService> obs = GetObserverService(); 1.1675 + if (obs) { 1.1676 + obs->AddObserver(new Observer, "memory-pressure", false); 1.1677 + } 1.1678 + 1.1679 +#ifndef RELEASE_BUILD 1.1680 + // Currently disabled for release builds, due to unexplained crashes 1.1681 + // during expiration; see bug 717175 & 894798. 1.1682 + mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1"); 1.1683 + if (mWordCacheExpirationTimer) { 1.1684 + mWordCacheExpirationTimer-> 1.1685 + InitWithFuncCallback(WordCacheExpirationTimerCallback, this, 1.1686 + SHAPED_WORD_TIMEOUT_SECONDS * 1000, 1.1687 + nsITimer::TYPE_REPEATING_SLACK); 1.1688 + } 1.1689 +#endif 1.1690 +} 1.1691 + 1.1692 +gfxFontCache::~gfxFontCache() 1.1693 +{ 1.1694 + // Ensure the user font cache releases its references to font entries, 1.1695 + // so they aren't kept alive after the font instances and font-list 1.1696 + // have been shut down. 1.1697 + gfxUserFontSet::UserFontCache::Shutdown(); 1.1698 + 1.1699 + if (mWordCacheExpirationTimer) { 1.1700 + mWordCacheExpirationTimer->Cancel(); 1.1701 + mWordCacheExpirationTimer = nullptr; 1.1702 + } 1.1703 + 1.1704 + // Expire everything that has a zero refcount, so we don't leak them. 1.1705 + AgeAllGenerations(); 1.1706 + // All fonts should be gone. 1.1707 + NS_WARN_IF_FALSE(mFonts.Count() == 0, 1.1708 + "Fonts still alive while shutting down gfxFontCache"); 1.1709 + // Note that we have to delete everything through the expiration 1.1710 + // tracker, since there might be fonts not in the hashtable but in 1.1711 + // the tracker. 1.1712 +} 1.1713 + 1.1714 +bool 1.1715 +gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const 1.1716 +{ 1.1717 + return aKey->mFontEntry == mFont->GetFontEntry() && 1.1718 + aKey->mStyle->Equals(*mFont->GetStyle()); 1.1719 +} 1.1720 + 1.1721 +already_AddRefed<gfxFont> 1.1722 +gfxFontCache::Lookup(const gfxFontEntry *aFontEntry, 1.1723 + const gfxFontStyle *aStyle) 1.1724 +{ 1.1725 + Key key(aFontEntry, aStyle); 1.1726 + HashEntry *entry = mFonts.GetEntry(key); 1.1727 + 1.1728 + Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr); 1.1729 + if (!entry) 1.1730 + return nullptr; 1.1731 + 1.1732 + nsRefPtr<gfxFont> font = entry->mFont; 1.1733 + return font.forget(); 1.1734 +} 1.1735 + 1.1736 +void 1.1737 +gfxFontCache::AddNew(gfxFont *aFont) 1.1738 +{ 1.1739 + Key key(aFont->GetFontEntry(), aFont->GetStyle()); 1.1740 + HashEntry *entry = mFonts.PutEntry(key); 1.1741 + if (!entry) 1.1742 + return; 1.1743 + gfxFont *oldFont = entry->mFont; 1.1744 + entry->mFont = aFont; 1.1745 + // Assert that we can find the entry we just put in (this fails if the key 1.1746 + // has a NaN float value in it, e.g. 'sizeAdjust'). 1.1747 + MOZ_ASSERT(entry == mFonts.GetEntry(key)); 1.1748 + // If someone's asked us to replace an existing font entry, then that's a 1.1749 + // bit weird, but let it happen, and expire the old font if it's not used. 1.1750 + if (oldFont && oldFont->GetExpirationState()->IsTracked()) { 1.1751 + // if oldFont == aFont, recount should be > 0, 1.1752 + // so we shouldn't be here. 1.1753 + NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!"); 1.1754 + NotifyExpired(oldFont); 1.1755 + } 1.1756 +} 1.1757 + 1.1758 +void 1.1759 +gfxFontCache::NotifyReleased(gfxFont *aFont) 1.1760 +{ 1.1761 + nsresult rv = AddObject(aFont); 1.1762 + if (NS_FAILED(rv)) { 1.1763 + // We couldn't track it for some reason. Kill it now. 1.1764 + DestroyFont(aFont); 1.1765 + } 1.1766 + // Note that we might have fonts that aren't in the hashtable, perhaps because 1.1767 + // of OOM adding to the hashtable or because someone did an AddNew where 1.1768 + // we already had a font. These fonts are added to the expiration tracker 1.1769 + // anyway, even though Lookup can't resurrect them. Eventually they will 1.1770 + // expire and be deleted. 1.1771 +} 1.1772 + 1.1773 +void 1.1774 +gfxFontCache::NotifyExpired(gfxFont *aFont) 1.1775 +{ 1.1776 + aFont->ClearCachedWords(); 1.1777 + RemoveObject(aFont); 1.1778 + DestroyFont(aFont); 1.1779 +} 1.1780 + 1.1781 +void 1.1782 +gfxFontCache::DestroyFont(gfxFont *aFont) 1.1783 +{ 1.1784 + Key key(aFont->GetFontEntry(), aFont->GetStyle()); 1.1785 + HashEntry *entry = mFonts.GetEntry(key); 1.1786 + if (entry && entry->mFont == aFont) { 1.1787 + mFonts.RemoveEntry(key); 1.1788 + } 1.1789 + NS_ASSERTION(aFont->GetRefCount() == 0, 1.1790 + "Destroying with non-zero ref count!"); 1.1791 + delete aFont; 1.1792 +} 1.1793 + 1.1794 +/*static*/ 1.1795 +PLDHashOperator 1.1796 +gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData) 1.1797 +{ 1.1798 + aHashEntry->mFont->AgeCachedWords(); 1.1799 + return PL_DHASH_NEXT; 1.1800 +} 1.1801 + 1.1802 +/*static*/ 1.1803 +void 1.1804 +gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache) 1.1805 +{ 1.1806 + gfxFontCache* cache = static_cast<gfxFontCache*>(aCache); 1.1807 + cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nullptr); 1.1808 +} 1.1809 + 1.1810 +/*static*/ 1.1811 +PLDHashOperator 1.1812 +gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData) 1.1813 +{ 1.1814 + aHashEntry->mFont->ClearCachedWords(); 1.1815 + return PL_DHASH_NEXT; 1.1816 +} 1.1817 + 1.1818 +/*static*/ 1.1819 +size_t 1.1820 +gfxFontCache::AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry, 1.1821 + MallocSizeOf aMallocSizeOf, 1.1822 + void* aUserArg) 1.1823 +{ 1.1824 + HashEntry *entry = static_cast<HashEntry*>(aHashEntry); 1.1825 + FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg); 1.1826 + entry->mFont->AddSizeOfExcludingThis(aMallocSizeOf, sizes); 1.1827 + 1.1828 + // The entry's size is recorded in the |sizes| parameter, so we return zero 1.1829 + // here to the hashtable enumerator. 1.1830 + return 0; 1.1831 +} 1.1832 + 1.1833 +void 1.1834 +gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 1.1835 + FontCacheSizes* aSizes) const 1.1836 +{ 1.1837 + // TODO: add the overhead of the expiration tracker (generation arrays) 1.1838 + 1.1839 + aSizes->mFontInstances += 1.1840 + mFonts.SizeOfExcludingThis(AddSizeOfFontEntryExcludingThis, 1.1841 + aMallocSizeOf, aSizes); 1.1842 +} 1.1843 + 1.1844 +void 1.1845 +gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 1.1846 + FontCacheSizes* aSizes) const 1.1847 +{ 1.1848 + aSizes->mFontInstances += aMallocSizeOf(this); 1.1849 + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.1850 +} 1.1851 + 1.1852 +#define MAX_SSXX_VALUE 99 1.1853 +#define MAX_CVXX_VALUE 99 1.1854 + 1.1855 +static void 1.1856 +LookupAlternateValues(gfxFontFeatureValueSet *featureLookup, 1.1857 + const nsAString& aFamily, 1.1858 + const nsTArray<gfxAlternateValue>& altValue, 1.1859 + nsTArray<gfxFontFeature>& aFontFeatures) 1.1860 +{ 1.1861 + uint32_t numAlternates = altValue.Length(); 1.1862 + for (uint32_t i = 0; i < numAlternates; i++) { 1.1863 + const gfxAlternateValue& av = altValue.ElementAt(i); 1.1864 + nsAutoTArray<uint32_t,4> values; 1.1865 + 1.1866 + // map <family, name, feature> ==> <values> 1.1867 + bool found = 1.1868 + featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate, 1.1869 + av.value, values); 1.1870 + uint32_t numValues = values.Length(); 1.1871 + 1.1872 + // nothing defined, skip 1.1873 + if (!found || numValues == 0) { 1.1874 + continue; 1.1875 + } 1.1876 + 1.1877 + gfxFontFeature feature; 1.1878 + if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) { 1.1879 + NS_ASSERTION(numValues <= 2, 1.1880 + "too many values allowed for character-variant"); 1.1881 + // character-variant(12 3) ==> 'cv12' = 3 1.1882 + uint32_t nn = values.ElementAt(0); 1.1883 + // ignore values greater than 99 1.1884 + if (nn == 0 || nn > MAX_CVXX_VALUE) { 1.1885 + continue; 1.1886 + } 1.1887 + feature.mValue = 1; 1.1888 + if (numValues > 1) { 1.1889 + feature.mValue = values.ElementAt(1); 1.1890 + } 1.1891 + feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10)); 1.1892 + aFontFeatures.AppendElement(feature); 1.1893 + 1.1894 + } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) { 1.1895 + // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1 1.1896 + feature.mValue = 1; 1.1897 + for (uint32_t v = 0; v < numValues; v++) { 1.1898 + uint32_t nn = values.ElementAt(v); 1.1899 + if (nn == 0 || nn > MAX_SSXX_VALUE) { 1.1900 + continue; 1.1901 + } 1.1902 + feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10)); 1.1903 + aFontFeatures.AppendElement(feature); 1.1904 + } 1.1905 + 1.1906 + } else { 1.1907 + NS_ASSERTION(numValues == 1, 1.1908 + "too many values for font-specific font-variant-alternates"); 1.1909 + feature.mValue = values.ElementAt(0); 1.1910 + 1.1911 + switch (av.alternate) { 1.1912 + case NS_FONT_VARIANT_ALTERNATES_STYLISTIC: // salt 1.1913 + feature.mTag = HB_TAG('s','a','l','t'); 1.1914 + break; 1.1915 + case NS_FONT_VARIANT_ALTERNATES_SWASH: // swsh, cswh 1.1916 + feature.mTag = HB_TAG('s','w','s','h'); 1.1917 + aFontFeatures.AppendElement(feature); 1.1918 + feature.mTag = HB_TAG('c','s','w','h'); 1.1919 + break; 1.1920 + case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm 1.1921 + feature.mTag = HB_TAG('o','r','n','m'); 1.1922 + break; 1.1923 + case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt 1.1924 + feature.mTag = HB_TAG('n','a','l','t'); 1.1925 + break; 1.1926 + default: 1.1927 + feature.mTag = 0; 1.1928 + break; 1.1929 + } 1.1930 + 1.1931 + NS_ASSERTION(feature.mTag, "unsupported alternate type"); 1.1932 + if (!feature.mTag) { 1.1933 + continue; 1.1934 + } 1.1935 + aFontFeatures.AppendElement(feature); 1.1936 + } 1.1937 + } 1.1938 +} 1.1939 + 1.1940 +/* static */ bool 1.1941 +gfxFontShaper::MergeFontFeatures( 1.1942 + const gfxFontStyle *aStyle, 1.1943 + const nsTArray<gfxFontFeature>& aFontFeatures, 1.1944 + bool aDisableLigatures, 1.1945 + const nsAString& aFamilyName, 1.1946 + nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures) 1.1947 +{ 1.1948 + uint32_t numAlts = aStyle->alternateValues.Length(); 1.1949 + const nsTArray<gfxFontFeature>& styleRuleFeatures = 1.1950 + aStyle->featureSettings; 1.1951 + 1.1952 + // bail immediately if nothing to do 1.1953 + if (styleRuleFeatures.IsEmpty() && 1.1954 + aFontFeatures.IsEmpty() && 1.1955 + !aDisableLigatures && 1.1956 + numAlts == 0) { 1.1957 + return false; 1.1958 + } 1.1959 + 1.1960 + // Ligature features are enabled by default in the generic shaper, 1.1961 + // so we explicitly turn them off if necessary (for letter-spacing) 1.1962 + if (aDisableLigatures) { 1.1963 + aMergedFeatures.Put(HB_TAG('l','i','g','a'), 0); 1.1964 + aMergedFeatures.Put(HB_TAG('c','l','i','g'), 0); 1.1965 + } 1.1966 + 1.1967 + // add feature values from font 1.1968 + uint32_t i, count; 1.1969 + 1.1970 + count = aFontFeatures.Length(); 1.1971 + for (i = 0; i < count; i++) { 1.1972 + const gfxFontFeature& feature = aFontFeatures.ElementAt(i); 1.1973 + aMergedFeatures.Put(feature.mTag, feature.mValue); 1.1974 + } 1.1975 + 1.1976 + // add font-specific feature values from style rules 1.1977 + if (aStyle->featureValueLookup && numAlts > 0) { 1.1978 + nsAutoTArray<gfxFontFeature,4> featureList; 1.1979 + 1.1980 + // insert list of alternate feature settings 1.1981 + LookupAlternateValues(aStyle->featureValueLookup, aFamilyName, 1.1982 + aStyle->alternateValues, featureList); 1.1983 + 1.1984 + count = featureList.Length(); 1.1985 + for (i = 0; i < count; i++) { 1.1986 + const gfxFontFeature& feature = featureList.ElementAt(i); 1.1987 + aMergedFeatures.Put(feature.mTag, feature.mValue); 1.1988 + } 1.1989 + } 1.1990 + 1.1991 + // add feature values from style rules 1.1992 + count = styleRuleFeatures.Length(); 1.1993 + for (i = 0; i < count; i++) { 1.1994 + const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i); 1.1995 + aMergedFeatures.Put(feature.mTag, feature.mValue); 1.1996 + } 1.1997 + 1.1998 + return aMergedFeatures.Count() != 0; 1.1999 +} 1.2000 + 1.2001 +void 1.2002 +gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft) 1.2003 +{ 1.2004 + mAscent = std::max(mAscent, aOther.mAscent); 1.2005 + mDescent = std::max(mDescent, aOther.mDescent); 1.2006 + if (aOtherIsOnLeft) { 1.2007 + mBoundingBox = 1.2008 + (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox); 1.2009 + } else { 1.2010 + mBoundingBox = 1.2011 + mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0)); 1.2012 + } 1.2013 + mAdvanceWidth += aOther.mAdvanceWidth; 1.2014 +} 1.2015 + 1.2016 +gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle, 1.2017 + AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) : 1.2018 + mScaledFont(aScaledFont), 1.2019 + mFontEntry(aFontEntry), mIsValid(true), 1.2020 + mApplySyntheticBold(false), 1.2021 + mStyle(*aFontStyle), 1.2022 + mAdjustedSize(0.0), 1.2023 + mFUnitsConvFactor(0.0f), 1.2024 + mAntialiasOption(anAAOption) 1.2025 +{ 1.2026 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.2027 + ++gFontCount; 1.2028 +#endif 1.2029 + mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled); 1.2030 +} 1.2031 + 1.2032 +static PLDHashOperator 1.2033 +NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey, 1.2034 + void* aClosure) 1.2035 +{ 1.2036 + aKey->GetKey()->ForgetFont(); 1.2037 + return PL_DHASH_NEXT; 1.2038 +} 1.2039 + 1.2040 +gfxFont::~gfxFont() 1.2041 +{ 1.2042 + uint32_t i, count = mGlyphExtentsArray.Length(); 1.2043 + // We destroy the contents of mGlyphExtentsArray explicitly instead of 1.2044 + // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs 1.2045 + // of classes that lack a proper copy constructor 1.2046 + for (i = 0; i < count; ++i) { 1.2047 + delete mGlyphExtentsArray[i]; 1.2048 + } 1.2049 + 1.2050 + mFontEntry->NotifyFontDestroyed(this); 1.2051 + 1.2052 + if (mGlyphChangeObservers) { 1.2053 + mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr); 1.2054 + } 1.2055 +} 1.2056 + 1.2057 +gfxFloat 1.2058 +gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID) 1.2059 +{ 1.2060 + if (!SetupCairoFont(aCtx)) { 1.2061 + return 0; 1.2062 + } 1.2063 + if (ProvidesGlyphWidths()) { 1.2064 + return GetGlyphWidth(aCtx, aGID) / 65536.0; 1.2065 + } 1.2066 + if (mFUnitsConvFactor == 0.0f) { 1.2067 + GetMetrics(); 1.2068 + } 1.2069 + NS_ASSERTION(mFUnitsConvFactor > 0.0f, 1.2070 + "missing font unit conversion factor"); 1.2071 + if (!mHarfBuzzShaper) { 1.2072 + mHarfBuzzShaper = new gfxHarfBuzzShaper(this); 1.2073 + } 1.2074 + gfxHarfBuzzShaper* shaper = 1.2075 + static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get()); 1.2076 + if (!shaper->Initialize()) { 1.2077 + return 0; 1.2078 + } 1.2079 + return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0; 1.2080 +} 1.2081 + 1.2082 +/*static*/ 1.2083 +PLDHashOperator 1.2084 +gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData) 1.2085 +{ 1.2086 + if (!aEntry->mShapedWord) { 1.2087 + NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!"); 1.2088 + return PL_DHASH_REMOVE; 1.2089 + } 1.2090 + if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) { 1.2091 + return PL_DHASH_REMOVE; 1.2092 + } 1.2093 + return PL_DHASH_NEXT; 1.2094 +} 1.2095 + 1.2096 +static void 1.2097 +CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag, 1.2098 + uint32_t aFeatureIndex, hb_set_t *aLookups) 1.2099 +{ 1.2100 + uint32_t lookups[32]; 1.2101 + uint32_t i, len, offset; 1.2102 + 1.2103 + offset = 0; 1.2104 + do { 1.2105 + len = ArrayLength(lookups); 1.2106 + hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex, 1.2107 + offset, &len, lookups); 1.2108 + for (i = 0; i < len; i++) { 1.2109 + hb_set_add(aLookups, lookups[i]); 1.2110 + } 1.2111 + offset += len; 1.2112 + } while (len == ArrayLength(lookups)); 1.2113 +} 1.2114 + 1.2115 +static void 1.2116 +CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag, 1.2117 + const nsTHashtable<nsUint32HashKey>& 1.2118 + aSpecificFeatures, 1.2119 + hb_set_t *aOtherLookups, 1.2120 + hb_set_t *aSpecificFeatureLookups, 1.2121 + uint32_t aScriptIndex, uint32_t aLangIndex) 1.2122 +{ 1.2123 + uint32_t reqFeatureIndex; 1.2124 + if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag, 1.2125 + aScriptIndex, 1.2126 + aLangIndex, 1.2127 + &reqFeatureIndex)) { 1.2128 + CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex, 1.2129 + aOtherLookups); 1.2130 + } 1.2131 + 1.2132 + uint32_t featureIndexes[32]; 1.2133 + uint32_t i, len, offset; 1.2134 + 1.2135 + offset = 0; 1.2136 + do { 1.2137 + len = ArrayLength(featureIndexes); 1.2138 + hb_ot_layout_language_get_feature_indexes(aFace, aTableTag, 1.2139 + aScriptIndex, aLangIndex, 1.2140 + offset, &len, featureIndexes); 1.2141 + 1.2142 + for (i = 0; i < len; i++) { 1.2143 + uint32_t featureIndex = featureIndexes[i]; 1.2144 + 1.2145 + // get the feature tag 1.2146 + hb_tag_t featureTag; 1.2147 + uint32_t tagLen = 1; 1.2148 + hb_ot_layout_language_get_feature_tags(aFace, aTableTag, 1.2149 + aScriptIndex, aLangIndex, 1.2150 + offset + i, &tagLen, 1.2151 + &featureTag); 1.2152 + 1.2153 + // collect lookups 1.2154 + hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ? 1.2155 + aSpecificFeatureLookups : aOtherLookups; 1.2156 + CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups); 1.2157 + } 1.2158 + offset += len; 1.2159 + } while (len == ArrayLength(featureIndexes)); 1.2160 +} 1.2161 + 1.2162 +static bool 1.2163 +HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag, 1.2164 + hb_tag_t aScriptTag, uint32_t aScriptIndex, 1.2165 + uint16_t aGlyph, 1.2166 + const nsTHashtable<nsUint32HashKey>& 1.2167 + aDefaultFeatures, 1.2168 + bool& aHasDefaultFeatureWithGlyph) 1.2169 +{ 1.2170 + uint32_t numLangs, lang; 1.2171 + hb_set_t *defaultFeatureLookups = hb_set_create(); 1.2172 + hb_set_t *nonDefaultFeatureLookups = hb_set_create(); 1.2173 + 1.2174 + // default lang 1.2175 + CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures, 1.2176 + nonDefaultFeatureLookups, defaultFeatureLookups, 1.2177 + aScriptIndex, 1.2178 + HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 1.2179 + 1.2180 + // iterate over langs 1.2181 + numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag, 1.2182 + aScriptIndex, 0, 1.2183 + nullptr, nullptr); 1.2184 + for (lang = 0; lang < numLangs; lang++) { 1.2185 + CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures, 1.2186 + nonDefaultFeatureLookups, 1.2187 + defaultFeatureLookups, 1.2188 + aScriptIndex, lang); 1.2189 + } 1.2190 + 1.2191 + // look for the glyph among default feature lookups 1.2192 + aHasDefaultFeatureWithGlyph = false; 1.2193 + hb_set_t *glyphs = hb_set_create(); 1.2194 + hb_codepoint_t index = -1; 1.2195 + while (hb_set_next(defaultFeatureLookups, &index)) { 1.2196 + hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, 1.2197 + glyphs, glyphs, glyphs, 1.2198 + glyphs); 1.2199 + if (hb_set_has(glyphs, aGlyph)) { 1.2200 + aHasDefaultFeatureWithGlyph = true; 1.2201 + break; 1.2202 + } 1.2203 + } 1.2204 + 1.2205 + // look for the glyph among non-default feature lookups 1.2206 + // if no default feature lookups contained spaces 1.2207 + bool hasNonDefaultFeatureWithGlyph = false; 1.2208 + if (!aHasDefaultFeatureWithGlyph) { 1.2209 + hb_set_clear(glyphs); 1.2210 + index = -1; 1.2211 + while (hb_set_next(nonDefaultFeatureLookups, &index)) { 1.2212 + hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, 1.2213 + glyphs, glyphs, glyphs, 1.2214 + glyphs); 1.2215 + if (hb_set_has(glyphs, aGlyph)) { 1.2216 + hasNonDefaultFeatureWithGlyph = true; 1.2217 + break; 1.2218 + } 1.2219 + } 1.2220 + } 1.2221 + 1.2222 + hb_set_destroy(glyphs); 1.2223 + hb_set_destroy(defaultFeatureLookups); 1.2224 + hb_set_destroy(nonDefaultFeatureLookups); 1.2225 + 1.2226 + return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph; 1.2227 +} 1.2228 + 1.2229 +static void 1.2230 +HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph, 1.2231 + hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific, 1.2232 + uint16_t aGlyph) 1.2233 +{ 1.2234 + // iterate over the scripts in the font 1.2235 + uint32_t numScripts, numLangs, script, lang; 1.2236 + hb_set_t *otherLookups = hb_set_create(); 1.2237 + hb_set_t *specificFeatureLookups = hb_set_create(); 1.2238 + nsTHashtable<nsUint32HashKey> specificFeature; 1.2239 + 1.2240 + specificFeature.PutEntry(aSpecificFeature); 1.2241 + 1.2242 + numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0, 1.2243 + nullptr, nullptr); 1.2244 + 1.2245 + for (script = 0; script < numScripts; script++) { 1.2246 + // default lang 1.2247 + CollectLookupsByLanguage(aFace, aTableTag, specificFeature, 1.2248 + otherLookups, specificFeatureLookups, 1.2249 + script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX); 1.2250 + 1.2251 + // iterate over langs 1.2252 + numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS, 1.2253 + script, 0, 1.2254 + nullptr, nullptr); 1.2255 + for (lang = 0; lang < numLangs; lang++) { 1.2256 + CollectLookupsByLanguage(aFace, aTableTag, specificFeature, 1.2257 + otherLookups, specificFeatureLookups, 1.2258 + script, lang); 1.2259 + } 1.2260 + } 1.2261 + 1.2262 + // look for the glyph among non-specific feature lookups 1.2263 + hb_set_t *glyphs = hb_set_create(); 1.2264 + hb_codepoint_t index = -1; 1.2265 + while (hb_set_next(otherLookups, &index)) { 1.2266 + hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, 1.2267 + glyphs, glyphs, glyphs, 1.2268 + glyphs); 1.2269 + if (hb_set_has(glyphs, aGlyph)) { 1.2270 + aHasGlyph = true; 1.2271 + break; 1.2272 + } 1.2273 + } 1.2274 + 1.2275 + // look for the glyph among specific feature lookups 1.2276 + hb_set_clear(glyphs); 1.2277 + index = -1; 1.2278 + while (hb_set_next(specificFeatureLookups, &index)) { 1.2279 + hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, 1.2280 + glyphs, glyphs, glyphs, 1.2281 + glyphs); 1.2282 + if (hb_set_has(glyphs, aGlyph)) { 1.2283 + aHasGlyphSpecific = true; 1.2284 + break; 1.2285 + } 1.2286 + } 1.2287 + 1.2288 + hb_set_destroy(glyphs); 1.2289 + hb_set_destroy(specificFeatureLookups); 1.2290 + hb_set_destroy(otherLookups); 1.2291 +} 1.2292 + 1.2293 +nsDataHashtable<nsUint32HashKey, int32_t> *gfxFont::sScriptTagToCode = nullptr; 1.2294 +nsTHashtable<nsUint32HashKey> *gfxFont::sDefaultFeatures = nullptr; 1.2295 + 1.2296 +static inline bool 1.2297 +HasSubstitution(uint32_t *aBitVector, uint32_t aBit) { 1.2298 + return (aBitVector[aBit >> 5] & (1 << (aBit & 0x1f))) != 0; 1.2299 +} 1.2300 + 1.2301 +// union of all default substitution features across scripts 1.2302 +static const hb_tag_t defaultFeatures[] = { 1.2303 + HB_TAG('a','b','v','f'), 1.2304 + HB_TAG('a','b','v','s'), 1.2305 + HB_TAG('a','k','h','n'), 1.2306 + HB_TAG('b','l','w','f'), 1.2307 + HB_TAG('b','l','w','s'), 1.2308 + HB_TAG('c','a','l','t'), 1.2309 + HB_TAG('c','c','m','p'), 1.2310 + HB_TAG('c','f','a','r'), 1.2311 + HB_TAG('c','j','c','t'), 1.2312 + HB_TAG('c','l','i','g'), 1.2313 + HB_TAG('f','i','n','2'), 1.2314 + HB_TAG('f','i','n','3'), 1.2315 + HB_TAG('f','i','n','a'), 1.2316 + HB_TAG('h','a','l','f'), 1.2317 + HB_TAG('h','a','l','n'), 1.2318 + HB_TAG('i','n','i','t'), 1.2319 + HB_TAG('i','s','o','l'), 1.2320 + HB_TAG('l','i','g','a'), 1.2321 + HB_TAG('l','j','m','o'), 1.2322 + HB_TAG('l','o','c','l'), 1.2323 + HB_TAG('l','t','r','a'), 1.2324 + HB_TAG('l','t','r','m'), 1.2325 + HB_TAG('m','e','d','2'), 1.2326 + HB_TAG('m','e','d','i'), 1.2327 + HB_TAG('m','s','e','t'), 1.2328 + HB_TAG('n','u','k','t'), 1.2329 + HB_TAG('p','r','e','f'), 1.2330 + HB_TAG('p','r','e','s'), 1.2331 + HB_TAG('p','s','t','f'), 1.2332 + HB_TAG('p','s','t','s'), 1.2333 + HB_TAG('r','c','l','t'), 1.2334 + HB_TAG('r','l','i','g'), 1.2335 + HB_TAG('r','k','r','f'), 1.2336 + HB_TAG('r','p','h','f'), 1.2337 + HB_TAG('r','t','l','a'), 1.2338 + HB_TAG('r','t','l','m'), 1.2339 + HB_TAG('t','j','m','o'), 1.2340 + HB_TAG('v','a','t','u'), 1.2341 + HB_TAG('v','e','r','t'), 1.2342 + HB_TAG('v','j','m','o') 1.2343 +}; 1.2344 + 1.2345 +void 1.2346 +gfxFont::CheckForFeaturesInvolvingSpace() 1.2347 +{ 1.2348 + mFontEntry->mHasSpaceFeaturesInitialized = true; 1.2349 + 1.2350 +#ifdef PR_LOGGING 1.2351 + bool log = LOG_FONTINIT_ENABLED(); 1.2352 + TimeStamp start; 1.2353 + if (MOZ_UNLIKELY(log)) { 1.2354 + start = TimeStamp::Now(); 1.2355 + } 1.2356 +#endif 1.2357 + 1.2358 + bool result = false; 1.2359 + 1.2360 + uint32_t spaceGlyph = GetSpaceGlyph(); 1.2361 + if (!spaceGlyph) { 1.2362 + return; 1.2363 + } 1.2364 + 1.2365 + hb_face_t *face = GetFontEntry()->GetHBFace(); 1.2366 + 1.2367 + // GSUB lookups - examine per script 1.2368 + if (hb_ot_layout_has_substitution(face)) { 1.2369 + 1.2370 + // set up the script ==> code hashtable if needed 1.2371 + if (!sScriptTagToCode) { 1.2372 + sScriptTagToCode = 1.2373 + new nsDataHashtable<nsUint32HashKey, 1.2374 + int32_t>(MOZ_NUM_SCRIPT_CODES); 1.2375 + sScriptTagToCode->Put(HB_TAG('D','F','L','T'), MOZ_SCRIPT_COMMON); 1.2376 + for (int32_t s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) { 1.2377 + hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s)); 1.2378 + hb_tag_t s1, s2; 1.2379 + hb_ot_tags_from_script(scriptTag, &s1, &s2); 1.2380 + sScriptTagToCode->Put(s1, s); 1.2381 + if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) { 1.2382 + sScriptTagToCode->Put(s2, s); 1.2383 + } 1.2384 + } 1.2385 + 1.2386 + uint32_t numDefaultFeatures = ArrayLength(defaultFeatures); 1.2387 + sDefaultFeatures = 1.2388 + new nsTHashtable<nsUint32HashKey>(numDefaultFeatures); 1.2389 + for (uint32_t i = 0; i < numDefaultFeatures; i++) { 1.2390 + sDefaultFeatures->PutEntry(defaultFeatures[i]); 1.2391 + } 1.2392 + } 1.2393 + 1.2394 + // iterate over the scripts in the font 1.2395 + hb_tag_t scriptTags[8]; 1.2396 + 1.2397 + uint32_t len, offset = 0; 1.2398 + do { 1.2399 + len = ArrayLength(scriptTags); 1.2400 + hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset, 1.2401 + &len, scriptTags); 1.2402 + for (uint32_t i = 0; i < len; i++) { 1.2403 + bool isDefaultFeature = false; 1.2404 + int32_t s; 1.2405 + if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB, 1.2406 + scriptTags[i], offset + i, 1.2407 + spaceGlyph, 1.2408 + *sDefaultFeatures, 1.2409 + isDefaultFeature) || 1.2410 + !sScriptTagToCode->Get(scriptTags[i], &s)) 1.2411 + { 1.2412 + continue; 1.2413 + } 1.2414 + result = true; 1.2415 + uint32_t index = s >> 5; 1.2416 + uint32_t bit = s & 0x1f; 1.2417 + if (isDefaultFeature) { 1.2418 + mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit); 1.2419 + } else { 1.2420 + mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit); 1.2421 + } 1.2422 + } 1.2423 + offset += len; 1.2424 + } while (len == ArrayLength(scriptTags)); 1.2425 + } 1.2426 + 1.2427 + // spaces in default features of default script? 1.2428 + // ==> can't use word cache, skip GPOS analysis 1.2429 + bool canUseWordCache = true; 1.2430 + if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, 1.2431 + MOZ_SCRIPT_COMMON)) { 1.2432 + canUseWordCache = false; 1.2433 + } 1.2434 + 1.2435 + // GPOS lookups - distinguish kerning from non-kerning features 1.2436 + mFontEntry->mHasSpaceFeaturesKerning = false; 1.2437 + mFontEntry->mHasSpaceFeaturesNonKerning = false; 1.2438 + 1.2439 + if (canUseWordCache && hb_ot_layout_has_positioning(face)) { 1.2440 + bool hasKerning = false, hasNonKerning = false; 1.2441 + HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning, 1.2442 + HB_TAG('k','e','r','n'), hasKerning, spaceGlyph); 1.2443 + if (hasKerning || hasNonKerning) { 1.2444 + result = true; 1.2445 + } 1.2446 + mFontEntry->mHasSpaceFeaturesKerning = hasKerning; 1.2447 + mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning; 1.2448 + } 1.2449 + 1.2450 + hb_face_destroy(face); 1.2451 + mFontEntry->mHasSpaceFeatures = result; 1.2452 + 1.2453 +#ifdef PR_LOGGING 1.2454 + if (MOZ_UNLIKELY(log)) { 1.2455 + TimeDuration elapsed = TimeStamp::Now() - start; 1.2456 + LOG_FONTINIT(( 1.2457 + "(fontinit-spacelookups) font: %s - " 1.2458 + "subst default: %8.8x %8.8x %8.8x %8.8x " 1.2459 + "subst non-default: %8.8x %8.8x %8.8x %8.8x " 1.2460 + "kerning: %s non-kerning: %s time: %6.3f\n", 1.2461 + NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(), 1.2462 + mFontEntry->mDefaultSubSpaceFeatures[3], 1.2463 + mFontEntry->mDefaultSubSpaceFeatures[2], 1.2464 + mFontEntry->mDefaultSubSpaceFeatures[1], 1.2465 + mFontEntry->mDefaultSubSpaceFeatures[0], 1.2466 + mFontEntry->mNonDefaultSubSpaceFeatures[3], 1.2467 + mFontEntry->mNonDefaultSubSpaceFeatures[2], 1.2468 + mFontEntry->mNonDefaultSubSpaceFeatures[1], 1.2469 + mFontEntry->mNonDefaultSubSpaceFeatures[0], 1.2470 + (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"), 1.2471 + (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"), 1.2472 + elapsed.ToMilliseconds() 1.2473 + )); 1.2474 + } 1.2475 +#endif 1.2476 +} 1.2477 + 1.2478 +bool 1.2479 +gfxFont::HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript) 1.2480 +{ 1.2481 + NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized, 1.2482 + "need to initialize space lookup flags"); 1.2483 + NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code"); 1.2484 + if (aRunScript == MOZ_SCRIPT_INVALID || 1.2485 + aRunScript >= MOZ_NUM_SCRIPT_CODES) { 1.2486 + return false; 1.2487 + } 1.2488 + 1.2489 + // default features have space lookups ==> true 1.2490 + if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, 1.2491 + MOZ_SCRIPT_COMMON) || 1.2492 + HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, 1.2493 + aRunScript)) 1.2494 + { 1.2495 + return true; 1.2496 + } 1.2497 + 1.2498 + // non-default features have space lookups and some type of 1.2499 + // font feature, in font or style is specified ==> true 1.2500 + if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, 1.2501 + MOZ_SCRIPT_COMMON) || 1.2502 + HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, 1.2503 + aRunScript)) && 1.2504 + (!mStyle.featureSettings.IsEmpty() || 1.2505 + !mFontEntry->mFeatureSettings.IsEmpty())) 1.2506 + { 1.2507 + return true; 1.2508 + } 1.2509 + 1.2510 + return false; 1.2511 +} 1.2512 + 1.2513 +bool 1.2514 +gfxFont::SpaceMayParticipateInShaping(int32_t aRunScript) 1.2515 +{ 1.2516 + // avoid checking fonts known not to include default space-dependent features 1.2517 + if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) { 1.2518 + if (!mKerningSet && mStyle.featureSettings.IsEmpty() && 1.2519 + mFontEntry->mFeatureSettings.IsEmpty()) { 1.2520 + return false; 1.2521 + } 1.2522 + } 1.2523 + 1.2524 + // We record the presence of space-dependent features in the font entry 1.2525 + // so that subsequent instantiations for the same font face won't 1.2526 + // require us to re-check the tables; however, the actual check is done 1.2527 + // by gfxFont because not all font entry subclasses know how to create 1.2528 + // a harfbuzz face for introspection. 1.2529 + if (!mFontEntry->mHasSpaceFeaturesInitialized) { 1.2530 + CheckForFeaturesInvolvingSpace(); 1.2531 + } 1.2532 + 1.2533 + if (!mFontEntry->mHasSpaceFeatures) { 1.2534 + return false; 1.2535 + } 1.2536 + 1.2537 + // if font has substitution rules or non-kerning positioning rules 1.2538 + // that involve spaces, bypass 1.2539 + if (HasSubstitutionRulesWithSpaceLookups(aRunScript) || 1.2540 + mFontEntry->mHasSpaceFeaturesNonKerning) { 1.2541 + return true; 1.2542 + } 1.2543 + 1.2544 + // if kerning explicitly enabled/disabled via font-feature-settings or 1.2545 + // font-kerning and kerning rules use spaces, only bypass when enabled 1.2546 + if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) { 1.2547 + return mKerningEnabled; 1.2548 + } 1.2549 + 1.2550 + return false; 1.2551 +} 1.2552 + 1.2553 +bool 1.2554 +gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) 1.2555 +{ 1.2556 + aFeatureOn = false; 1.2557 + 1.2558 + if (mStyle.featureSettings.IsEmpty() && 1.2559 + GetFontEntry()->mFeatureSettings.IsEmpty()) { 1.2560 + return false; 1.2561 + } 1.2562 + 1.2563 + // add feature values from font 1.2564 + bool featureSet = false; 1.2565 + uint32_t i, count; 1.2566 + 1.2567 + nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings; 1.2568 + count = fontFeatures.Length(); 1.2569 + for (i = 0; i < count; i++) { 1.2570 + const gfxFontFeature& feature = fontFeatures.ElementAt(i); 1.2571 + if (feature.mTag == aFeature) { 1.2572 + featureSet = true; 1.2573 + aFeatureOn = (feature.mValue != 0); 1.2574 + } 1.2575 + } 1.2576 + 1.2577 + // add feature values from style rules 1.2578 + nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings; 1.2579 + count = styleFeatures.Length(); 1.2580 + for (i = 0; i < count; i++) { 1.2581 + const gfxFontFeature& feature = styleFeatures.ElementAt(i); 1.2582 + if (feature.mTag == aFeature) { 1.2583 + featureSet = true; 1.2584 + aFeatureOn = (feature.mValue != 0); 1.2585 + } 1.2586 + } 1.2587 + 1.2588 + return featureSet; 1.2589 +} 1.2590 + 1.2591 +/** 1.2592 + * A helper function in case we need to do any rounding or other 1.2593 + * processing here. 1.2594 + */ 1.2595 +#define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \ 1.2596 + (double(aAppUnits)*double(aDevUnitsPerAppUnit)) 1.2597 + 1.2598 +struct GlyphBuffer { 1.2599 +#define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t)) 1.2600 + cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE]; 1.2601 + unsigned int mNumGlyphs; 1.2602 + 1.2603 + GlyphBuffer() 1.2604 + : mNumGlyphs(0) { } 1.2605 + 1.2606 + cairo_glyph_t *AppendGlyph() { 1.2607 + return &mGlyphBuffer[mNumGlyphs++]; 1.2608 + } 1.2609 + 1.2610 + void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse, 1.2611 + gfxTextContextPaint *aContextPaint, 1.2612 + const gfxMatrix& aGlobalMatrix, bool aFinish = false) { 1.2613 + // Ensure there's enough room for a glyph to be added to the buffer 1.2614 + // and we actually have glyphs to draw 1.2615 + if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) { 1.2616 + return; 1.2617 + } 1.2618 + 1.2619 + if (aReverse) { 1.2620 + for (uint32_t i = 0; i < mNumGlyphs/2; ++i) { 1.2621 + cairo_glyph_t tmp = mGlyphBuffer[i]; 1.2622 + mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i]; 1.2623 + mGlyphBuffer[mNumGlyphs - 1 - i] = tmp; 1.2624 + } 1.2625 + } 1.2626 + 1.2627 + if (aDrawMode == DrawMode::GLYPH_PATH) { 1.2628 + cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs); 1.2629 + } else { 1.2630 + if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == 1.2631 + (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) { 1.2632 + FlushStroke(aCR, aContextPaint, aGlobalMatrix); 1.2633 + } 1.2634 + if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) { 1.2635 + PROFILER_LABEL("GlyphBuffer", "cairo_show_glyphs"); 1.2636 + nsRefPtr<gfxPattern> pattern; 1.2637 + if (aContextPaint && 1.2638 + !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) { 1.2639 + cairo_save(aCR); 1.2640 + cairo_set_source(aCR, pattern->CairoPattern()); 1.2641 + } 1.2642 + 1.2643 + cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs); 1.2644 + 1.2645 + if (pattern) { 1.2646 + cairo_restore(aCR); 1.2647 + } 1.2648 + } 1.2649 + if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == 1.2650 + int(DrawMode::GLYPH_STROKE)) { 1.2651 + FlushStroke(aCR, aContextPaint, aGlobalMatrix); 1.2652 + } 1.2653 + } 1.2654 + 1.2655 + mNumGlyphs = 0; 1.2656 + } 1.2657 + 1.2658 +private: 1.2659 + void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint, 1.2660 + const gfxMatrix& aGlobalMatrix) { 1.2661 + nsRefPtr<gfxPattern> pattern; 1.2662 + if (aContextPaint && 1.2663 + !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) { 1.2664 + cairo_save(aCR); 1.2665 + cairo_set_source(aCR, pattern->CairoPattern()); 1.2666 + } 1.2667 + 1.2668 + cairo_new_path(aCR); 1.2669 + cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs); 1.2670 + cairo_stroke(aCR); 1.2671 + 1.2672 + if (pattern) { 1.2673 + cairo_restore(aCR); 1.2674 + } 1.2675 + } 1.2676 + 1.2677 +#undef GLYPH_BUFFER_SIZE 1.2678 +}; 1.2679 + 1.2680 +static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) { 1.2681 + switch (aAAOption) { 1.2682 + case gfxFont::kAntialiasSubpixel: 1.2683 + return AntialiasMode::SUBPIXEL; 1.2684 + case gfxFont::kAntialiasGrayscale: 1.2685 + return AntialiasMode::GRAY; 1.2686 + case gfxFont::kAntialiasNone: 1.2687 + return AntialiasMode::NONE; 1.2688 + default: 1.2689 + return AntialiasMode::DEFAULT; 1.2690 + } 1.2691 +} 1.2692 + 1.2693 +struct GlyphBufferAzure { 1.2694 +#define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph)) 1.2695 + Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE]; 1.2696 + unsigned int mNumGlyphs; 1.2697 + 1.2698 + GlyphBufferAzure() 1.2699 + : mNumGlyphs(0) { } 1.2700 + 1.2701 + Glyph *AppendGlyph() { 1.2702 + return &mGlyphBuffer[mNumGlyphs++]; 1.2703 + } 1.2704 + 1.2705 + void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont, 1.2706 + DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions, 1.2707 + gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions, 1.2708 + bool aFinish = false) 1.2709 + { 1.2710 + // Ensure there's enough room for a glyph to be added to the buffer 1.2711 + if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) { 1.2712 + return; 1.2713 + } 1.2714 + 1.2715 + if (aReverse) { 1.2716 + Glyph *begin = &mGlyphBuffer[0]; 1.2717 + Glyph *end = &mGlyphBuffer[mNumGlyphs]; 1.2718 + std::reverse(begin, end); 1.2719 + } 1.2720 + 1.2721 + gfx::GlyphBuffer buf; 1.2722 + buf.mGlyphs = mGlyphBuffer; 1.2723 + buf.mNumGlyphs = mNumGlyphs; 1.2724 + 1.2725 + gfxContext::AzureState state = aThebesContext->CurrentState(); 1.2726 + if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == 1.2727 + (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) { 1.2728 + FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state); 1.2729 + } 1.2730 + if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) { 1.2731 + if (state.pattern || aContextPaint) { 1.2732 + Pattern *pat; 1.2733 + 1.2734 + nsRefPtr<gfxPattern> fillPattern; 1.2735 + if (!aContextPaint || 1.2736 + !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) { 1.2737 + if (state.pattern) { 1.2738 + pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr); 1.2739 + } else { 1.2740 + pat = nullptr; 1.2741 + } 1.2742 + } else { 1.2743 + pat = fillPattern->GetPattern(aDT); 1.2744 + } 1.2745 + 1.2746 + if (pat) { 1.2747 + Matrix saved; 1.2748 + Matrix *mat = nullptr; 1.2749 + if (aInvFontMatrix) { 1.2750 + // The brush matrix needs to be multiplied with the inverted matrix 1.2751 + // as well, to move the brush into the space of the glyphs. Before 1.2752 + // the render target transformation 1.2753 + 1.2754 + // This relies on the returned Pattern not to be reused by 1.2755 + // others, but regenerated on GetPattern calls. This is true! 1.2756 + if (pat->GetType() == PatternType::LINEAR_GRADIENT) { 1.2757 + mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix; 1.2758 + } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) { 1.2759 + mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix; 1.2760 + } else if (pat->GetType() == PatternType::SURFACE) { 1.2761 + mat = &static_cast<SurfacePattern*>(pat)->mMatrix; 1.2762 + } 1.2763 + 1.2764 + if (mat) { 1.2765 + saved = *mat; 1.2766 + *mat = (*mat) * (*aInvFontMatrix); 1.2767 + } 1.2768 + } 1.2769 + 1.2770 + aDT->FillGlyphs(aFont, buf, *pat, 1.2771 + aDrawOptions, aOptions); 1.2772 + 1.2773 + if (mat) { 1.2774 + *mat = saved; 1.2775 + } 1.2776 + } 1.2777 + } else if (state.sourceSurface) { 1.2778 + aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface, 1.2779 + ExtendMode::CLAMP, 1.2780 + state.surfTransform), 1.2781 + aDrawOptions, aOptions); 1.2782 + } else { 1.2783 + aDT->FillGlyphs(aFont, buf, ColorPattern(state.color), 1.2784 + aDrawOptions, aOptions); 1.2785 + } 1.2786 + } 1.2787 + if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) { 1.2788 + aThebesContext->EnsurePathBuilder(); 1.2789 + Matrix mat = aDT->GetTransform(); 1.2790 + aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, aDT->GetType(), &mat); 1.2791 + } 1.2792 + if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) == 1.2793 + int(DrawMode::GLYPH_STROKE)) { 1.2794 + FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state); 1.2795 + } 1.2796 + 1.2797 + mNumGlyphs = 0; 1.2798 + } 1.2799 + 1.2800 +private: 1.2801 + void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, 1.2802 + ScaledFont *aFont, gfxContext *aThebesContext, 1.2803 + gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState) 1.2804 + { 1.2805 + RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT); 1.2806 + if (aContextPaint) { 1.2807 + nsRefPtr<gfxPattern> strokePattern = 1.2808 + aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix()); 1.2809 + if (strokePattern) { 1.2810 + aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions); 1.2811 + } 1.2812 + } 1.2813 + } 1.2814 + 1.2815 +#undef GLYPH_BUFFER_SIZE 1.2816 +}; 1.2817 + 1.2818 +// Bug 674909. When synthetic bolding text by drawing twice, need to 1.2819 +// render using a pixel offset in device pixels, otherwise text 1.2820 +// doesn't appear bolded, it appears as if a bad text shadow exists 1.2821 +// when a non-identity transform exists. Use an offset factor so that 1.2822 +// the second draw occurs at a constant offset in device pixels. 1.2823 + 1.2824 +double 1.2825 +gfxFont::CalcXScale(gfxContext *aContext) 1.2826 +{ 1.2827 + // determine magnitude of a 1px x offset in device space 1.2828 + gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0)); 1.2829 + if (t.width == 1.0 && t.height == 0.0) { 1.2830 + // short-circuit the most common case to avoid sqrt() and division 1.2831 + return 1.0; 1.2832 + } 1.2833 + 1.2834 + double m = sqrt(t.width * t.width + t.height * t.height); 1.2835 + 1.2836 + NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding"); 1.2837 + if (m == 0.0) { 1.2838 + return 0.0; // effectively disables offset 1.2839 + } 1.2840 + 1.2841 + // scale factor so that offsets are 1px in device pixels 1.2842 + return 1.0 / m; 1.2843 +} 1.2844 + 1.2845 +static DrawMode 1.2846 +ForcePaintingDrawMode(DrawMode aDrawMode) 1.2847 +{ 1.2848 + return aDrawMode == DrawMode::GLYPH_PATH ? 1.2849 + DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) : 1.2850 + aDrawMode; 1.2851 +} 1.2852 + 1.2853 +void 1.2854 +gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, 1.2855 + gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt, 1.2856 + Spacing *aSpacing, gfxTextContextPaint *aContextPaint, 1.2857 + gfxTextRunDrawCallbacks *aCallbacks) 1.2858 +{ 1.2859 + NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)), 1.2860 + "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH"); 1.2861 + 1.2862 + if (aStart >= aEnd) 1.2863 + return; 1.2864 + 1.2865 + const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs(); 1.2866 + const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); 1.2867 + const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit); 1.2868 + bool isRTL = aTextRun->IsRightToLeft(); 1.2869 + double direction = aTextRun->GetDirection(); 1.2870 + gfxMatrix globalMatrix = aContext->CurrentMatrix(); 1.2871 + 1.2872 + bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this); 1.2873 + nsAutoPtr<gfxTextContextPaint> contextPaint; 1.2874 + if (haveSVGGlyphs && !aContextPaint) { 1.2875 + // If no pattern is specified for fill, use the current pattern 1.2876 + NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text"); 1.2877 + nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern(); 1.2878 + contextPaint = new SimpleTextContextPaint(fillPattern, nullptr, 1.2879 + aContext->CurrentMatrix()); 1.2880 + aContextPaint = contextPaint; 1.2881 + } 1.2882 + 1.2883 + // synthetic-bold strikes are each offset one device pixel in run direction 1.2884 + // (these values are only needed if IsSyntheticBold() is true) 1.2885 + double synBoldOnePixelOffset = 0; 1.2886 + int32_t strikes = 1; 1.2887 + if (IsSyntheticBold()) { 1.2888 + double xscale = CalcXScale(aContext); 1.2889 + synBoldOnePixelOffset = direction * xscale; 1.2890 + if (xscale != 0.0) { 1.2891 + // use as many strikes as needed for the the increased advance 1.2892 + strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale); 1.2893 + } 1.2894 + } 1.2895 + 1.2896 + uint32_t i; 1.2897 + // Current position in appunits 1.2898 + double x = aPt->x; 1.2899 + double y = aPt->y; 1.2900 + 1.2901 + cairo_t *cr = aContext->GetCairo(); 1.2902 + RefPtr<DrawTarget> dt = aContext->GetDrawTarget(); 1.2903 + 1.2904 + bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs; 1.2905 + bool emittedGlyphs = false; 1.2906 + 1.2907 + if (aContext->IsCairo()) { 1.2908 + bool success = SetupCairoFont(aContext); 1.2909 + if (MOZ_UNLIKELY(!success)) 1.2910 + return; 1.2911 + 1.2912 + ::GlyphBuffer glyphs; 1.2913 + cairo_glyph_t *glyph; 1.2914 + 1.2915 + if (aSpacing) { 1.2916 + x += direction*aSpacing[0].mBefore; 1.2917 + } 1.2918 + for (i = aStart; i < aEnd; ++i) { 1.2919 + const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i]; 1.2920 + if (glyphData->IsSimpleGlyph()) { 1.2921 + double advance = glyphData->GetSimpleAdvance(); 1.2922 + double glyphX; 1.2923 + if (isRTL) { 1.2924 + x -= advance; 1.2925 + glyphX = x; 1.2926 + } else { 1.2927 + glyphX = x; 1.2928 + x += advance; 1.2929 + } 1.2930 + 1.2931 + if (haveSVGGlyphs) { 1.2932 + if (!paintSVGGlyphs) { 1.2933 + continue; 1.2934 + } 1.2935 + gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.2936 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.2937 + DrawMode mode = ForcePaintingDrawMode(aDrawMode); 1.2938 + if (RenderSVGGlyph(aContext, point, mode, 1.2939 + glyphData->GetSimpleGlyph(), aContextPaint, 1.2940 + aCallbacks, emittedGlyphs)) { 1.2941 + continue; 1.2942 + } 1.2943 + } 1.2944 + 1.2945 + // Perhaps we should put a scale in the cairo context instead of 1.2946 + // doing this scaling here... 1.2947 + // Multiplying by the reciprocal may introduce tiny error here, 1.2948 + // but we assume cairo is going to round coordinates at some stage 1.2949 + // and this is faster 1.2950 + glyph = glyphs.AppendGlyph(); 1.2951 + glyph->index = glyphData->GetSimpleGlyph(); 1.2952 + glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); 1.2953 + glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit); 1.2954 + glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix); 1.2955 + 1.2956 + // synthetic bolding by multi-striking with 1-pixel offsets 1.2957 + // at least once, more if there's room (large font sizes) 1.2958 + if (IsSyntheticBold()) { 1.2959 + double strikeOffset = synBoldOnePixelOffset; 1.2960 + int32_t strikeCount = strikes; 1.2961 + do { 1.2962 + cairo_glyph_t *doubleglyph; 1.2963 + doubleglyph = glyphs.AppendGlyph(); 1.2964 + doubleglyph->index = glyph->index; 1.2965 + doubleglyph->x = 1.2966 + ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit, 1.2967 + devUnitsPerAppUnit); 1.2968 + doubleglyph->y = glyph->y; 1.2969 + strikeOffset += synBoldOnePixelOffset; 1.2970 + glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix); 1.2971 + } while (--strikeCount > 0); 1.2972 + } 1.2973 + emittedGlyphs = true; 1.2974 + } else { 1.2975 + uint32_t glyphCount = glyphData->GetGlyphCount(); 1.2976 + if (glyphCount > 0) { 1.2977 + const gfxTextRun::DetailedGlyph *details = 1.2978 + aTextRun->GetDetailedGlyphs(i); 1.2979 + NS_ASSERTION(details, "detailedGlyph should not be missing!"); 1.2980 + double advance; 1.2981 + for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) { 1.2982 + advance = details->mAdvance; 1.2983 + if (glyphData->IsMissing()) { 1.2984 + // default ignorable characters will have zero advance width. 1.2985 + // we don't have to draw the hexbox for them 1.2986 + if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) { 1.2987 + double glyphX = x; 1.2988 + if (isRTL) { 1.2989 + glyphX -= advance; 1.2990 + } 1.2991 + gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.2992 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.2993 + gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit); 1.2994 + gfxFloat height = GetMetrics().maxAscent; 1.2995 + gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); 1.2996 + gfxFontMissingGlyphs::DrawMissingGlyph(aContext, 1.2997 + glyphRect, 1.2998 + details->mGlyphID, 1.2999 + appUnitsPerDevUnit); 1.3000 + } 1.3001 + } else { 1.3002 + double glyphX = x + details->mXOffset; 1.3003 + if (isRTL) { 1.3004 + glyphX -= advance; 1.3005 + } 1.3006 + 1.3007 + gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.3008 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.3009 + 1.3010 + if (haveSVGGlyphs) { 1.3011 + if (!paintSVGGlyphs) { 1.3012 + continue; 1.3013 + } 1.3014 + DrawMode mode = ForcePaintingDrawMode(aDrawMode); 1.3015 + if (RenderSVGGlyph(aContext, point, mode, 1.3016 + details->mGlyphID, 1.3017 + aContextPaint, aCallbacks, 1.3018 + emittedGlyphs)) { 1.3019 + continue; 1.3020 + } 1.3021 + } 1.3022 + 1.3023 + glyph = glyphs.AppendGlyph(); 1.3024 + glyph->index = details->mGlyphID; 1.3025 + glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); 1.3026 + glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); 1.3027 + glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix); 1.3028 + 1.3029 + if (IsSyntheticBold()) { 1.3030 + double strikeOffset = synBoldOnePixelOffset; 1.3031 + int32_t strikeCount = strikes; 1.3032 + do { 1.3033 + cairo_glyph_t *doubleglyph; 1.3034 + doubleglyph = glyphs.AppendGlyph(); 1.3035 + doubleglyph->index = glyph->index; 1.3036 + doubleglyph->x = 1.3037 + ToDeviceUnits(glyphX + strikeOffset * 1.3038 + appUnitsPerDevUnit, 1.3039 + devUnitsPerAppUnit); 1.3040 + doubleglyph->y = glyph->y; 1.3041 + strikeOffset += synBoldOnePixelOffset; 1.3042 + glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix); 1.3043 + } while (--strikeCount > 0); 1.3044 + } 1.3045 + emittedGlyphs = true; 1.3046 + } 1.3047 + } 1.3048 + } 1.3049 + } 1.3050 + 1.3051 + if (aSpacing) { 1.3052 + double space = aSpacing[i - aStart].mAfter; 1.3053 + if (i + 1 < aEnd) { 1.3054 + space += aSpacing[i + 1 - aStart].mBefore; 1.3055 + } 1.3056 + x += direction*space; 1.3057 + } 1.3058 + } 1.3059 + 1.3060 + if (gfxFontTestStore::CurrentStore()) { 1.3061 + /* This assumes that the tests won't have anything that results 1.3062 + * in more than GLYPH_BUFFER_SIZE glyphs. Do this before we 1.3063 + * flush, since that'll blow away the num_glyphs. 1.3064 + */ 1.3065 + gfxFontTestStore::CurrentStore()->AddItem(GetName(), 1.3066 + glyphs.mGlyphBuffer, 1.3067 + glyphs.mNumGlyphs); 1.3068 + } 1.3069 + 1.3070 + // draw any remaining glyphs 1.3071 + glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix, true); 1.3072 + if (aCallbacks && emittedGlyphs) { 1.3073 + aCallbacks->NotifyGlyphPathEmitted(); 1.3074 + } 1.3075 + 1.3076 + } else { 1.3077 + RefPtr<ScaledFont> scaledFont = GetScaledFont(dt); 1.3078 + 1.3079 + if (!scaledFont) { 1.3080 + return; 1.3081 + } 1.3082 + 1.3083 + bool oldSubpixelAA = dt->GetPermitSubpixelAA(); 1.3084 + 1.3085 + if (!AllowSubpixelAA()) { 1.3086 + dt->SetPermitSubpixelAA(false); 1.3087 + } 1.3088 + 1.3089 + GlyphBufferAzure glyphs; 1.3090 + Glyph *glyph; 1.3091 + 1.3092 + Matrix mat, matInv; 1.3093 + Matrix oldMat = dt->GetTransform(); 1.3094 + 1.3095 + // This is nullptr when we have inverse-transformed glyphs and we need 1.3096 + // to transform the Brush inside flush. 1.3097 + Matrix *passedInvMatrix = nullptr; 1.3098 + 1.3099 + RefPtr<GlyphRenderingOptions> renderingOptions = 1.3100 + GetGlyphRenderingOptions(); 1.3101 + 1.3102 + DrawOptions drawOptions; 1.3103 + drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption); 1.3104 + 1.3105 + // The cairo DrawTarget backend uses the cairo_scaled_font directly 1.3106 + // and so has the font skew matrix applied already. 1.3107 + if (mScaledFont && 1.3108 + dt->GetType() != BackendType::CAIRO) { 1.3109 + cairo_matrix_t matrix; 1.3110 + cairo_scaled_font_get_font_matrix(mScaledFont, &matrix); 1.3111 + if (matrix.xy != 0) { 1.3112 + // If this matrix applies a skew, which can happen when drawing 1.3113 + // oblique fonts, we will set the DrawTarget matrix to apply the 1.3114 + // skew. We'll need to move the glyphs by the inverse of the skew to 1.3115 + // get the glyphs positioned correctly in the new device space 1.3116 + // though, since the font matrix should only be applied to drawing 1.3117 + // the glyphs, and not to their position. 1.3118 + mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix)); 1.3119 + 1.3120 + mat._11 = mat._22 = 1.0; 1.3121 + float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size; 1.3122 + mat._21 /= adjustedSize; 1.3123 + 1.3124 + dt->SetTransform(mat * oldMat); 1.3125 + 1.3126 + matInv = mat; 1.3127 + matInv.Invert(); 1.3128 + 1.3129 + passedInvMatrix = &matInv; 1.3130 + } 1.3131 + } 1.3132 + 1.3133 + if (aSpacing) { 1.3134 + x += direction*aSpacing[0].mBefore; 1.3135 + } 1.3136 + for (i = aStart; i < aEnd; ++i) { 1.3137 + const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i]; 1.3138 + if (glyphData->IsSimpleGlyph()) { 1.3139 + double advance = glyphData->GetSimpleAdvance(); 1.3140 + double glyphX; 1.3141 + if (isRTL) { 1.3142 + x -= advance; 1.3143 + glyphX = x; 1.3144 + } else { 1.3145 + glyphX = x; 1.3146 + x += advance; 1.3147 + } 1.3148 + 1.3149 + if (haveSVGGlyphs) { 1.3150 + if (!paintSVGGlyphs) { 1.3151 + continue; 1.3152 + } 1.3153 + gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.3154 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.3155 + DrawMode mode = ForcePaintingDrawMode(aDrawMode); 1.3156 + if (RenderSVGGlyph(aContext, point, mode, 1.3157 + glyphData->GetSimpleGlyph(), aContextPaint, 1.3158 + aCallbacks, emittedGlyphs)) { 1.3159 + continue; 1.3160 + } 1.3161 + } 1.3162 + 1.3163 + // Perhaps we should put a scale in the cairo context instead of 1.3164 + // doing this scaling here... 1.3165 + // Multiplying by the reciprocal may introduce tiny error here, 1.3166 + // but we assume cairo is going to round coordinates at some stage 1.3167 + // and this is faster 1.3168 + glyph = glyphs.AppendGlyph(); 1.3169 + glyph->mIndex = glyphData->GetSimpleGlyph(); 1.3170 + glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); 1.3171 + glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit); 1.3172 + glyph->mPosition = matInv * glyph->mPosition; 1.3173 + glyphs.Flush(dt, aContextPaint, scaledFont, 1.3174 + aDrawMode, isRTL, renderingOptions, 1.3175 + aContext, passedInvMatrix, 1.3176 + drawOptions); 1.3177 + 1.3178 + // synthetic bolding by multi-striking with 1-pixel offsets 1.3179 + // at least once, more if there's room (large font sizes) 1.3180 + if (IsSyntheticBold()) { 1.3181 + double strikeOffset = synBoldOnePixelOffset; 1.3182 + int32_t strikeCount = strikes; 1.3183 + do { 1.3184 + Glyph *doubleglyph; 1.3185 + doubleglyph = glyphs.AppendGlyph(); 1.3186 + doubleglyph->mIndex = glyph->mIndex; 1.3187 + doubleglyph->mPosition.x = 1.3188 + ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit, 1.3189 + devUnitsPerAppUnit); 1.3190 + doubleglyph->mPosition.y = glyph->mPosition.y; 1.3191 + doubleglyph->mPosition = matInv * doubleglyph->mPosition; 1.3192 + strikeOffset += synBoldOnePixelOffset; 1.3193 + glyphs.Flush(dt, aContextPaint, scaledFont, 1.3194 + aDrawMode, isRTL, renderingOptions, 1.3195 + aContext, passedInvMatrix, 1.3196 + drawOptions); 1.3197 + } while (--strikeCount > 0); 1.3198 + } 1.3199 + emittedGlyphs = true; 1.3200 + } else { 1.3201 + uint32_t glyphCount = glyphData->GetGlyphCount(); 1.3202 + if (glyphCount > 0) { 1.3203 + const gfxTextRun::DetailedGlyph *details = 1.3204 + aTextRun->GetDetailedGlyphs(i); 1.3205 + NS_ASSERTION(details, "detailedGlyph should not be missing!"); 1.3206 + double advance; 1.3207 + for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) { 1.3208 + advance = details->mAdvance; 1.3209 + if (glyphData->IsMissing()) { 1.3210 + // default ignorable characters will have zero advance width. 1.3211 + // we don't have to draw the hexbox for them 1.3212 + if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) { 1.3213 + double glyphX = x; 1.3214 + if (isRTL) { 1.3215 + glyphX -= advance; 1.3216 + } 1.3217 + gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.3218 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.3219 + gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit); 1.3220 + gfxFloat height = GetMetrics().maxAscent; 1.3221 + gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height); 1.3222 + gfxFontMissingGlyphs::DrawMissingGlyph(aContext, 1.3223 + glyphRect, 1.3224 + details->mGlyphID, 1.3225 + appUnitsPerDevUnit); 1.3226 + } 1.3227 + } else { 1.3228 + double glyphX = x + details->mXOffset; 1.3229 + if (isRTL) { 1.3230 + glyphX -= advance; 1.3231 + } 1.3232 + 1.3233 + gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit), 1.3234 + ToDeviceUnits(y, devUnitsPerAppUnit)); 1.3235 + 1.3236 + if (haveSVGGlyphs) { 1.3237 + if (!paintSVGGlyphs) { 1.3238 + continue; 1.3239 + } 1.3240 + DrawMode mode = ForcePaintingDrawMode(aDrawMode); 1.3241 + if (RenderSVGGlyph(aContext, point, mode, 1.3242 + details->mGlyphID, 1.3243 + aContextPaint, aCallbacks, 1.3244 + emittedGlyphs)) { 1.3245 + continue; 1.3246 + } 1.3247 + } 1.3248 + 1.3249 + glyph = glyphs.AppendGlyph(); 1.3250 + glyph->mIndex = details->mGlyphID; 1.3251 + glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit); 1.3252 + glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit); 1.3253 + glyph->mPosition = matInv * glyph->mPosition; 1.3254 + glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, 1.3255 + isRTL, renderingOptions, aContext, passedInvMatrix, 1.3256 + drawOptions); 1.3257 + 1.3258 + if (IsSyntheticBold()) { 1.3259 + double strikeOffset = synBoldOnePixelOffset; 1.3260 + int32_t strikeCount = strikes; 1.3261 + do { 1.3262 + Glyph *doubleglyph; 1.3263 + doubleglyph = glyphs.AppendGlyph(); 1.3264 + doubleglyph->mIndex = glyph->mIndex; 1.3265 + doubleglyph->mPosition.x = 1.3266 + ToDeviceUnits(glyphX + strikeOffset * 1.3267 + appUnitsPerDevUnit, 1.3268 + devUnitsPerAppUnit); 1.3269 + doubleglyph->mPosition.y = glyph->mPosition.y; 1.3270 + strikeOffset += synBoldOnePixelOffset; 1.3271 + doubleglyph->mPosition = matInv * doubleglyph->mPosition; 1.3272 + glyphs.Flush(dt, aContextPaint, scaledFont, 1.3273 + aDrawMode, isRTL, renderingOptions, 1.3274 + aContext, passedInvMatrix, drawOptions); 1.3275 + } while (--strikeCount > 0); 1.3276 + } 1.3277 + emittedGlyphs = true; 1.3278 + } 1.3279 + } 1.3280 + } 1.3281 + } 1.3282 + 1.3283 + if (aSpacing) { 1.3284 + double space = aSpacing[i - aStart].mAfter; 1.3285 + if (i + 1 < aEnd) { 1.3286 + space += aSpacing[i + 1 - aStart].mBefore; 1.3287 + } 1.3288 + x += direction*space; 1.3289 + } 1.3290 + } 1.3291 + 1.3292 + glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL, 1.3293 + renderingOptions, aContext, passedInvMatrix, 1.3294 + drawOptions, true); 1.3295 + if (aCallbacks && emittedGlyphs) { 1.3296 + aCallbacks->NotifyGlyphPathEmitted(); 1.3297 + } 1.3298 + 1.3299 + dt->SetTransform(oldMat); 1.3300 + 1.3301 + dt->SetPermitSubpixelAA(oldSubpixelAA); 1.3302 + } 1.3303 + 1.3304 + *aPt = gfxPoint(x, y); 1.3305 +} 1.3306 + 1.3307 +bool 1.3308 +gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, 1.3309 + uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) 1.3310 +{ 1.3311 + if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) { 1.3312 + return false; 1.3313 + } 1.3314 + 1.3315 + const gfxFloat devUnitsPerSVGUnit = 1.3316 + GetAdjustedSize() / GetFontEntry()->UnitsPerEm(); 1.3317 + gfxContextMatrixAutoSaveRestore matrixRestore(aContext); 1.3318 + 1.3319 + aContext->Translate(gfxPoint(aPoint.x, aPoint.y)); 1.3320 + aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit); 1.3321 + 1.3322 + aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit); 1.3323 + 1.3324 + return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode), 1.3325 + aContextPaint); 1.3326 +} 1.3327 + 1.3328 +bool 1.3329 +gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode, 1.3330 + uint32_t aGlyphId, gfxTextContextPaint *aContextPaint, 1.3331 + gfxTextRunDrawCallbacks *aCallbacks, 1.3332 + bool& aEmittedGlyphs) 1.3333 +{ 1.3334 + if (aCallbacks) { 1.3335 + if (aEmittedGlyphs) { 1.3336 + aCallbacks->NotifyGlyphPathEmitted(); 1.3337 + aEmittedGlyphs = false; 1.3338 + } 1.3339 + aCallbacks->NotifyBeforeSVGGlyphPainted(); 1.3340 + } 1.3341 + bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId, 1.3342 + aContextPaint); 1.3343 + if (aCallbacks) { 1.3344 + aCallbacks->NotifyAfterSVGGlyphPainted(); 1.3345 + } 1.3346 + return rendered; 1.3347 +} 1.3348 + 1.3349 +static void 1.3350 +UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax) 1.3351 +{ 1.3352 + *aDestMin = std::min(*aDestMin, aX); 1.3353 + *aDestMax = std::max(*aDestMax, aX); 1.3354 +} 1.3355 + 1.3356 +// We get precise glyph extents if the textrun creator requested them, or 1.3357 +// if the font is a user font --- in which case the author may be relying 1.3358 +// on overflowing glyphs. 1.3359 +static bool 1.3360 +NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun) 1.3361 +{ 1.3362 + return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) || 1.3363 + aFont->GetFontEntry()->IsUserFont(); 1.3364 +} 1.3365 + 1.3366 +static bool 1.3367 +NeedsGlyphExtents(gfxTextRun *aTextRun) 1.3368 +{ 1.3369 + if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) 1.3370 + return true; 1.3371 + uint32_t numRuns; 1.3372 + const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns); 1.3373 + for (uint32_t i = 0; i < numRuns; ++i) { 1.3374 + if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont()) 1.3375 + return true; 1.3376 + } 1.3377 + return false; 1.3378 +} 1.3379 + 1.3380 +gfxFont::RunMetrics 1.3381 +gfxFont::Measure(gfxTextRun *aTextRun, 1.3382 + uint32_t aStart, uint32_t aEnd, 1.3383 + BoundingBoxType aBoundingBoxType, 1.3384 + gfxContext *aRefContext, 1.3385 + Spacing *aSpacing) 1.3386 +{ 1.3387 + // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS 1.3388 + // and the underlying cairo font may be antialiased, 1.3389 + // we need to create a copy in order to avoid getting cached extents. 1.3390 + // This is only used by MathML layout at present. 1.3391 + if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS && 1.3392 + mAntialiasOption != kAntialiasNone) { 1.3393 + if (!mNonAAFont) { 1.3394 + mNonAAFont = CopyWithAntialiasOption(kAntialiasNone); 1.3395 + } 1.3396 + // if font subclass doesn't implement CopyWithAntialiasOption(), 1.3397 + // it will return null and we'll proceed to use the existing font 1.3398 + if (mNonAAFont) { 1.3399 + return mNonAAFont->Measure(aTextRun, aStart, aEnd, 1.3400 + TIGHT_HINTED_OUTLINE_EXTENTS, 1.3401 + aRefContext, aSpacing); 1.3402 + } 1.3403 + } 1.3404 + 1.3405 + const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); 1.3406 + // Current position in appunits 1.3407 + const gfxFont::Metrics& fontMetrics = GetMetrics(); 1.3408 + 1.3409 + RunMetrics metrics; 1.3410 + metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit; 1.3411 + metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit; 1.3412 + if (aStart == aEnd) { 1.3413 + // exit now before we look at aSpacing[0], which is undefined 1.3414 + metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent); 1.3415 + return metrics; 1.3416 + } 1.3417 + 1.3418 + gfxFloat advanceMin = 0, advanceMax = 0; 1.3419 + const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs(); 1.3420 + bool isRTL = aTextRun->IsRightToLeft(); 1.3421 + double direction = aTextRun->GetDirection(); 1.3422 + bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun); 1.3423 + gfxGlyphExtents *extents = 1.3424 + (aBoundingBoxType == LOOSE_INK_EXTENTS && 1.3425 + !needsGlyphExtents && 1.3426 + !aTextRun->HasDetailedGlyphs()) ? nullptr 1.3427 + : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit()); 1.3428 + double x = 0; 1.3429 + if (aSpacing) { 1.3430 + x += direction*aSpacing[0].mBefore; 1.3431 + } 1.3432 + uint32_t i; 1.3433 + for (i = aStart; i < aEnd; ++i) { 1.3434 + const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i]; 1.3435 + if (glyphData->IsSimpleGlyph()) { 1.3436 + double advance = glyphData->GetSimpleAdvance(); 1.3437 + // Only get the real glyph horizontal extent if we were asked 1.3438 + // for the tight bounding box or we're in quality mode 1.3439 + if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) && 1.3440 + extents) { 1.3441 + uint32_t glyphIndex = glyphData->GetSimpleGlyph(); 1.3442 + uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex); 1.3443 + if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH && 1.3444 + aBoundingBoxType == LOOSE_INK_EXTENTS) { 1.3445 + UnionRange(x, &advanceMin, &advanceMax); 1.3446 + UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax); 1.3447 + } else { 1.3448 + gfxRect glyphRect; 1.3449 + if (!extents->GetTightGlyphExtentsAppUnits(this, 1.3450 + aRefContext, glyphIndex, &glyphRect)) { 1.3451 + glyphRect = gfxRect(0, metrics.mBoundingBox.Y(), 1.3452 + advance, metrics.mBoundingBox.Height()); 1.3453 + } 1.3454 + if (isRTL) { 1.3455 + glyphRect -= gfxPoint(advance, 0); 1.3456 + } 1.3457 + glyphRect += gfxPoint(x, 0); 1.3458 + metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); 1.3459 + } 1.3460 + } 1.3461 + x += direction*advance; 1.3462 + } else { 1.3463 + uint32_t glyphCount = glyphData->GetGlyphCount(); 1.3464 + if (glyphCount > 0) { 1.3465 + const gfxTextRun::DetailedGlyph *details = 1.3466 + aTextRun->GetDetailedGlyphs(i); 1.3467 + NS_ASSERTION(details != nullptr, 1.3468 + "detaiedGlyph record should not be missing!"); 1.3469 + uint32_t j; 1.3470 + for (j = 0; j < glyphCount; ++j, ++details) { 1.3471 + uint32_t glyphIndex = details->mGlyphID; 1.3472 + gfxPoint glyphPt(x + details->mXOffset, details->mYOffset); 1.3473 + double advance = details->mAdvance; 1.3474 + gfxRect glyphRect; 1.3475 + if (glyphData->IsMissing() || !extents || 1.3476 + !extents->GetTightGlyphExtentsAppUnits(this, 1.3477 + aRefContext, glyphIndex, &glyphRect)) { 1.3478 + // We might have failed to get glyph extents due to 1.3479 + // OOM or something 1.3480 + glyphRect = gfxRect(0, -metrics.mAscent, 1.3481 + advance, metrics.mAscent + metrics.mDescent); 1.3482 + } 1.3483 + if (isRTL) { 1.3484 + glyphRect -= gfxPoint(advance, 0); 1.3485 + } 1.3486 + glyphRect += gfxPoint(x, 0); 1.3487 + metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect); 1.3488 + x += direction*advance; 1.3489 + } 1.3490 + } 1.3491 + } 1.3492 + // Every other glyph type is ignored 1.3493 + if (aSpacing) { 1.3494 + double space = aSpacing[i - aStart].mAfter; 1.3495 + if (i + 1 < aEnd) { 1.3496 + space += aSpacing[i + 1 - aStart].mBefore; 1.3497 + } 1.3498 + x += direction*space; 1.3499 + } 1.3500 + } 1.3501 + 1.3502 + if (aBoundingBoxType == LOOSE_INK_EXTENTS) { 1.3503 + UnionRange(x, &advanceMin, &advanceMax); 1.3504 + gfxRect fontBox(advanceMin, -metrics.mAscent, 1.3505 + advanceMax - advanceMin, metrics.mAscent + metrics.mDescent); 1.3506 + metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox); 1.3507 + } 1.3508 + if (isRTL) { 1.3509 + metrics.mBoundingBox -= gfxPoint(x, 0); 1.3510 + } 1.3511 + 1.3512 + metrics.mAdvanceWidth = x*direction; 1.3513 + return metrics; 1.3514 +} 1.3515 + 1.3516 +static PLDHashOperator 1.3517 +NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey, 1.3518 + void* aClosure) 1.3519 +{ 1.3520 + aKey->GetKey()->NotifyGlyphsChanged(); 1.3521 + return PL_DHASH_NEXT; 1.3522 +} 1.3523 + 1.3524 +void 1.3525 +gfxFont::NotifyGlyphsChanged() 1.3526 +{ 1.3527 + uint32_t i, count = mGlyphExtentsArray.Length(); 1.3528 + for (i = 0; i < count; ++i) { 1.3529 + // Flush cached extents array 1.3530 + mGlyphExtentsArray[i]->NotifyGlyphsChanged(); 1.3531 + } 1.3532 + 1.3533 + if (mGlyphChangeObservers) { 1.3534 + mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr); 1.3535 + } 1.3536 +} 1.3537 + 1.3538 +static bool 1.3539 +IsBoundarySpace(char16_t aChar, char16_t aNextChar) 1.3540 +{ 1.3541 + return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar); 1.3542 +} 1.3543 + 1.3544 +static inline uint32_t 1.3545 +HashMix(uint32_t aHash, char16_t aCh) 1.3546 +{ 1.3547 + return (aHash >> 28) ^ (aHash << 4) ^ aCh; 1.3548 +} 1.3549 + 1.3550 +#ifdef __GNUC__ 1.3551 +#define GFX_MAYBE_UNUSED __attribute__((unused)) 1.3552 +#else 1.3553 +#define GFX_MAYBE_UNUSED 1.3554 +#endif 1.3555 + 1.3556 +template<typename T> 1.3557 +gfxShapedWord* 1.3558 +gfxFont::GetShapedWord(gfxContext *aContext, 1.3559 + const T *aText, 1.3560 + uint32_t aLength, 1.3561 + uint32_t aHash, 1.3562 + int32_t aRunScript, 1.3563 + int32_t aAppUnitsPerDevUnit, 1.3564 + uint32_t aFlags, 1.3565 + gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED) 1.3566 +{ 1.3567 + // if the cache is getting too big, flush it and start over 1.3568 + uint32_t wordCacheMaxEntries = 1.3569 + gfxPlatform::GetPlatform()->WordCacheMaxEntries(); 1.3570 + if (mWordCache->Count() > wordCacheMaxEntries) { 1.3571 + NS_WARNING("flushing shaped-word cache"); 1.3572 + ClearCachedWords(); 1.3573 + } 1.3574 + 1.3575 + // if there's a cached entry for this word, just return it 1.3576 + CacheHashKey key(aText, aLength, aHash, 1.3577 + aRunScript, 1.3578 + aAppUnitsPerDevUnit, 1.3579 + aFlags); 1.3580 + 1.3581 + CacheHashEntry *entry = mWordCache->PutEntry(key); 1.3582 + if (!entry) { 1.3583 + NS_WARNING("failed to create word cache entry - expect missing text"); 1.3584 + return nullptr; 1.3585 + } 1.3586 + gfxShapedWord *sw = entry->mShapedWord; 1.3587 + 1.3588 + bool isContent = !mStyle.systemFont; 1.3589 + 1.3590 + if (sw) { 1.3591 + sw->ResetAge(); 1.3592 + Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT : 1.3593 + Telemetry::WORD_CACHE_HITS_CHROME), 1.3594 + aLength); 1.3595 +#ifndef RELEASE_BUILD 1.3596 + if (aTextPerf) { 1.3597 + aTextPerf->current.wordCacheHit++; 1.3598 + } 1.3599 +#endif 1.3600 + return sw; 1.3601 + } 1.3602 + 1.3603 + Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT : 1.3604 + Telemetry::WORD_CACHE_MISSES_CHROME), 1.3605 + aLength); 1.3606 +#ifndef RELEASE_BUILD 1.3607 + if (aTextPerf) { 1.3608 + aTextPerf->current.wordCacheMiss++; 1.3609 + } 1.3610 +#endif 1.3611 + 1.3612 + sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength, 1.3613 + aRunScript, 1.3614 + aAppUnitsPerDevUnit, 1.3615 + aFlags); 1.3616 + if (!sw) { 1.3617 + NS_WARNING("failed to create gfxShapedWord - expect missing text"); 1.3618 + return nullptr; 1.3619 + } 1.3620 + 1.3621 + DebugOnly<bool> ok = 1.3622 + ShapeText(aContext, aText, 0, aLength, aRunScript, sw); 1.3623 + 1.3624 + NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text"); 1.3625 + 1.3626 + return sw; 1.3627 +} 1.3628 + 1.3629 +bool 1.3630 +gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const 1.3631 +{ 1.3632 + const gfxShapedWord *sw = mShapedWord; 1.3633 + if (!sw) { 1.3634 + return false; 1.3635 + } 1.3636 + if (sw->GetLength() != aKey->mLength || 1.3637 + sw->Flags() != aKey->mFlags || 1.3638 + sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit || 1.3639 + sw->Script() != aKey->mScript) { 1.3640 + return false; 1.3641 + } 1.3642 + if (sw->TextIs8Bit()) { 1.3643 + if (aKey->mTextIs8Bit) { 1.3644 + return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle, 1.3645 + aKey->mLength * sizeof(uint8_t))); 1.3646 + } 1.3647 + // The key has 16-bit text, even though all the characters are < 256, 1.3648 + // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're 1.3649 + // comparing with will have 8-bit text. 1.3650 + const uint8_t *s1 = sw->Text8Bit(); 1.3651 + const char16_t *s2 = aKey->mText.mDouble; 1.3652 + const char16_t *s2end = s2 + aKey->mLength; 1.3653 + while (s2 < s2end) { 1.3654 + if (*s1++ != *s2++) { 1.3655 + return false; 1.3656 + } 1.3657 + } 1.3658 + return true; 1.3659 + } 1.3660 + NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 && 1.3661 + !aKey->mTextIs8Bit, "didn't expect 8-bit text here"); 1.3662 + return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble, 1.3663 + aKey->mLength * sizeof(char16_t))); 1.3664 +} 1.3665 + 1.3666 +bool 1.3667 +gfxFont::ShapeText(gfxContext *aContext, 1.3668 + const uint8_t *aText, 1.3669 + uint32_t aOffset, 1.3670 + uint32_t aLength, 1.3671 + int32_t aScript, 1.3672 + gfxShapedText *aShapedText, 1.3673 + bool aPreferPlatformShaping) 1.3674 +{ 1.3675 + nsDependentCSubstring ascii((const char*)aText, aLength); 1.3676 + nsAutoString utf16; 1.3677 + AppendASCIItoUTF16(ascii, utf16); 1.3678 + if (utf16.Length() != aLength) { 1.3679 + return false; 1.3680 + } 1.3681 + return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength, 1.3682 + aScript, aShapedText, aPreferPlatformShaping); 1.3683 +} 1.3684 + 1.3685 +bool 1.3686 +gfxFont::ShapeText(gfxContext *aContext, 1.3687 + const char16_t *aText, 1.3688 + uint32_t aOffset, 1.3689 + uint32_t aLength, 1.3690 + int32_t aScript, 1.3691 + gfxShapedText *aShapedText, 1.3692 + bool aPreferPlatformShaping) 1.3693 +{ 1.3694 + bool ok = false; 1.3695 + 1.3696 + if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { 1.3697 + ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, 1.3698 + aScript, aShapedText); 1.3699 + } 1.3700 + 1.3701 + if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) { 1.3702 + if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) { 1.3703 + ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, 1.3704 + aScript, aShapedText); 1.3705 + } 1.3706 + } 1.3707 + 1.3708 + if (!ok) { 1.3709 + if (!mPlatformShaper) { 1.3710 + CreatePlatformShaper(); 1.3711 + NS_ASSERTION(mPlatformShaper, "no platform shaper available!"); 1.3712 + } 1.3713 + if (mPlatformShaper) { 1.3714 + ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength, 1.3715 + aScript, aShapedText); 1.3716 + } 1.3717 + } 1.3718 + 1.3719 + PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); 1.3720 + 1.3721 + return ok; 1.3722 +} 1.3723 + 1.3724 +void 1.3725 +gfxFont::PostShapingFixup(gfxContext *aContext, 1.3726 + const char16_t *aText, 1.3727 + uint32_t aOffset, 1.3728 + uint32_t aLength, 1.3729 + gfxShapedText *aShapedText) 1.3730 +{ 1.3731 + if (IsSyntheticBold()) { 1.3732 + float synBoldOffset = 1.3733 + GetSyntheticBoldOffset() * CalcXScale(aContext); 1.3734 + aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset, 1.3735 + aOffset, aLength); 1.3736 + } 1.3737 +} 1.3738 + 1.3739 +#define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid 1.3740 + // over-stressing platform shapers 1.3741 +#define BACKTRACK_LIMIT 16 // backtrack this far looking for a good place 1.3742 + // to split into fragments for separate shaping 1.3743 + 1.3744 +template<typename T> 1.3745 +bool 1.3746 +gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext, 1.3747 + const T *aText, 1.3748 + uint32_t aOffset, 1.3749 + uint32_t aLength, 1.3750 + int32_t aScript, 1.3751 + gfxTextRun *aTextRun) 1.3752 +{ 1.3753 + aTextRun->SetupClusterBoundaries(aOffset, aText, aLength); 1.3754 + 1.3755 + bool ok = true; 1.3756 + 1.3757 + while (ok && aLength > 0) { 1.3758 + uint32_t fragLen = aLength; 1.3759 + 1.3760 + // limit the length of text we pass to shapers in a single call 1.3761 + if (fragLen > MAX_SHAPING_LENGTH) { 1.3762 + fragLen = MAX_SHAPING_LENGTH; 1.3763 + 1.3764 + // in the 8-bit case, there are no multi-char clusters, 1.3765 + // so we don't need to do this check 1.3766 + if (sizeof(T) == sizeof(char16_t)) { 1.3767 + uint32_t i; 1.3768 + for (i = 0; i < BACKTRACK_LIMIT; ++i) { 1.3769 + if (aTextRun->IsClusterStart(aOffset + fragLen - i)) { 1.3770 + fragLen -= i; 1.3771 + break; 1.3772 + } 1.3773 + } 1.3774 + if (i == BACKTRACK_LIMIT) { 1.3775 + // if we didn't find any cluster start while backtracking, 1.3776 + // just check that we're not in the middle of a surrogate 1.3777 + // pair; back up by one code unit if we are. 1.3778 + if (NS_IS_LOW_SURROGATE(aText[fragLen]) && 1.3779 + NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) { 1.3780 + --fragLen; 1.3781 + } 1.3782 + } 1.3783 + } 1.3784 + } 1.3785 + 1.3786 + ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun); 1.3787 + 1.3788 + aText += fragLen; 1.3789 + aOffset += fragLen; 1.3790 + aLength -= fragLen; 1.3791 + } 1.3792 + 1.3793 + return ok; 1.3794 +} 1.3795 + 1.3796 +// Check if aCh is an unhandled control character that should be displayed 1.3797 +// as a hexbox rather than rendered by some random font on the system. 1.3798 +// We exclude \r as stray s are rather common (bug 941940). 1.3799 +// Note that \n and \t don't come through here, as they have specific 1.3800 +// meanings that have already been handled. 1.3801 +static bool 1.3802 +IsInvalidControlChar(uint32_t aCh) 1.3803 +{ 1.3804 + return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f); 1.3805 +} 1.3806 + 1.3807 +template<typename T> 1.3808 +bool 1.3809 +gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext, 1.3810 + const T *aText, 1.3811 + uint32_t aOffset, 1.3812 + uint32_t aLength, 1.3813 + int32_t aScript, 1.3814 + gfxTextRun *aTextRun) 1.3815 +{ 1.3816 + uint32_t fragStart = 0; 1.3817 + bool ok = true; 1.3818 + 1.3819 + for (uint32_t i = 0; i <= aLength && ok; ++i) { 1.3820 + T ch = (i < aLength) ? aText[i] : '\n'; 1.3821 + bool invalid = gfxFontGroup::IsInvalidChar(ch); 1.3822 + uint32_t length = i - fragStart; 1.3823 + 1.3824 + // break into separate fragments when we hit an invalid char 1.3825 + if (!invalid) { 1.3826 + continue; 1.3827 + } 1.3828 + 1.3829 + if (length > 0) { 1.3830 + ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart, 1.3831 + aOffset + fragStart, length, 1.3832 + aScript, aTextRun); 1.3833 + } 1.3834 + 1.3835 + if (i == aLength) { 1.3836 + break; 1.3837 + } 1.3838 + 1.3839 + // fragment was terminated by an invalid char: skip it, 1.3840 + // unless it's a control char that we want to show as a hexbox, 1.3841 + // but record where TAB or NEWLINE occur 1.3842 + if (ch == '\t') { 1.3843 + aTextRun->SetIsTab(aOffset + i); 1.3844 + } else if (ch == '\n') { 1.3845 + aTextRun->SetIsNewline(aOffset + i); 1.3846 + } else if (IsInvalidControlChar(ch) && 1.3847 + !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { 1.3848 + aTextRun->SetMissingGlyph(aOffset + i, ch, this); 1.3849 + } 1.3850 + fragStart = i + 1; 1.3851 + } 1.3852 + 1.3853 + NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text"); 1.3854 + return ok; 1.3855 +} 1.3856 + 1.3857 +#ifndef RELEASE_BUILD 1.3858 +#define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0) 1.3859 +#else 1.3860 +#define TEXT_PERF_INCR(tp, m) 1.3861 +#endif 1.3862 + 1.3863 +inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; } 1.3864 +inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; } 1.3865 + 1.3866 +inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen) 1.3867 +{ 1.3868 + return memchr(aString, 0x20, aLen) != nullptr; 1.3869 +} 1.3870 + 1.3871 +inline static bool HasSpaces(const char16_t *aString, uint32_t aLen) 1.3872 +{ 1.3873 + for (const char16_t *ch = aString; ch < aString + aLen; ch++) { 1.3874 + if (*ch == 0x20) { 1.3875 + return true; 1.3876 + } 1.3877 + } 1.3878 + return false; 1.3879 +} 1.3880 + 1.3881 +template<typename T> 1.3882 +bool 1.3883 +gfxFont::SplitAndInitTextRun(gfxContext *aContext, 1.3884 + gfxTextRun *aTextRun, 1.3885 + const T *aString, 1.3886 + uint32_t aRunStart, 1.3887 + uint32_t aRunLength, 1.3888 + int32_t aRunScript) 1.3889 +{ 1.3890 + if (aRunLength == 0) { 1.3891 + return true; 1.3892 + } 1.3893 + 1.3894 + gfxTextPerfMetrics *tp = nullptr; 1.3895 + 1.3896 +#ifndef RELEASE_BUILD 1.3897 + tp = aTextRun->GetFontGroup()->GetTextPerfMetrics(); 1.3898 + if (tp) { 1.3899 + if (mStyle.systemFont) { 1.3900 + tp->current.numChromeTextRuns++; 1.3901 + } else { 1.3902 + tp->current.numContentTextRuns++; 1.3903 + } 1.3904 + tp->current.numChars += aRunLength; 1.3905 + if (aRunLength > tp->current.maxTextRunLen) { 1.3906 + tp->current.maxTextRunLen = aRunLength; 1.3907 + } 1.3908 + } 1.3909 +#endif 1.3910 + 1.3911 + uint32_t wordCacheCharLimit = 1.3912 + gfxPlatform::GetPlatform()->WordCacheCharLimit(); 1.3913 + 1.3914 + // If spaces can participate in shaping (e.g. within lookups for automatic 1.3915 + // fractions), need to shape without using the word cache which segments 1.3916 + // textruns on space boundaries. Word cache can be used if the textrun 1.3917 + // is short enough to fit in the word cache and it lacks spaces. 1.3918 + if (SpaceMayParticipateInShaping(aRunScript)) { 1.3919 + if (aRunLength > wordCacheCharLimit || 1.3920 + HasSpaces(aString + aRunStart, aRunLength)) { 1.3921 + TEXT_PERF_INCR(tp, wordCacheSpaceRules); 1.3922 + return ShapeTextWithoutWordCache(aContext, aString + aRunStart, 1.3923 + aRunStart, aRunLength, aRunScript, 1.3924 + aTextRun); 1.3925 + } 1.3926 + } 1.3927 + 1.3928 + InitWordCache(); 1.3929 + 1.3930 + // the only flags we care about for ShapedWord construction/caching 1.3931 + uint32_t flags = aTextRun->GetFlags(); 1.3932 + flags &= (gfxTextRunFactory::TEXT_IS_RTL | 1.3933 + gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES | 1.3934 + gfxTextRunFactory::TEXT_USE_MATH_SCRIPT); 1.3935 + if (sizeof(T) == sizeof(uint8_t)) { 1.3936 + flags |= gfxTextRunFactory::TEXT_IS_8BIT; 1.3937 + } 1.3938 + 1.3939 + const T *text = aString + aRunStart; 1.3940 + uint32_t wordStart = 0; 1.3941 + uint32_t hash = 0; 1.3942 + bool wordIs8Bit = true; 1.3943 + int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit(); 1.3944 + 1.3945 + T nextCh = text[0]; 1.3946 + for (uint32_t i = 0; i <= aRunLength; ++i) { 1.3947 + T ch = nextCh; 1.3948 + nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n'; 1.3949 + bool boundary = IsBoundarySpace(ch, nextCh); 1.3950 + bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch); 1.3951 + uint32_t length = i - wordStart; 1.3952 + 1.3953 + // break into separate ShapedWords when we hit an invalid char, 1.3954 + // or a boundary space (always handled individually), 1.3955 + // or the first non-space after a space 1.3956 + if (!boundary && !invalid) { 1.3957 + if (!IsChar8Bit(ch)) { 1.3958 + wordIs8Bit = false; 1.3959 + } 1.3960 + // include this character in the hash, and move on to next 1.3961 + hash = HashMix(hash, ch); 1.3962 + continue; 1.3963 + } 1.3964 + 1.3965 + // We've decided to break here (i.e. we're at the end of a "word"); 1.3966 + // shape the word and add it to the textrun. 1.3967 + // For words longer than the limit, we don't use the 1.3968 + // font's word cache but just shape directly into the textrun. 1.3969 + if (length > wordCacheCharLimit) { 1.3970 + TEXT_PERF_INCR(tp, wordCacheLong); 1.3971 + bool ok = ShapeFragmentWithoutWordCache(aContext, 1.3972 + text + wordStart, 1.3973 + aRunStart + wordStart, 1.3974 + length, 1.3975 + aRunScript, 1.3976 + aTextRun); 1.3977 + if (!ok) { 1.3978 + return false; 1.3979 + } 1.3980 + } else if (length > 0) { 1.3981 + uint32_t wordFlags = flags; 1.3982 + // in the 8-bit version of this method, TEXT_IS_8BIT was 1.3983 + // already set as part of |flags|, so no need for a per-word 1.3984 + // adjustment here 1.3985 + if (sizeof(T) == sizeof(char16_t)) { 1.3986 + if (wordIs8Bit) { 1.3987 + wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT; 1.3988 + } 1.3989 + } 1.3990 + gfxShapedWord *sw = GetShapedWord(aContext, 1.3991 + text + wordStart, length, 1.3992 + hash, aRunScript, 1.3993 + appUnitsPerDevUnit, 1.3994 + wordFlags, tp); 1.3995 + if (sw) { 1.3996 + aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart); 1.3997 + } else { 1.3998 + return false; // failed, presumably out of memory? 1.3999 + } 1.4000 + } 1.4001 + 1.4002 + if (boundary) { 1.4003 + // word was terminated by a space: add that to the textrun 1.4004 + if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext, 1.4005 + aRunStart + i, ch)) 1.4006 + { 1.4007 + static const uint8_t space = ' '; 1.4008 + gfxShapedWord *sw = 1.4009 + GetShapedWord(aContext, 1.4010 + &space, 1, 1.4011 + HashMix(0, ' '), aRunScript, 1.4012 + appUnitsPerDevUnit, 1.4013 + flags | gfxTextRunFactory::TEXT_IS_8BIT, tp); 1.4014 + if (sw) { 1.4015 + aTextRun->CopyGlyphDataFrom(sw, aRunStart + i); 1.4016 + } else { 1.4017 + return false; 1.4018 + } 1.4019 + } 1.4020 + hash = 0; 1.4021 + wordStart = i + 1; 1.4022 + wordIs8Bit = true; 1.4023 + continue; 1.4024 + } 1.4025 + 1.4026 + if (i == aRunLength) { 1.4027 + break; 1.4028 + } 1.4029 + 1.4030 + NS_ASSERTION(invalid, 1.4031 + "how did we get here except via an invalid char?"); 1.4032 + 1.4033 + // word was terminated by an invalid char: skip it, 1.4034 + // unless it's a control char that we want to show as a hexbox, 1.4035 + // but record where TAB or NEWLINE occur 1.4036 + if (ch == '\t') { 1.4037 + aTextRun->SetIsTab(aRunStart + i); 1.4038 + } else if (ch == '\n') { 1.4039 + aTextRun->SetIsNewline(aRunStart + i); 1.4040 + } else if (IsInvalidControlChar(ch) && 1.4041 + !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) { 1.4042 + aTextRun->SetMissingGlyph(aRunStart + i, ch, this); 1.4043 + } 1.4044 + 1.4045 + hash = 0; 1.4046 + wordStart = i + 1; 1.4047 + wordIs8Bit = true; 1.4048 + } 1.4049 + 1.4050 + return true; 1.4051 +} 1.4052 + 1.4053 +gfxGlyphExtents * 1.4054 +gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) { 1.4055 + uint32_t i, count = mGlyphExtentsArray.Length(); 1.4056 + for (i = 0; i < count; ++i) { 1.4057 + if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit) 1.4058 + return mGlyphExtentsArray[i]; 1.4059 + } 1.4060 + gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit); 1.4061 + if (glyphExtents) { 1.4062 + mGlyphExtentsArray.AppendElement(glyphExtents); 1.4063 + // Initialize the extents of a space glyph, assuming that spaces don't 1.4064 + // render anything! 1.4065 + glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0); 1.4066 + } 1.4067 + return glyphExtents; 1.4068 +} 1.4069 + 1.4070 +void 1.4071 +gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight, 1.4072 + gfxGlyphExtents *aExtents) 1.4073 +{ 1.4074 + gfxContextMatrixAutoSaveRestore matrixRestore(aContext); 1.4075 + aContext->IdentityMatrix(); 1.4076 + 1.4077 + gfxRect svgBounds; 1.4078 + if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) && 1.4079 + mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) { 1.4080 + gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit(); 1.4081 + aExtents->SetTightGlyphExtents(aGlyphID, 1.4082 + gfxRect(svgBounds.x * d2a, 1.4083 + svgBounds.y * d2a, 1.4084 + svgBounds.width * d2a, 1.4085 + svgBounds.height * d2a)); 1.4086 + return; 1.4087 + } 1.4088 + 1.4089 + cairo_glyph_t glyph; 1.4090 + glyph.index = aGlyphID; 1.4091 + glyph.x = 0; 1.4092 + glyph.y = 0; 1.4093 + cairo_text_extents_t extents; 1.4094 + cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents); 1.4095 + 1.4096 + const Metrics& fontMetrics = GetMetrics(); 1.4097 + int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit(); 1.4098 + if (!aNeedTight && extents.x_bearing >= 0 && 1.4099 + extents.y_bearing >= -fontMetrics.maxAscent && 1.4100 + extents.height + extents.y_bearing <= fontMetrics.maxDescent) { 1.4101 + uint32_t appUnitsWidth = 1.4102 + uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit)); 1.4103 + if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) { 1.4104 + aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth)); 1.4105 + return; 1.4106 + } 1.4107 + } 1.4108 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.4109 + if (!aNeedTight) { 1.4110 + ++gGlyphExtentsSetupFallBackToTight; 1.4111 + } 1.4112 +#endif 1.4113 + 1.4114 + gfxFloat d2a = appUnitsPerDevUnit; 1.4115 + gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a, 1.4116 + extents.width*d2a, extents.height*d2a); 1.4117 + aExtents->SetTightGlyphExtents(aGlyphID, bounds); 1.4118 +} 1.4119 + 1.4120 +// Try to initialize font metrics by reading sfnt tables directly; 1.4121 +// set mIsValid=TRUE and return TRUE on success. 1.4122 +// Return FALSE if the gfxFontEntry subclass does not 1.4123 +// implement GetFontTable(), or for non-sfnt fonts where tables are 1.4124 +// not available. 1.4125 +// If this returns TRUE without setting the mIsValid flag, then we -did- 1.4126 +// apparently find an sfnt, but it was too broken to be used. 1.4127 +bool 1.4128 +gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics) 1.4129 +{ 1.4130 + mIsValid = false; // font is NOT valid in case of early return 1.4131 + 1.4132 + const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a'); 1.4133 + const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t'); 1.4134 + const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2'); 1.4135 + 1.4136 + uint32_t len; 1.4137 + 1.4138 + if (mFUnitsConvFactor == 0.0) { 1.4139 + // If the conversion factor from FUnits is not yet set, 1.4140 + // get the unitsPerEm from the 'head' table via the font entry 1.4141 + uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm(); 1.4142 + if (unitsPerEm == gfxFontEntry::kInvalidUPEM) { 1.4143 + return false; 1.4144 + } 1.4145 + mFUnitsConvFactor = mAdjustedSize / unitsPerEm; 1.4146 + } 1.4147 + 1.4148 + // 'hhea' table is required to get vertical extents 1.4149 + gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag); 1.4150 + if (!hheaTable) { 1.4151 + return false; // no 'hhea' table -> not an sfnt 1.4152 + } 1.4153 + const HheaTable* hhea = 1.4154 + reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len)); 1.4155 + if (len < sizeof(HheaTable)) { 1.4156 + return false; 1.4157 + } 1.4158 + 1.4159 +#define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor 1.4160 +#define SET_SIGNED(field,src) aMetrics.field = int16_t(src) * mFUnitsConvFactor 1.4161 + 1.4162 + SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax); 1.4163 + SET_SIGNED(maxAscent, hhea->ascender); 1.4164 + SET_SIGNED(maxDescent, -int16_t(hhea->descender)); 1.4165 + SET_SIGNED(externalLeading, hhea->lineGap); 1.4166 + 1.4167 + // 'post' table is required for underline metrics 1.4168 + gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag); 1.4169 + if (!postTable) { 1.4170 + return true; // no 'post' table -> sfnt is not valid 1.4171 + } 1.4172 + const PostTable *post = 1.4173 + reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len)); 1.4174 + if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) { 1.4175 + return true; // bad post table -> sfnt is not valid 1.4176 + } 1.4177 + 1.4178 + SET_SIGNED(underlineOffset, post->underlinePosition); 1.4179 + SET_UNSIGNED(underlineSize, post->underlineThickness); 1.4180 + 1.4181 + // 'OS/2' table is optional, if not found we'll estimate xHeight 1.4182 + // and aveCharWidth by measuring glyphs 1.4183 + gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag); 1.4184 + if (os2Table) { 1.4185 + const OS2Table *os2 = 1.4186 + reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len)); 1.4187 + if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) && 1.4188 + uint16_t(os2->version) >= 2) { 1.4189 + // version 2 and later includes the x-height field 1.4190 + SET_SIGNED(xHeight, os2->sxHeight); 1.4191 + // Abs because of negative xHeight seen in Kokonor (Tibetan) font 1.4192 + aMetrics.xHeight = Abs(aMetrics.xHeight); 1.4193 + } 1.4194 + // this should always be present in any valid OS/2 of any version 1.4195 + if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) { 1.4196 + SET_SIGNED(aveCharWidth, os2->xAvgCharWidth); 1.4197 + SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset); 1.4198 + SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset); 1.4199 + SET_SIGNED(strikeoutSize, os2->yStrikeoutSize); 1.4200 + SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition); 1.4201 + 1.4202 + // for fonts with USE_TYPO_METRICS set in the fsSelection field, 1.4203 + // and for all OpenType math fonts (having a 'MATH' table), 1.4204 + // let the OS/2 sTypo* metrics override those from the hhea table 1.4205 + // (see http://www.microsoft.com/typography/otspec/os2.htm#fss) 1.4206 + const uint16_t kUseTypoMetricsMask = 1 << 7; 1.4207 + if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) || 1.4208 + mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) { 1.4209 + SET_SIGNED(maxAscent, os2->sTypoAscender); 1.4210 + SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender)); 1.4211 + SET_SIGNED(externalLeading, os2->sTypoLineGap); 1.4212 + } 1.4213 + } 1.4214 + } 1.4215 + 1.4216 + mIsValid = true; 1.4217 + 1.4218 + return true; 1.4219 +} 1.4220 + 1.4221 +static double 1.4222 +RoundToNearestMultiple(double aValue, double aFraction) 1.4223 +{ 1.4224 + return floor(aValue/aFraction + 0.5) * aFraction; 1.4225 +} 1.4226 + 1.4227 +void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics) 1.4228 +{ 1.4229 + aMetrics.maxAscent = 1.4230 + ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0)); 1.4231 + aMetrics.maxDescent = 1.4232 + ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0)); 1.4233 + 1.4234 + if (aMetrics.xHeight <= 0) { 1.4235 + // only happens if we couldn't find either font metrics 1.4236 + // or a char to measure; 1.4237 + // pick an arbitrary value that's better than zero 1.4238 + aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR; 1.4239 + } 1.4240 + 1.4241 + aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent; 1.4242 + 1.4243 + if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) { 1.4244 + aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight; 1.4245 + } else { 1.4246 + aMetrics.internalLeading = 0.0; 1.4247 + } 1.4248 + 1.4249 + aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight 1.4250 + / aMetrics.maxHeight; 1.4251 + aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent; 1.4252 + 1.4253 + if (GetFontEntry()->IsFixedPitch()) { 1.4254 + // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger 1.4255 + // advance than the average character width... this forces 1.4256 + // those fonts to be recognized like fixed pitch fonts by layout. 1.4257 + aMetrics.maxAdvance = aMetrics.aveCharWidth; 1.4258 + } 1.4259 + 1.4260 + if (!aMetrics.subscriptOffset) { 1.4261 + aMetrics.subscriptOffset = aMetrics.xHeight; 1.4262 + } 1.4263 + if (!aMetrics.superscriptOffset) { 1.4264 + aMetrics.superscriptOffset = aMetrics.xHeight; 1.4265 + } 1.4266 + 1.4267 + if (!aMetrics.strikeoutOffset) { 1.4268 + aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5; 1.4269 + } 1.4270 + if (!aMetrics.strikeoutSize) { 1.4271 + aMetrics.strikeoutSize = aMetrics.underlineSize; 1.4272 + } 1.4273 +} 1.4274 + 1.4275 +void 1.4276 +gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont) 1.4277 +{ 1.4278 + // Even if this font size is zero, this font is created with non-zero size. 1.4279 + // However, for layout and others, we should return the metrics of zero size font. 1.4280 + if (mStyle.size == 0.0) { 1.4281 + memset(aMetrics, 0, sizeof(gfxFont::Metrics)); 1.4282 + return; 1.4283 + } 1.4284 + 1.4285 + // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset. 1.4286 + // If the values are not suitable, we should use x-height instead of them. 1.4287 + // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632 1.4288 + if (aMetrics->superscriptOffset <= 0 || 1.4289 + aMetrics->superscriptOffset >= aMetrics->maxAscent) { 1.4290 + aMetrics->superscriptOffset = aMetrics->xHeight; 1.4291 + } 1.4292 + // And also checking the case of sub script offset. The old gfx for win has checked this too. 1.4293 + if (aMetrics->subscriptOffset <= 0 || 1.4294 + aMetrics->subscriptOffset >= aMetrics->maxAscent) { 1.4295 + aMetrics->subscriptOffset = aMetrics->xHeight; 1.4296 + } 1.4297 + 1.4298 + aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize); 1.4299 + aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize); 1.4300 + 1.4301 + aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0); 1.4302 + 1.4303 + if (aMetrics->maxAscent < 1.0) { 1.4304 + // We cannot draw strikeout line and overline in the ascent... 1.4305 + aMetrics->underlineSize = 0; 1.4306 + aMetrics->underlineOffset = 0; 1.4307 + aMetrics->strikeoutSize = 0; 1.4308 + aMetrics->strikeoutOffset = 0; 1.4309 + return; 1.4310 + } 1.4311 + 1.4312 + /** 1.4313 + * Some CJK fonts have bad underline offset. Therefore, if this is such font, 1.4314 + * we need to lower the underline offset to bottom of *em* descent. 1.4315 + * However, if this is system font, we should not do this for the rendering compatibility with 1.4316 + * another application's UI on the platform. 1.4317 + * XXX Should not use this hack if the font size is too small? 1.4318 + * Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2) 1.4319 + */ 1.4320 + if (!mStyle.systemFont && aIsBadUnderlineFont) { 1.4321 + // First, we need 2 pixels between baseline and underline at least. Because many CJK characters 1.4322 + // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters. 1.4323 + aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0); 1.4324 + 1.4325 + // Next, we put the underline to bottom of below of the descent space. 1.4326 + if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) { 1.4327 + aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent); 1.4328 + } else { 1.4329 + aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, 1.4330 + aMetrics->underlineSize - aMetrics->emDescent); 1.4331 + } 1.4332 + } 1.4333 + // If underline positioned is too far from the text, descent position is preferred so that underline 1.4334 + // will stay within the boundary. 1.4335 + else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) { 1.4336 + if (aMetrics->underlineSize > aMetrics->maxDescent) 1.4337 + aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0); 1.4338 + // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.) 1.4339 + aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent; 1.4340 + } 1.4341 + 1.4342 + // If strikeout line is overflowed from the ascent, the line should be resized and moved for 1.4343 + // that being in the ascent space. 1.4344 + // Note that the strikeoutOffset is *middle* of the strikeout line position. 1.4345 + gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5); 1.4346 + if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) { 1.4347 + if (aMetrics->strikeoutSize > aMetrics->maxAscent) { 1.4348 + aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0); 1.4349 + halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5); 1.4350 + } 1.4351 + gfxFloat ascent = floor(aMetrics->maxAscent + 0.5); 1.4352 + aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0); 1.4353 + } 1.4354 + 1.4355 + // If overline is larger than the ascent, the line should be resized. 1.4356 + if (aMetrics->underlineSize > aMetrics->maxAscent) { 1.4357 + aMetrics->underlineSize = aMetrics->maxAscent; 1.4358 + } 1.4359 +} 1.4360 + 1.4361 +gfxFloat 1.4362 +gfxFont::SynthesizeSpaceWidth(uint32_t aCh) 1.4363 +{ 1.4364 + // return an appropriate width for various Unicode space characters 1.4365 + // that we "fake" if they're not actually present in the font; 1.4366 + // returns negative value if the char is not a known space. 1.4367 + switch (aCh) { 1.4368 + case 0x2000: // en quad 1.4369 + case 0x2002: return GetAdjustedSize() / 2; // en space 1.4370 + case 0x2001: // em quad 1.4371 + case 0x2003: return GetAdjustedSize(); // em space 1.4372 + case 0x2004: return GetAdjustedSize() / 3; // three-per-em space 1.4373 + case 0x2005: return GetAdjustedSize() / 4; // four-per-em space 1.4374 + case 0x2006: return GetAdjustedSize() / 6; // six-per-em space 1.4375 + case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space 1.4376 + case 0x2008: return GetMetrics().spaceWidth; // punctuation space 1.4377 + case 0x2009: return GetAdjustedSize() / 5; // thin space 1.4378 + case 0x200a: return GetAdjustedSize() / 10; // hair space 1.4379 + case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space 1.4380 + default: return -1.0; 1.4381 + } 1.4382 +} 1.4383 + 1.4384 +/*static*/ size_t 1.4385 +gfxFont::WordCacheEntrySizeOfExcludingThis(CacheHashEntry* aHashEntry, 1.4386 + MallocSizeOf aMallocSizeOf, 1.4387 + void* aUserArg) 1.4388 +{ 1.4389 + return aMallocSizeOf(aHashEntry->mShapedWord.get()); 1.4390 +} 1.4391 + 1.4392 +void 1.4393 +gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf, 1.4394 + FontCacheSizes* aSizes) const 1.4395 +{ 1.4396 + for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) { 1.4397 + aSizes->mFontInstances += 1.4398 + mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf); 1.4399 + } 1.4400 + if (mWordCache) { 1.4401 + aSizes->mShapedWords += 1.4402 + mWordCache->SizeOfExcludingThis(WordCacheEntrySizeOfExcludingThis, 1.4403 + aMallocSizeOf); 1.4404 + } 1.4405 +} 1.4406 + 1.4407 +void 1.4408 +gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf, 1.4409 + FontCacheSizes* aSizes) const 1.4410 +{ 1.4411 + aSizes->mFontInstances += aMallocSizeOf(this); 1.4412 + AddSizeOfExcludingThis(aMallocSizeOf, aSizes); 1.4413 +} 1.4414 + 1.4415 +void 1.4416 +gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver) 1.4417 +{ 1.4418 + if (!mGlyphChangeObservers) { 1.4419 + mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >; 1.4420 + } 1.4421 + mGlyphChangeObservers->PutEntry(aObserver); 1.4422 +} 1.4423 + 1.4424 +void 1.4425 +gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver) 1.4426 +{ 1.4427 + NS_ASSERTION(mGlyphChangeObservers, "No observers registered"); 1.4428 + NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered"); 1.4429 + mGlyphChangeObservers->RemoveEntry(aObserver); 1.4430 +} 1.4431 + 1.4432 +gfxGlyphExtents::~gfxGlyphExtents() 1.4433 +{ 1.4434 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.4435 + gGlyphExtentsWidthsTotalSize += 1.4436 + mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf); 1.4437 + gGlyphExtentsCount++; 1.4438 +#endif 1.4439 + MOZ_COUNT_DTOR(gfxGlyphExtents); 1.4440 +} 1.4441 + 1.4442 +bool 1.4443 +gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont, 1.4444 + gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents) 1.4445 +{ 1.4446 + HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID); 1.4447 + if (!entry) { 1.4448 + if (!aContext) { 1.4449 + NS_WARNING("Could not get glyph extents (no aContext)"); 1.4450 + return false; 1.4451 + } 1.4452 + 1.4453 + if (aFont->SetupCairoFont(aContext)) { 1.4454 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.4455 + ++gGlyphExtentsSetupLazyTight; 1.4456 +#endif 1.4457 + aFont->SetupGlyphExtents(aContext, aGlyphID, true, this); 1.4458 + entry = mTightGlyphExtents.GetEntry(aGlyphID); 1.4459 + } 1.4460 + if (!entry) { 1.4461 + NS_WARNING("Could not get glyph extents"); 1.4462 + return false; 1.4463 + } 1.4464 + } 1.4465 + 1.4466 + *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height); 1.4467 + return true; 1.4468 +} 1.4469 + 1.4470 +gfxGlyphExtents::GlyphWidths::~GlyphWidths() 1.4471 +{ 1.4472 + uint32_t i, count = mBlocks.Length(); 1.4473 + for (i = 0; i < count; ++i) { 1.4474 + uintptr_t bits = mBlocks[i]; 1.4475 + if (bits && !(bits & 0x1)) { 1.4476 + delete[] reinterpret_cast<uint16_t *>(bits); 1.4477 + } 1.4478 + } 1.4479 +} 1.4480 + 1.4481 +uint32_t 1.4482 +gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.4483 +{ 1.4484 + uint32_t i; 1.4485 + uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf); 1.4486 + for (i = 0; i < mBlocks.Length(); ++i) { 1.4487 + uintptr_t bits = mBlocks[i]; 1.4488 + if (bits && !(bits & 0x1)) { 1.4489 + size += aMallocSizeOf(reinterpret_cast<void*>(bits)); 1.4490 + } 1.4491 + } 1.4492 + return size; 1.4493 +} 1.4494 + 1.4495 +void 1.4496 +gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth) 1.4497 +{ 1.4498 + uint32_t block = aGlyphID >> BLOCK_SIZE_BITS; 1.4499 + uint32_t len = mBlocks.Length(); 1.4500 + if (block >= len) { 1.4501 + uintptr_t *elems = mBlocks.AppendElements(block + 1 - len); 1.4502 + if (!elems) 1.4503 + return; 1.4504 + memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len)); 1.4505 + } 1.4506 + 1.4507 + uintptr_t bits = mBlocks[block]; 1.4508 + uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1); 1.4509 + if (!bits) { 1.4510 + mBlocks[block] = MakeSingle(glyphOffset, aWidth); 1.4511 + return; 1.4512 + } 1.4513 + 1.4514 + uint16_t *newBlock; 1.4515 + if (bits & 0x1) { 1.4516 + // Expand the block to a real block. We could avoid this by checking 1.4517 + // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother 1.4518 + newBlock = new uint16_t[BLOCK_SIZE]; 1.4519 + if (!newBlock) 1.4520 + return; 1.4521 + uint32_t i; 1.4522 + for (i = 0; i < BLOCK_SIZE; ++i) { 1.4523 + newBlock[i] = INVALID_WIDTH; 1.4524 + } 1.4525 + newBlock[GetGlyphOffset(bits)] = GetWidth(bits); 1.4526 + mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock); 1.4527 + } else { 1.4528 + newBlock = reinterpret_cast<uint16_t *>(bits); 1.4529 + } 1.4530 + newBlock[glyphOffset] = aWidth; 1.4531 +} 1.4532 + 1.4533 +void 1.4534 +gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits) 1.4535 +{ 1.4536 + HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID); 1.4537 + if (!entry) 1.4538 + return; 1.4539 + entry->x = aExtentsAppUnits.X(); 1.4540 + entry->y = aExtentsAppUnits.Y(); 1.4541 + entry->width = aExtentsAppUnits.Width(); 1.4542 + entry->height = aExtentsAppUnits.Height(); 1.4543 +} 1.4544 + 1.4545 +size_t 1.4546 +gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const 1.4547 +{ 1.4548 + return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) + 1.4549 + mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf); 1.4550 +} 1.4551 + 1.4552 +size_t 1.4553 +gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const 1.4554 +{ 1.4555 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.4556 +} 1.4557 + 1.4558 +gfxFontGroup::gfxFontGroup(const nsAString& aFamilies, 1.4559 + const gfxFontStyle *aStyle, 1.4560 + gfxUserFontSet *aUserFontSet) 1.4561 + : mFamilies(aFamilies) 1.4562 + , mStyle(*aStyle) 1.4563 + , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET) 1.4564 + , mHyphenWidth(-1) 1.4565 + , mUserFontSet(aUserFontSet) 1.4566 + , mTextPerf(nullptr) 1.4567 + , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language)) 1.4568 + , mSkipDrawing(false) 1.4569 +{ 1.4570 + // We don't use SetUserFontSet() here, as we want to unconditionally call 1.4571 + // BuildFontList() rather than only do UpdateFontList() if it changed. 1.4572 + mCurrGeneration = GetGeneration(); 1.4573 + BuildFontList(); 1.4574 +} 1.4575 + 1.4576 +void 1.4577 +gfxFontGroup::BuildFontList() 1.4578 +{ 1.4579 +// "#if" to be removed once all platforms are moved to gfxPlatformFontList interface 1.4580 +// and subclasses of gfxFontGroup eliminated 1.4581 +#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) 1.4582 + ForEachFont(FindPlatformFont, this); 1.4583 + 1.4584 + if (mFonts.Length() == 0) { 1.4585 + bool needsBold; 1.4586 + gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList(); 1.4587 + gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle); 1.4588 + NS_ASSERTION(defaultFamily, 1.4589 + "invalid default font returned by GetDefaultFont"); 1.4590 + 1.4591 + if (defaultFamily) { 1.4592 + gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle, 1.4593 + needsBold); 1.4594 + if (fe) { 1.4595 + nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, 1.4596 + needsBold); 1.4597 + if (font) { 1.4598 + mFonts.AppendElement(FamilyFace(defaultFamily, font)); 1.4599 + } 1.4600 + } 1.4601 + } 1.4602 + 1.4603 + if (mFonts.Length() == 0) { 1.4604 + // Try for a "font of last resort...." 1.4605 + // Because an empty font list would be Really Bad for later code 1.4606 + // that assumes it will be able to get valid metrics for layout, 1.4607 + // just look for the first usable font and put in the list. 1.4608 + // (see bug 554544) 1.4609 + nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families; 1.4610 + pfl->GetFontFamilyList(families); 1.4611 + uint32_t count = families.Length(); 1.4612 + for (uint32_t i = 0; i < count; ++i) { 1.4613 + gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle, 1.4614 + needsBold); 1.4615 + if (fe) { 1.4616 + nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, 1.4617 + needsBold); 1.4618 + if (font) { 1.4619 + mFonts.AppendElement(FamilyFace(families[i], font)); 1.4620 + break; 1.4621 + } 1.4622 + } 1.4623 + } 1.4624 + } 1.4625 + 1.4626 + if (mFonts.Length() == 0) { 1.4627 + // an empty font list at this point is fatal; we're not going to 1.4628 + // be able to do even the most basic layout operations 1.4629 + char msg[256]; // CHECK buffer length if revising message below 1.4630 + sprintf(msg, "unable to find a usable font (%.220s)", 1.4631 + NS_ConvertUTF16toUTF8(mFamilies).get()); 1.4632 + NS_RUNTIMEABORT(msg); 1.4633 + } 1.4634 + } 1.4635 + 1.4636 + if (!mStyle.systemFont) { 1.4637 + uint32_t count = mFonts.Length(); 1.4638 + for (uint32_t i = 0; i < count; ++i) { 1.4639 + gfxFont* font = mFonts[i].Font(); 1.4640 + if (font->GetFontEntry()->mIsBadUnderlineFont) { 1.4641 + gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset; 1.4642 + gfxFloat bad = font->GetMetrics().underlineOffset; 1.4643 + mUnderlineOffset = std::min(first, bad); 1.4644 + break; 1.4645 + } 1.4646 + } 1.4647 + } 1.4648 +#endif 1.4649 +} 1.4650 + 1.4651 +bool 1.4652 +gfxFontGroup::FindPlatformFont(const nsAString& aName, 1.4653 + const nsACString& aGenericName, 1.4654 + bool aUseFontSet, 1.4655 + void *aClosure) 1.4656 +{ 1.4657 + gfxFontGroup *fontGroup = static_cast<gfxFontGroup*>(aClosure); 1.4658 + const gfxFontStyle *fontStyle = fontGroup->GetStyle(); 1.4659 + 1.4660 + bool needsBold; 1.4661 + gfxFontFamily *family = nullptr; 1.4662 + gfxFontEntry *fe = nullptr; 1.4663 + 1.4664 + if (aUseFontSet) { 1.4665 + // First, look up in the user font set... 1.4666 + // If the fontSet matches the family, we must not look for a platform 1.4667 + // font of the same name, even if we fail to actually get a fontEntry 1.4668 + // here; we'll fall back to the next name in the CSS font-family list. 1.4669 + gfxUserFontSet *fs = fontGroup->GetUserFontSet(); 1.4670 + if (fs) { 1.4671 + // If the fontSet matches the family, but the font has not yet finished 1.4672 + // loading (nor has its load timeout fired), the fontGroup should wait 1.4673 + // for the download, and not actually draw its text yet. 1.4674 + family = fs->GetFamily(aName); 1.4675 + if (family) { 1.4676 + bool waitForUserFont = false; 1.4677 + fe = fs->FindFontEntry(family, *fontStyle, 1.4678 + needsBold, waitForUserFont); 1.4679 + if (!fe && waitForUserFont) { 1.4680 + fontGroup->mSkipDrawing = true; 1.4681 + } 1.4682 + } 1.4683 + } 1.4684 + } 1.4685 + 1.4686 + // Not known in the user font set ==> check system fonts 1.4687 + // XXX: Fallback is bad.. 1.4688 + if (!family) { 1.4689 + gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList(); 1.4690 + family = fontList->FindFamily(aName); 1.4691 + if (family) { 1.4692 + fe = family->FindFontForStyle(*fontStyle, needsBold); 1.4693 + } 1.4694 + } 1.4695 + 1.4696 + // add to the font group, unless it's already there 1.4697 + if (fe && !fontGroup->HasFont(fe)) { 1.4698 + nsRefPtr<gfxFont> font = fe->FindOrMakeFont(fontStyle, needsBold); 1.4699 + if (font) { 1.4700 + fontGroup->mFonts.AppendElement(FamilyFace(family, font)); 1.4701 + } 1.4702 + } 1.4703 + 1.4704 + return true; 1.4705 +} 1.4706 + 1.4707 +bool 1.4708 +gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry) 1.4709 +{ 1.4710 + uint32_t count = mFonts.Length(); 1.4711 + for (uint32_t i = 0; i < count; ++i) { 1.4712 + if (mFonts[i].Font()->GetFontEntry() == aFontEntry) 1.4713 + return true; 1.4714 + } 1.4715 + return false; 1.4716 +} 1.4717 + 1.4718 +gfxFontGroup::~gfxFontGroup() 1.4719 +{ 1.4720 + mFonts.Clear(); 1.4721 +} 1.4722 + 1.4723 +gfxFontGroup * 1.4724 +gfxFontGroup::Copy(const gfxFontStyle *aStyle) 1.4725 +{ 1.4726 + gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet); 1.4727 + fg->SetTextPerfMetrics(mTextPerf); 1.4728 + return fg; 1.4729 +} 1.4730 + 1.4731 +bool 1.4732 +gfxFontGroup::IsInvalidChar(uint8_t ch) 1.4733 +{ 1.4734 + return ((ch & 0x7f) < 0x20 || ch == 0x7f); 1.4735 +} 1.4736 + 1.4737 +bool 1.4738 +gfxFontGroup::IsInvalidChar(char16_t ch) 1.4739 +{ 1.4740 + // All printable 7-bit ASCII values are OK 1.4741 + if (ch >= ' ' && ch < 0x7f) { 1.4742 + return false; 1.4743 + } 1.4744 + // No point in sending non-printing control chars through font shaping 1.4745 + if (ch <= 0x9f) { 1.4746 + return true; 1.4747 + } 1.4748 + return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ && 1.4749 + (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) || 1.4750 + IsBidiControl(ch)); 1.4751 +} 1.4752 + 1.4753 +bool 1.4754 +gfxFontGroup::ForEachFont(FontCreationCallback fc, 1.4755 + void *closure) 1.4756 +{ 1.4757 + return ForEachFontInternal(mFamilies, mStyle.language, 1.4758 + true, true, true, fc, closure); 1.4759 +} 1.4760 + 1.4761 +bool 1.4762 +gfxFontGroup::ForEachFont(const nsAString& aFamilies, 1.4763 + nsIAtom *aLanguage, 1.4764 + FontCreationCallback fc, 1.4765 + void *closure) 1.4766 +{ 1.4767 + return ForEachFontInternal(aFamilies, aLanguage, 1.4768 + false, true, true, fc, closure); 1.4769 +} 1.4770 + 1.4771 +struct ResolveData { 1.4772 + ResolveData(gfxFontGroup::FontCreationCallback aCallback, 1.4773 + nsACString& aGenericFamily, 1.4774 + bool aUseFontSet, 1.4775 + void *aClosure) : 1.4776 + mCallback(aCallback), 1.4777 + mGenericFamily(aGenericFamily), 1.4778 + mUseFontSet(aUseFontSet), 1.4779 + mClosure(aClosure) { 1.4780 + } 1.4781 + gfxFontGroup::FontCreationCallback mCallback; 1.4782 + nsCString mGenericFamily; 1.4783 + bool mUseFontSet; 1.4784 + void *mClosure; 1.4785 +}; 1.4786 + 1.4787 +bool 1.4788 +gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies, 1.4789 + nsIAtom *aLanguage, 1.4790 + bool aResolveGeneric, 1.4791 + bool aResolveFontName, 1.4792 + bool aUseFontSet, 1.4793 + FontCreationCallback fc, 1.4794 + void *closure) 1.4795 +{ 1.4796 + const char16_t kSingleQuote = char16_t('\''); 1.4797 + const char16_t kDoubleQuote = char16_t('\"'); 1.4798 + const char16_t kComma = char16_t(','); 1.4799 + 1.4800 + nsIAtom *groupAtom = nullptr; 1.4801 + nsAutoCString groupString; 1.4802 + if (aLanguage) { 1.4803 + if (!gLangService) { 1.4804 + CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService); 1.4805 + } 1.4806 + if (gLangService) { 1.4807 + nsresult rv; 1.4808 + groupAtom = gLangService->GetLanguageGroup(aLanguage, &rv); 1.4809 + } 1.4810 + } 1.4811 + if (!groupAtom) { 1.4812 + groupAtom = nsGkAtoms::Unicode; 1.4813 + } 1.4814 + groupAtom->ToUTF8String(groupString); 1.4815 + 1.4816 + nsPromiseFlatString families(aFamilies); 1.4817 + const char16_t *p, *p_end; 1.4818 + families.BeginReading(p); 1.4819 + families.EndReading(p_end); 1.4820 + nsAutoString family; 1.4821 + nsAutoCString lcFamily; 1.4822 + nsAutoString genericFamily; 1.4823 + 1.4824 + while (p < p_end) { 1.4825 + while (nsCRT::IsAsciiSpace(*p) || *p == kComma) 1.4826 + if (++p == p_end) 1.4827 + return true; 1.4828 + 1.4829 + bool generic; 1.4830 + if (*p == kSingleQuote || *p == kDoubleQuote) { 1.4831 + // quoted font family 1.4832 + char16_t quoteMark = *p; 1.4833 + if (++p == p_end) 1.4834 + return true; 1.4835 + const char16_t *nameStart = p; 1.4836 + 1.4837 + // XXX What about CSS character escapes? 1.4838 + while (*p != quoteMark) 1.4839 + if (++p == p_end) 1.4840 + return true; 1.4841 + 1.4842 + family = Substring(nameStart, p); 1.4843 + generic = false; 1.4844 + genericFamily.SetIsVoid(true); 1.4845 + 1.4846 + while (++p != p_end && *p != kComma) 1.4847 + /* nothing */ ; 1.4848 + 1.4849 + } else { 1.4850 + // unquoted font family 1.4851 + const char16_t *nameStart = p; 1.4852 + while (++p != p_end && *p != kComma) 1.4853 + /* nothing */ ; 1.4854 + 1.4855 + family = Substring(nameStart, p); 1.4856 + family.CompressWhitespace(false, true); 1.4857 + 1.4858 + if (aResolveGeneric && 1.4859 + (family.LowerCaseEqualsLiteral("serif") || 1.4860 + family.LowerCaseEqualsLiteral("sans-serif") || 1.4861 + family.LowerCaseEqualsLiteral("monospace") || 1.4862 + family.LowerCaseEqualsLiteral("cursive") || 1.4863 + family.LowerCaseEqualsLiteral("fantasy"))) 1.4864 + { 1.4865 + generic = true; 1.4866 + 1.4867 + ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily); 1.4868 + 1.4869 + nsAutoCString prefName("font.name."); 1.4870 + prefName.Append(lcFamily); 1.4871 + prefName.AppendLiteral("."); 1.4872 + prefName.Append(groupString); 1.4873 + 1.4874 + nsAdoptingString value = Preferences::GetString(prefName.get()); 1.4875 + if (value) { 1.4876 + CopyASCIItoUTF16(lcFamily, genericFamily); 1.4877 + family = value; 1.4878 + } 1.4879 + } else { 1.4880 + generic = false; 1.4881 + genericFamily.SetIsVoid(true); 1.4882 + } 1.4883 + } 1.4884 + 1.4885 + NS_LossyConvertUTF16toASCII gf(genericFamily); 1.4886 + if (generic) { 1.4887 + ForEachFontInternal(family, groupAtom, false, 1.4888 + aResolveFontName, false, 1.4889 + fc, closure); 1.4890 + } else if (!family.IsEmpty()) { 1.4891 + if (aResolveFontName) { 1.4892 + ResolveData data(fc, gf, aUseFontSet, closure); 1.4893 + bool aborted = false, needsBold; 1.4894 + nsresult rv = NS_OK; 1.4895 + bool foundFamily = false; 1.4896 + bool waitForUserFont = false; 1.4897 + gfxFontEntry *fe = nullptr; 1.4898 + if (aUseFontSet && mUserFontSet) { 1.4899 + gfxFontFamily *fam = mUserFontSet->GetFamily(family); 1.4900 + if (fam) { 1.4901 + fe = mUserFontSet->FindFontEntry(fam, mStyle, 1.4902 + needsBold, 1.4903 + waitForUserFont); 1.4904 + } 1.4905 + } 1.4906 + if (fe) { 1.4907 + gfxFontGroup::FontResolverProc(family, &data); 1.4908 + } else { 1.4909 + if (waitForUserFont) { 1.4910 + mSkipDrawing = true; 1.4911 + } 1.4912 + if (!foundFamily) { 1.4913 + gfxPlatform *pf = gfxPlatform::GetPlatform(); 1.4914 + // XXX: Fallback is bad 1.4915 + rv = pf->ResolveFontName(family, 1.4916 + gfxFontGroup::FontResolverProc, 1.4917 + &data, aborted); 1.4918 + } 1.4919 + } 1.4920 + if (NS_FAILED(rv) || aborted) 1.4921 + return false; 1.4922 + } 1.4923 + else { 1.4924 + if (!fc(family, gf, aUseFontSet, closure)) 1.4925 + return false; 1.4926 + } 1.4927 + } 1.4928 + 1.4929 + if (generic && aResolveGeneric) { 1.4930 + nsAutoCString prefName("font.name-list."); 1.4931 + prefName.Append(lcFamily); 1.4932 + prefName.AppendLiteral("."); 1.4933 + prefName.Append(groupString); 1.4934 + nsAdoptingString value = Preferences::GetString(prefName.get()); 1.4935 + if (value) { 1.4936 + ForEachFontInternal(value, groupAtom, false, 1.4937 + aResolveFontName, false, 1.4938 + fc, closure); 1.4939 + } 1.4940 + } 1.4941 + 1.4942 + ++p; // may advance past p_end 1.4943 + } 1.4944 + 1.4945 + return true; 1.4946 +} 1.4947 + 1.4948 +bool 1.4949 +gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure) 1.4950 +{ 1.4951 + ResolveData *data = reinterpret_cast<ResolveData*>(aClosure); 1.4952 + return (data->mCallback)(aName, data->mGenericFamily, data->mUseFontSet, 1.4953 + data->mClosure); 1.4954 +} 1.4955 + 1.4956 +gfxTextRun * 1.4957 +gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags) 1.4958 +{ 1.4959 + aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT; 1.4960 + return gfxTextRun::Create(aParams, 0, this, aFlags); 1.4961 +} 1.4962 + 1.4963 +gfxTextRun * 1.4964 +gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags) 1.4965 +{ 1.4966 + aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT; 1.4967 + 1.4968 + gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags); 1.4969 + if (!textRun) { 1.4970 + return nullptr; 1.4971 + } 1.4972 + 1.4973 + gfxFont *font = GetFontAt(0); 1.4974 + if (MOZ_UNLIKELY(GetStyle()->size == 0)) { 1.4975 + // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle 1.4976 + // them, and always create at least size 1 fonts, i.e. they still 1.4977 + // render something for size 0 fonts. 1.4978 + textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false); 1.4979 + } 1.4980 + else { 1.4981 + if (font->GetSpaceGlyph()) { 1.4982 + // Normally, the font has a cached space glyph, so we can avoid 1.4983 + // the cost of calling FindFontForChar. 1.4984 + textRun->SetSpaceGlyph(font, aParams->mContext, 0); 1.4985 + } else { 1.4986 + // In case the primary font doesn't have <space> (bug 970891), 1.4987 + // find one that does. 1.4988 + uint8_t matchType; 1.4989 + nsRefPtr<gfxFont> spaceFont = 1.4990 + FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType); 1.4991 + if (spaceFont) { 1.4992 + textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0); 1.4993 + } 1.4994 + } 1.4995 + } 1.4996 + 1.4997 + // Note that the gfxGlyphExtents glyph bounds storage for the font will 1.4998 + // always contain an entry for the font's space glyph, so we don't have 1.4999 + // to call FetchGlyphExtents here. 1.5000 + return textRun; 1.5001 +} 1.5002 + 1.5003 +gfxTextRun * 1.5004 +gfxFontGroup::MakeBlankTextRun(uint32_t aLength, 1.5005 + const Parameters *aParams, uint32_t aFlags) 1.5006 +{ 1.5007 + gfxTextRun *textRun = 1.5008 + gfxTextRun::Create(aParams, aLength, this, aFlags); 1.5009 + if (!textRun) { 1.5010 + return nullptr; 1.5011 + } 1.5012 + 1.5013 + textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false); 1.5014 + return textRun; 1.5015 +} 1.5016 + 1.5017 +gfxTextRun * 1.5018 +gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit) 1.5019 +{ 1.5020 + // only use U+2010 if it is supported by the first font in the group; 1.5021 + // it's better to use ASCII '-' from the primary font than to fall back to 1.5022 + // U+2010 from some other, possibly poorly-matching face 1.5023 + static const char16_t hyphen = 0x2010; 1.5024 + gfxFont *font = GetFontAt(0); 1.5025 + if (font && font->HasCharacter(hyphen)) { 1.5026 + return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit, 1.5027 + gfxFontGroup::TEXT_IS_PERSISTENT); 1.5028 + } 1.5029 + 1.5030 + static const uint8_t dash = '-'; 1.5031 + return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit, 1.5032 + gfxFontGroup::TEXT_IS_PERSISTENT); 1.5033 +} 1.5034 + 1.5035 +gfxFloat 1.5036 +gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider) 1.5037 +{ 1.5038 + if (mHyphenWidth < 0) { 1.5039 + nsRefPtr<gfxContext> ctx(aProvider->GetContext()); 1.5040 + if (ctx) { 1.5041 + nsAutoPtr<gfxTextRun> 1.5042 + hyphRun(MakeHyphenTextRun(ctx, 1.5043 + aProvider->GetAppUnitsPerDevUnit())); 1.5044 + mHyphenWidth = hyphRun.get() ? 1.5045 + hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0; 1.5046 + } 1.5047 + } 1.5048 + return mHyphenWidth; 1.5049 +} 1.5050 + 1.5051 +gfxTextRun * 1.5052 +gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength, 1.5053 + const Parameters *aParams, uint32_t aFlags) 1.5054 +{ 1.5055 + if (aLength == 0) { 1.5056 + return MakeEmptyTextRun(aParams, aFlags); 1.5057 + } 1.5058 + if (aLength == 1 && aString[0] == ' ') { 1.5059 + return MakeSpaceTextRun(aParams, aFlags); 1.5060 + } 1.5061 + 1.5062 + aFlags |= TEXT_IS_8BIT; 1.5063 + 1.5064 + if (GetStyle()->size == 0) { 1.5065 + // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle 1.5066 + // them, and always create at least size 1 fonts, i.e. they still 1.5067 + // render something for size 0 fonts. 1.5068 + return MakeBlankTextRun(aLength, aParams, aFlags); 1.5069 + } 1.5070 + 1.5071 + gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength, 1.5072 + this, aFlags); 1.5073 + if (!textRun) { 1.5074 + return nullptr; 1.5075 + } 1.5076 + 1.5077 + InitTextRun(aParams->mContext, textRun, aString, aLength); 1.5078 + 1.5079 + textRun->FetchGlyphExtents(aParams->mContext); 1.5080 + 1.5081 + return textRun; 1.5082 +} 1.5083 + 1.5084 +gfxTextRun * 1.5085 +gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength, 1.5086 + const Parameters *aParams, uint32_t aFlags) 1.5087 +{ 1.5088 + if (aLength == 0) { 1.5089 + return MakeEmptyTextRun(aParams, aFlags); 1.5090 + } 1.5091 + if (aLength == 1 && aString[0] == ' ') { 1.5092 + return MakeSpaceTextRun(aParams, aFlags); 1.5093 + } 1.5094 + if (GetStyle()->size == 0) { 1.5095 + return MakeBlankTextRun(aLength, aParams, aFlags); 1.5096 + } 1.5097 + 1.5098 + gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength, 1.5099 + this, aFlags); 1.5100 + if (!textRun) { 1.5101 + return nullptr; 1.5102 + } 1.5103 + 1.5104 + InitTextRun(aParams->mContext, textRun, aString, aLength); 1.5105 + 1.5106 + textRun->FetchGlyphExtents(aParams->mContext); 1.5107 + 1.5108 + return textRun; 1.5109 +} 1.5110 + 1.5111 +template<typename T> 1.5112 +void 1.5113 +gfxFontGroup::InitTextRun(gfxContext *aContext, 1.5114 + gfxTextRun *aTextRun, 1.5115 + const T *aString, 1.5116 + uint32_t aLength) 1.5117 +{ 1.5118 + NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run"); 1.5119 + 1.5120 + // we need to do numeral processing even on 8-bit text, 1.5121 + // in case we're converting Western to Hindi/Arabic digits 1.5122 + int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption(); 1.5123 + nsAutoArrayPtr<char16_t> transformedString; 1.5124 + if (numOption != IBMBIDI_NUMERAL_NOMINAL) { 1.5125 + // scan the string for numerals that may need to be transformed; 1.5126 + // if we find any, we'll make a local copy here and use that for 1.5127 + // font matching and glyph generation/shaping 1.5128 + bool prevIsArabic = 1.5129 + (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0; 1.5130 + for (uint32_t i = 0; i < aLength; ++i) { 1.5131 + char16_t origCh = aString[i]; 1.5132 + char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption); 1.5133 + if (newCh != origCh) { 1.5134 + if (!transformedString) { 1.5135 + transformedString = new char16_t[aLength]; 1.5136 + if (sizeof(T) == sizeof(char16_t)) { 1.5137 + memcpy(transformedString.get(), aString, i * sizeof(char16_t)); 1.5138 + } else { 1.5139 + for (uint32_t j = 0; j < i; ++j) { 1.5140 + transformedString[j] = aString[j]; 1.5141 + } 1.5142 + } 1.5143 + } 1.5144 + } 1.5145 + if (transformedString) { 1.5146 + transformedString[i] = newCh; 1.5147 + } 1.5148 + prevIsArabic = IS_ARABIC_CHAR(newCh); 1.5149 + } 1.5150 + } 1.5151 + 1.5152 +#ifdef PR_LOGGING 1.5153 + PRLogModuleInfo *log = (mStyle.systemFont ? 1.5154 + gfxPlatform::GetLog(eGfxLog_textrunui) : 1.5155 + gfxPlatform::GetLog(eGfxLog_textrun)); 1.5156 +#endif 1.5157 + 1.5158 + if (sizeof(T) == sizeof(uint8_t) && !transformedString) { 1.5159 + 1.5160 +#ifdef PR_LOGGING 1.5161 + if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) { 1.5162 + nsAutoCString lang; 1.5163 + mStyle.language->ToUTF8String(lang); 1.5164 + nsAutoCString str((const char*)aString, aLength); 1.5165 + PR_LOG(log, PR_LOG_WARNING,\ 1.5166 + ("(%s) fontgroup: [%s] lang: %s script: %d len %d " 1.5167 + "weight: %d width: %d style: %s size: %6.2f %d-byte " 1.5168 + "TEXTRUN [%s] ENDTEXTRUN\n", 1.5169 + (mStyle.systemFont ? "textrunui" : "textrun"), 1.5170 + NS_ConvertUTF16toUTF8(mFamilies).get(), 1.5171 + lang.get(), MOZ_SCRIPT_LATIN, aLength, 1.5172 + uint32_t(mStyle.weight), uint32_t(mStyle.stretch), 1.5173 + (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" : 1.5174 + (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" : 1.5175 + "normal")), 1.5176 + mStyle.size, 1.5177 + sizeof(T), 1.5178 + str.get())); 1.5179 + } 1.5180 +#endif 1.5181 + 1.5182 + // the text is still purely 8-bit; bypass the script-run itemizer 1.5183 + // and treat it as a single Latin run 1.5184 + InitScriptRun(aContext, aTextRun, aString, 1.5185 + 0, aLength, MOZ_SCRIPT_LATIN); 1.5186 + } else { 1.5187 + const char16_t *textPtr; 1.5188 + if (transformedString) { 1.5189 + textPtr = transformedString.get(); 1.5190 + } else { 1.5191 + // typecast to avoid compilation error for the 8-bit version, 1.5192 + // even though this is dead code in that case 1.5193 + textPtr = reinterpret_cast<const char16_t*>(aString); 1.5194 + } 1.5195 + 1.5196 + // split into script runs so that script can potentially influence 1.5197 + // the font matching process below 1.5198 + gfxScriptItemizer scriptRuns(textPtr, aLength); 1.5199 + 1.5200 + uint32_t runStart = 0, runLimit = aLength; 1.5201 + int32_t runScript = MOZ_SCRIPT_LATIN; 1.5202 + while (scriptRuns.Next(runStart, runLimit, runScript)) { 1.5203 + 1.5204 +#ifdef PR_LOGGING 1.5205 + if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) { 1.5206 + nsAutoCString lang; 1.5207 + mStyle.language->ToUTF8String(lang); 1.5208 + uint32_t runLen = runLimit - runStart; 1.5209 + PR_LOG(log, PR_LOG_WARNING,\ 1.5210 + ("(%s) fontgroup: [%s] lang: %s script: %d len %d " 1.5211 + "weight: %d width: %d style: %s size: %6.2f %d-byte " 1.5212 + "TEXTRUN [%s] ENDTEXTRUN\n", 1.5213 + (mStyle.systemFont ? "textrunui" : "textrun"), 1.5214 + NS_ConvertUTF16toUTF8(mFamilies).get(), 1.5215 + lang.get(), runScript, runLen, 1.5216 + uint32_t(mStyle.weight), uint32_t(mStyle.stretch), 1.5217 + (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" : 1.5218 + (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" : 1.5219 + "normal")), 1.5220 + mStyle.size, 1.5221 + sizeof(T), 1.5222 + NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get())); 1.5223 + } 1.5224 +#endif 1.5225 + 1.5226 + InitScriptRun(aContext, aTextRun, textPtr, 1.5227 + runStart, runLimit, runScript); 1.5228 + } 1.5229 + } 1.5230 + 1.5231 + if (sizeof(T) == sizeof(char16_t) && aLength > 0) { 1.5232 + gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs(); 1.5233 + if (!glyph->IsSimpleGlyph()) { 1.5234 + glyph->SetClusterStart(true); 1.5235 + } 1.5236 + } 1.5237 + 1.5238 + // It's possible for CoreText to omit glyph runs if it decides they contain 1.5239 + // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we 1.5240 + // need to eliminate them from the glyph run array to avoid drawing "partial 1.5241 + // ligatures" with the wrong font. 1.5242 + // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because 1.5243 + // it will iterate back over all glyphruns in the textrun, which leads to 1.5244 + // pathologically-bad perf in the case where a textrun contains many script 1.5245 + // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs 1.5246 + // every time a new script subrun is processed. 1.5247 + aTextRun->SanitizeGlyphRuns(); 1.5248 + 1.5249 + aTextRun->SortGlyphRuns(); 1.5250 +} 1.5251 + 1.5252 +template<typename T> 1.5253 +void 1.5254 +gfxFontGroup::InitScriptRun(gfxContext *aContext, 1.5255 + gfxTextRun *aTextRun, 1.5256 + const T *aString, 1.5257 + uint32_t aScriptRunStart, 1.5258 + uint32_t aScriptRunEnd, 1.5259 + int32_t aRunScript) 1.5260 +{ 1.5261 + NS_ASSERTION(aScriptRunEnd > aScriptRunStart, 1.5262 + "don't call InitScriptRun for a zero-length run"); 1.5263 + 1.5264 + gfxFont *mainFont = GetFontAt(0); 1.5265 + 1.5266 + uint32_t runStart = aScriptRunStart; 1.5267 + nsAutoTArray<gfxTextRange,3> fontRanges; 1.5268 + ComputeRanges(fontRanges, aString + aScriptRunStart, 1.5269 + aScriptRunEnd - aScriptRunStart, aRunScript); 1.5270 + uint32_t numRanges = fontRanges.Length(); 1.5271 + 1.5272 + for (uint32_t r = 0; r < numRanges; r++) { 1.5273 + const gfxTextRange& range = fontRanges[r]; 1.5274 + uint32_t matchedLength = range.Length(); 1.5275 + gfxFont *matchedFont = range.font; 1.5276 + 1.5277 + // create the glyph run for this range 1.5278 + if (matchedFont) { 1.5279 + aTextRun->AddGlyphRun(matchedFont, range.matchType, 1.5280 + runStart, (matchedLength > 0)); 1.5281 + // do glyph layout and record the resulting positioned glyphs 1.5282 + if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString, 1.5283 + runStart, matchedLength, 1.5284 + aRunScript)) { 1.5285 + // glyph layout failed! treat as missing glyphs 1.5286 + matchedFont = nullptr; 1.5287 + } 1.5288 + } else { 1.5289 + aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup, 1.5290 + runStart, (matchedLength > 0)); 1.5291 + } 1.5292 + 1.5293 + if (!matchedFont) { 1.5294 + // We need to set cluster boundaries (and mark spaces) so that 1.5295 + // surrogate pairs, combining characters, etc behave properly, 1.5296 + // even if we don't have glyphs for them 1.5297 + aTextRun->SetupClusterBoundaries(runStart, aString + runStart, 1.5298 + matchedLength); 1.5299 + 1.5300 + // various "missing" characters may need special handling, 1.5301 + // so we check for them here 1.5302 + uint32_t runLimit = runStart + matchedLength; 1.5303 + for (uint32_t index = runStart; index < runLimit; index++) { 1.5304 + T ch = aString[index]; 1.5305 + 1.5306 + // tab and newline are not to be displayed as hexboxes, 1.5307 + // but do need to be recorded in the textrun 1.5308 + if (ch == '\n') { 1.5309 + aTextRun->SetIsNewline(index); 1.5310 + continue; 1.5311 + } 1.5312 + if (ch == '\t') { 1.5313 + aTextRun->SetIsTab(index); 1.5314 + continue; 1.5315 + } 1.5316 + 1.5317 + // for 16-bit textruns only, check for surrogate pairs and 1.5318 + // special Unicode spaces; omit these checks in 8-bit runs 1.5319 + if (sizeof(T) == sizeof(char16_t)) { 1.5320 + if (NS_IS_HIGH_SURROGATE(ch) && 1.5321 + index + 1 < aScriptRunEnd && 1.5322 + NS_IS_LOW_SURROGATE(aString[index + 1])) 1.5323 + { 1.5324 + aTextRun->SetMissingGlyph(index, 1.5325 + SURROGATE_TO_UCS4(ch, 1.5326 + aString[index + 1]), 1.5327 + mainFont); 1.5328 + index++; 1.5329 + continue; 1.5330 + } 1.5331 + 1.5332 + // check if this is a known Unicode whitespace character that 1.5333 + // we can render using the space glyph with a custom width 1.5334 + gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch); 1.5335 + if (wid >= 0.0) { 1.5336 + nscoord advance = 1.5337 + aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5); 1.5338 + if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) { 1.5339 + aTextRun->GetCharacterGlyphs()[index]. 1.5340 + SetSimpleGlyph(advance, 1.5341 + mainFont->GetSpaceGlyph()); 1.5342 + } else { 1.5343 + gfxTextRun::DetailedGlyph detailedGlyph; 1.5344 + detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph(); 1.5345 + detailedGlyph.mAdvance = advance; 1.5346 + detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0; 1.5347 + gfxShapedText::CompressedGlyph g; 1.5348 + g.SetComplex(true, true, 1); 1.5349 + aTextRun->SetGlyphs(index, 1.5350 + g, &detailedGlyph); 1.5351 + } 1.5352 + continue; 1.5353 + } 1.5354 + } 1.5355 + 1.5356 + if (IsInvalidChar(ch)) { 1.5357 + // invalid chars are left as zero-width/invisible 1.5358 + continue; 1.5359 + } 1.5360 + 1.5361 + // record char code so we can draw a box with the Unicode value 1.5362 + aTextRun->SetMissingGlyph(index, ch, mainFont); 1.5363 + } 1.5364 + } 1.5365 + 1.5366 + runStart += matchedLength; 1.5367 + } 1.5368 +} 1.5369 + 1.5370 +gfxTextRun * 1.5371 +gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel, 1.5372 + LazyReferenceContextGetter& aRefContextGetter) 1.5373 +{ 1.5374 + if (mCachedEllipsisTextRun && 1.5375 + mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) { 1.5376 + return mCachedEllipsisTextRun; 1.5377 + } 1.5378 + 1.5379 + // Use a Unicode ellipsis if the font supports it, 1.5380 + // otherwise use three ASCII periods as fallback. 1.5381 + gfxFont* firstFont = GetFontAt(0); 1.5382 + nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0]) 1.5383 + ? nsDependentString(kEllipsisChar, 1.5384 + ArrayLength(kEllipsisChar) - 1) 1.5385 + : nsDependentString(kASCIIPeriodsChar, 1.5386 + ArrayLength(kASCIIPeriodsChar) - 1); 1.5387 + 1.5388 + nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext(); 1.5389 + Parameters params = { 1.5390 + refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel 1.5391 + }; 1.5392 + gfxTextRun* textRun = 1.5393 + MakeTextRun(ellipsis.get(), ellipsis.Length(), ¶ms, TEXT_IS_PERSISTENT); 1.5394 + if (!textRun) { 1.5395 + return nullptr; 1.5396 + } 1.5397 + mCachedEllipsisTextRun = textRun; 1.5398 + textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis 1.5399 + // textrun prolong the fontgroup's life 1.5400 + return textRun; 1.5401 +} 1.5402 + 1.5403 +already_AddRefed<gfxFont> 1.5404 +gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh) 1.5405 +{ 1.5406 + if (!aFamily->TestCharacterMap(aCh)) { 1.5407 + return nullptr; 1.5408 + } 1.5409 + 1.5410 + // Note that we don't need the actual runScript in matchData for 1.5411 + // gfxFontFamily::SearchAllFontsForChar, it's only used for the 1.5412 + // system-fallback case. So we can just set it to 0 here. 1.5413 + GlobalFontMatch matchData(aCh, 0, &mStyle); 1.5414 + aFamily->SearchAllFontsForChar(&matchData); 1.5415 + gfxFontEntry *fe = matchData.mBestMatch; 1.5416 + if (!fe) { 1.5417 + return nullptr; 1.5418 + } 1.5419 + 1.5420 + bool needsBold = mStyle.weight >= 600 && !fe->IsBold(); 1.5421 + nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold); 1.5422 + return font.forget(); 1.5423 +} 1.5424 + 1.5425 +already_AddRefed<gfxFont> 1.5426 +gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, 1.5427 + int32_t aRunScript, gfxFont *aPrevMatchedFont, 1.5428 + uint8_t *aMatchType) 1.5429 +{ 1.5430 + // To optimize common cases, try the first font in the font-group 1.5431 + // before going into the more detailed checks below 1.5432 + uint32_t nextIndex = 0; 1.5433 + bool isJoinControl = gfxFontUtils::IsJoinControl(aCh); 1.5434 + bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh); 1.5435 + bool isVarSelector = gfxFontUtils::IsVarSelector(aCh); 1.5436 + 1.5437 + if (!isJoinControl && !wasJoinCauser && !isVarSelector) { 1.5438 + nsRefPtr<gfxFont> firstFont = mFonts[0].Font(); 1.5439 + if (firstFont->HasCharacter(aCh)) { 1.5440 + *aMatchType = gfxTextRange::kFontGroup; 1.5441 + return firstFont.forget(); 1.5442 + } 1.5443 + // It's possible that another font in the family (e.g. regular face, 1.5444 + // where the requested style was italic) will support the character 1.5445 + nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh); 1.5446 + if (font) { 1.5447 + *aMatchType = gfxTextRange::kFontGroup; 1.5448 + return font.forget(); 1.5449 + } 1.5450 + // we don't need to check the first font again below 1.5451 + ++nextIndex; 1.5452 + } 1.5453 + 1.5454 + if (aPrevMatchedFont) { 1.5455 + // Don't switch fonts for control characters, regardless of 1.5456 + // whether they are present in the current font, as they won't 1.5457 + // actually be rendered (see bug 716229) 1.5458 + if (isJoinControl || 1.5459 + GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) { 1.5460 + nsRefPtr<gfxFont> ret = aPrevMatchedFont; 1.5461 + return ret.forget(); 1.5462 + } 1.5463 + 1.5464 + // if previous character was a join-causer (ZWJ), 1.5465 + // use the same font as the previous range if we can 1.5466 + if (wasJoinCauser) { 1.5467 + if (aPrevMatchedFont->HasCharacter(aCh)) { 1.5468 + nsRefPtr<gfxFont> ret = aPrevMatchedFont; 1.5469 + return ret.forget(); 1.5470 + } 1.5471 + } 1.5472 + } 1.5473 + 1.5474 + // if this character is a variation selector, 1.5475 + // use the previous font regardless of whether it supports VS or not. 1.5476 + // otherwise the text run will be divided. 1.5477 + if (isVarSelector) { 1.5478 + if (aPrevMatchedFont) { 1.5479 + nsRefPtr<gfxFont> ret = aPrevMatchedFont; 1.5480 + return ret.forget(); 1.5481 + } 1.5482 + // VS alone. it's meaningless to search different fonts 1.5483 + return nullptr; 1.5484 + } 1.5485 + 1.5486 + // 1. check remaining fonts in the font group 1.5487 + uint32_t fontListLength = FontListLength(); 1.5488 + for (uint32_t i = nextIndex; i < fontListLength; i++) { 1.5489 + nsRefPtr<gfxFont> font = mFonts[i].Font(); 1.5490 + if (font->HasCharacter(aCh)) { 1.5491 + *aMatchType = gfxTextRange::kFontGroup; 1.5492 + return font.forget(); 1.5493 + } 1.5494 + 1.5495 + font = TryAllFamilyMembers(mFonts[i].Family(), aCh); 1.5496 + if (font) { 1.5497 + *aMatchType = gfxTextRange::kFontGroup; 1.5498 + return font.forget(); 1.5499 + } 1.5500 + } 1.5501 + 1.5502 + // if character is in Private Use Area, don't do matching against pref or system fonts 1.5503 + if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD)) 1.5504 + return nullptr; 1.5505 + 1.5506 + // 2. search pref fonts 1.5507 + nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh); 1.5508 + if (font) { 1.5509 + *aMatchType = gfxTextRange::kPrefsFallback; 1.5510 + return font.forget(); 1.5511 + } 1.5512 + 1.5513 + // 3. use fallback fonts 1.5514 + // -- before searching for something else check the font used for the previous character 1.5515 + if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) { 1.5516 + *aMatchType = gfxTextRange::kSystemFallback; 1.5517 + nsRefPtr<gfxFont> ret = aPrevMatchedFont; 1.5518 + return ret.forget(); 1.5519 + } 1.5520 + 1.5521 + // never fall back for characters from unknown scripts 1.5522 + if (aRunScript == HB_SCRIPT_UNKNOWN) { 1.5523 + return nullptr; 1.5524 + } 1.5525 + 1.5526 + // for known "space" characters, don't do a full system-fallback search; 1.5527 + // we'll synthesize appropriate-width spaces instead of missing-glyph boxes 1.5528 + if (GetGeneralCategory(aCh) == 1.5529 + HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR && 1.5530 + GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0) 1.5531 + { 1.5532 + return nullptr; 1.5533 + } 1.5534 + 1.5535 + // -- otherwise look for other stuff 1.5536 + *aMatchType = gfxTextRange::kSystemFallback; 1.5537 + font = WhichSystemFontSupportsChar(aCh, aRunScript); 1.5538 + return font.forget(); 1.5539 +} 1.5540 + 1.5541 +template<typename T> 1.5542 +void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges, 1.5543 + const T *aString, uint32_t aLength, 1.5544 + int32_t aRunScript) 1.5545 +{ 1.5546 + NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty"); 1.5547 + NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text"); 1.5548 + 1.5549 + uint32_t prevCh = 0; 1.5550 + int32_t lastRangeIndex = -1; 1.5551 + 1.5552 + // initialize prevFont to the group's primary font, so that this will be 1.5553 + // used for string-initial control chars, etc rather than risk hitting font 1.5554 + // fallback for these (bug 716229) 1.5555 + gfxFont *prevFont = GetFontAt(0); 1.5556 + 1.5557 + // if we use the initial value of prevFont, we treat this as a match from 1.5558 + // the font group; fixes bug 978313 1.5559 + uint8_t matchType = gfxTextRange::kFontGroup; 1.5560 + 1.5561 + for (uint32_t i = 0; i < aLength; i++) { 1.5562 + 1.5563 + const uint32_t origI = i; // save off in case we increase for surrogate 1.5564 + 1.5565 + // set up current ch 1.5566 + uint32_t ch = aString[i]; 1.5567 + 1.5568 + // in 16-bit case only, check for surrogate pair 1.5569 + if (sizeof(T) == sizeof(char16_t)) { 1.5570 + if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) && 1.5571 + NS_IS_LOW_SURROGATE(aString[i + 1])) { 1.5572 + i++; 1.5573 + ch = SURROGATE_TO_UCS4(ch, aString[i]); 1.5574 + } 1.5575 + } 1.5576 + 1.5577 + if (ch == 0xa0) { 1.5578 + ch = ' '; 1.5579 + } 1.5580 + 1.5581 + // find the font for this char 1.5582 + nsRefPtr<gfxFont> font = 1.5583 + FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType); 1.5584 + 1.5585 +#ifndef RELEASE_BUILD 1.5586 + if (MOZ_UNLIKELY(mTextPerf)) { 1.5587 + if (matchType == gfxTextRange::kPrefsFallback) { 1.5588 + mTextPerf->current.fallbackPrefs++; 1.5589 + } else if (matchType == gfxTextRange::kSystemFallback) { 1.5590 + mTextPerf->current.fallbackSystem++; 1.5591 + } 1.5592 + } 1.5593 +#endif 1.5594 + 1.5595 + prevCh = ch; 1.5596 + 1.5597 + if (lastRangeIndex == -1) { 1.5598 + // first char ==> make a new range 1.5599 + aRanges.AppendElement(gfxTextRange(0, 1, font, matchType)); 1.5600 + lastRangeIndex++; 1.5601 + prevFont = font; 1.5602 + } else { 1.5603 + // if font has changed, make a new range 1.5604 + gfxTextRange& prevRange = aRanges[lastRangeIndex]; 1.5605 + if (prevRange.font != font || prevRange.matchType != matchType) { 1.5606 + // close out the previous range 1.5607 + prevRange.end = origI; 1.5608 + aRanges.AppendElement(gfxTextRange(origI, i + 1, 1.5609 + font, matchType)); 1.5610 + lastRangeIndex++; 1.5611 + 1.5612 + // update prevFont for the next match, *unless* we switched 1.5613 + // fonts on a ZWJ, in which case propagating the changed font 1.5614 + // is probably not a good idea (see bug 619511) 1.5615 + if (sizeof(T) == sizeof(uint8_t) || 1.5616 + !gfxFontUtils::IsJoinCauser(ch)) 1.5617 + { 1.5618 + prevFont = font; 1.5619 + } 1.5620 + } 1.5621 + } 1.5622 + } 1.5623 + 1.5624 + aRanges[lastRangeIndex].end = aLength; 1.5625 +} 1.5626 + 1.5627 +gfxUserFontSet* 1.5628 +gfxFontGroup::GetUserFontSet() 1.5629 +{ 1.5630 + return mUserFontSet; 1.5631 +} 1.5632 + 1.5633 +void 1.5634 +gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet) 1.5635 +{ 1.5636 + if (aUserFontSet == mUserFontSet) { 1.5637 + return; 1.5638 + } 1.5639 + mUserFontSet = aUserFontSet; 1.5640 + mCurrGeneration = GetGeneration() - 1; 1.5641 + UpdateFontList(); 1.5642 +} 1.5643 + 1.5644 +uint64_t 1.5645 +gfxFontGroup::GetGeneration() 1.5646 +{ 1.5647 + if (!mUserFontSet) 1.5648 + return 0; 1.5649 + return mUserFontSet->GetGeneration(); 1.5650 +} 1.5651 + 1.5652 +void 1.5653 +gfxFontGroup::UpdateFontList() 1.5654 +{ 1.5655 + if (mCurrGeneration != GetGeneration()) { 1.5656 + // xxx - can probably improve this to detect when all fonts were found, so no need to update list 1.5657 + mFonts.Clear(); 1.5658 + mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET; 1.5659 + mSkipDrawing = false; 1.5660 + 1.5661 + // bug 548184 - need to clean up FT2, OS/2 platform code to use BuildFontList 1.5662 +#if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID) 1.5663 + BuildFontList(); 1.5664 +#else 1.5665 + ForEachFont(FindPlatformFont, this); 1.5666 +#endif 1.5667 + mCurrGeneration = GetGeneration(); 1.5668 + mCachedEllipsisTextRun = nullptr; 1.5669 + } 1.5670 +} 1.5671 + 1.5672 +struct PrefFontCallbackData { 1.5673 + PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray) 1.5674 + : mPrefFamilies(aFamiliesArray) 1.5675 + {} 1.5676 + 1.5677 + nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies; 1.5678 + 1.5679 + static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure) 1.5680 + { 1.5681 + PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure); 1.5682 + 1.5683 + gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName); 1.5684 + if (family) { 1.5685 + prefFontData->mPrefFamilies.AppendElement(family); 1.5686 + } 1.5687 + return true; 1.5688 + } 1.5689 +}; 1.5690 + 1.5691 +already_AddRefed<gfxFont> 1.5692 +gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh) 1.5693 +{ 1.5694 + nsRefPtr<gfxFont> font; 1.5695 + 1.5696 + // get the pref font list if it hasn't been set up already 1.5697 + uint32_t unicodeRange = FindCharUnicodeRange(aCh); 1.5698 + eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange); 1.5699 + 1.5700 + // if the last pref font was the first family in the pref list, no need to recheck through a list of families 1.5701 + if (mLastPrefFont && charLang == mLastPrefLang && 1.5702 + mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) { 1.5703 + font = mLastPrefFont; 1.5704 + return font.forget(); 1.5705 + } 1.5706 + 1.5707 + // based on char lang and page lang, set up list of pref lang fonts to check 1.5708 + eFontPrefLang prefLangs[kMaxLenPrefLangList]; 1.5709 + uint32_t i, numLangs = 0; 1.5710 + 1.5711 + gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang); 1.5712 + 1.5713 + for (i = 0; i < numLangs; i++) { 1.5714 + nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families; 1.5715 + eFontPrefLang currentLang = prefLangs[i]; 1.5716 + 1.5717 + gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList(); 1.5718 + 1.5719 + // get the pref families for a single pref lang 1.5720 + if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) { 1.5721 + eFontPrefLang prefLangsToSearch[1] = { currentLang }; 1.5722 + PrefFontCallbackData prefFontData(families); 1.5723 + gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry, 1.5724 + &prefFontData); 1.5725 + fontList->SetPrefFontFamilyEntries(currentLang, families); 1.5726 + } 1.5727 + 1.5728 + // find the first pref font that includes the character 1.5729 + uint32_t j, numPrefs; 1.5730 + numPrefs = families.Length(); 1.5731 + for (j = 0; j < numPrefs; j++) { 1.5732 + // look up the appropriate face 1.5733 + gfxFontFamily *family = families[j]; 1.5734 + if (!family) continue; 1.5735 + 1.5736 + // if a pref font is used, it's likely to be used again in the same text run. 1.5737 + // the style doesn't change so the face lookup can be cached rather than calling 1.5738 + // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent 1.5739 + // pref font lookups 1.5740 + if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) { 1.5741 + font = mLastPrefFont; 1.5742 + return font.forget(); 1.5743 + } 1.5744 + 1.5745 + bool needsBold; 1.5746 + gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold); 1.5747 + // if ch in cmap, create and return a gfxFont 1.5748 + if (fe && fe->TestCharacterMap(aCh)) { 1.5749 + nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold); 1.5750 + if (!prefFont) continue; 1.5751 + mLastPrefFamily = family; 1.5752 + mLastPrefFont = prefFont; 1.5753 + mLastPrefLang = charLang; 1.5754 + mLastPrefFirstFont = (i == 0 && j == 0); 1.5755 + return prefFont.forget(); 1.5756 + } 1.5757 + 1.5758 + } 1.5759 + } 1.5760 + 1.5761 + return nullptr; 1.5762 +} 1.5763 + 1.5764 +already_AddRefed<gfxFont> 1.5765 +gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript) 1.5766 +{ 1.5767 + gfxFontEntry *fe = 1.5768 + gfxPlatformFontList::PlatformFontList()-> 1.5769 + SystemFindFontForChar(aCh, aRunScript, &mStyle); 1.5770 + if (fe) { 1.5771 + bool wantBold = mStyle.ComputeWeight() >= 6; 1.5772 + nsRefPtr<gfxFont> font = 1.5773 + fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold()); 1.5774 + return font.forget(); 1.5775 + } 1.5776 + 1.5777 + return nullptr; 1.5778 +} 1.5779 + 1.5780 +/*static*/ void 1.5781 +gfxFontGroup::Shutdown() 1.5782 +{ 1.5783 + NS_IF_RELEASE(gLangService); 1.5784 +} 1.5785 + 1.5786 +nsILanguageAtomService* gfxFontGroup::gLangService = nullptr; 1.5787 + 1.5788 + 1.5789 +#define DEFAULT_PIXEL_FONT_SIZE 16.0f 1.5790 + 1.5791 +/*static*/ uint32_t 1.5792 +gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag) 1.5793 +{ 1.5794 + if (!aLangTag.Length() || aLangTag.Length() > 4) { 1.5795 + return NO_FONT_LANGUAGE_OVERRIDE; 1.5796 + } 1.5797 + uint32_t index, result = 0; 1.5798 + for (index = 0; index < aLangTag.Length(); ++index) { 1.5799 + char16_t ch = aLangTag[index]; 1.5800 + if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII 1.5801 + return NO_FONT_LANGUAGE_OVERRIDE; 1.5802 + } 1.5803 + result = (result << 8) + ch; 1.5804 + } 1.5805 + while (index++ < 4) { 1.5806 + result = (result << 8) + 0x20; 1.5807 + } 1.5808 + return result; 1.5809 +} 1.5810 + 1.5811 +gfxFontStyle::gfxFontStyle() : 1.5812 + language(nsGkAtoms::x_western), 1.5813 + size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f), 1.5814 + languageOverride(NO_FONT_LANGUAGE_OVERRIDE), 1.5815 + weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL), 1.5816 + systemFont(true), printerFont(false), useGrayscaleAntialiasing(false), 1.5817 + style(NS_FONT_STYLE_NORMAL) 1.5818 +{ 1.5819 +} 1.5820 + 1.5821 +gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch, 1.5822 + gfxFloat aSize, nsIAtom *aLanguage, 1.5823 + float aSizeAdjust, bool aSystemFont, 1.5824 + bool aPrinterFont, 1.5825 + const nsString& aLanguageOverride): 1.5826 + language(aLanguage), 1.5827 + size(aSize), sizeAdjust(aSizeAdjust), 1.5828 + languageOverride(ParseFontLanguageOverride(aLanguageOverride)), 1.5829 + weight(aWeight), stretch(aStretch), 1.5830 + systemFont(aSystemFont), printerFont(aPrinterFont), 1.5831 + useGrayscaleAntialiasing(false), style(aStyle) 1.5832 +{ 1.5833 + MOZ_ASSERT(!mozilla::IsNaN(size)); 1.5834 + MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust)); 1.5835 + 1.5836 + if (weight > 900) 1.5837 + weight = 900; 1.5838 + if (weight < 100) 1.5839 + weight = 100; 1.5840 + 1.5841 + if (size >= FONT_MAX_SIZE) { 1.5842 + size = FONT_MAX_SIZE; 1.5843 + sizeAdjust = 0.0; 1.5844 + } else if (size < 0.0) { 1.5845 + NS_WARNING("negative font size"); 1.5846 + size = 0.0; 1.5847 + } 1.5848 + 1.5849 + if (!language) { 1.5850 + NS_WARNING("null language"); 1.5851 + language = nsGkAtoms::x_western; 1.5852 + } 1.5853 +} 1.5854 + 1.5855 +gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) : 1.5856 + language(aStyle.language), 1.5857 + featureValueLookup(aStyle.featureValueLookup), 1.5858 + size(aStyle.size), sizeAdjust(aStyle.sizeAdjust), 1.5859 + languageOverride(aStyle.languageOverride), 1.5860 + weight(aStyle.weight), stretch(aStyle.stretch), 1.5861 + systemFont(aStyle.systemFont), printerFont(aStyle.printerFont), 1.5862 + useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing), 1.5863 + style(aStyle.style) 1.5864 +{ 1.5865 + featureSettings.AppendElements(aStyle.featureSettings); 1.5866 + alternateValues.AppendElements(aStyle.alternateValues); 1.5867 +} 1.5868 + 1.5869 +int8_t 1.5870 +gfxFontStyle::ComputeWeight() const 1.5871 +{ 1.5872 + int8_t baseWeight = (weight + 50) / 100; 1.5873 + 1.5874 + if (baseWeight < 0) 1.5875 + baseWeight = 0; 1.5876 + if (baseWeight > 9) 1.5877 + baseWeight = 9; 1.5878 + 1.5879 + return baseWeight; 1.5880 +} 1.5881 + 1.5882 +void 1.5883 +gfxShapedText::SetupClusterBoundaries(uint32_t aOffset, 1.5884 + const char16_t *aString, 1.5885 + uint32_t aLength) 1.5886 +{ 1.5887 + CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset; 1.5888 + 1.5889 + gfxTextRun::CompressedGlyph extendCluster; 1.5890 + extendCluster.SetComplex(false, true, 0); 1.5891 + 1.5892 + ClusterIterator iter(aString, aLength); 1.5893 + 1.5894 + // the ClusterIterator won't be able to tell us if the string 1.5895 + // _begins_ with a cluster-extender, so we handle that here 1.5896 + if (aLength && IsClusterExtender(*aString)) { 1.5897 + *glyphs = extendCluster; 1.5898 + } 1.5899 + 1.5900 + while (!iter.AtEnd()) { 1.5901 + if (*iter == char16_t(' ')) { 1.5902 + glyphs->SetIsSpace(); 1.5903 + } 1.5904 + // advance iter to the next cluster-start (or end of text) 1.5905 + iter.Next(); 1.5906 + // step past the first char of the cluster 1.5907 + aString++; 1.5908 + glyphs++; 1.5909 + // mark all the rest as cluster-continuations 1.5910 + while (aString < iter) { 1.5911 + *glyphs = extendCluster; 1.5912 + if (NS_IS_LOW_SURROGATE(*aString)) { 1.5913 + glyphs->SetIsLowSurrogate(); 1.5914 + } 1.5915 + glyphs++; 1.5916 + aString++; 1.5917 + } 1.5918 + } 1.5919 +} 1.5920 + 1.5921 +void 1.5922 +gfxShapedText::SetupClusterBoundaries(uint32_t aOffset, 1.5923 + const uint8_t *aString, 1.5924 + uint32_t aLength) 1.5925 +{ 1.5926 + CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset; 1.5927 + const uint8_t *limit = aString + aLength; 1.5928 + 1.5929 + while (aString < limit) { 1.5930 + if (*aString == uint8_t(' ')) { 1.5931 + glyphs->SetIsSpace(); 1.5932 + } 1.5933 + aString++; 1.5934 + glyphs++; 1.5935 + } 1.5936 +} 1.5937 + 1.5938 +gfxShapedText::DetailedGlyph * 1.5939 +gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount) 1.5940 +{ 1.5941 + NS_ASSERTION(aIndex < GetLength(), "Index out of range"); 1.5942 + 1.5943 + if (!mDetailedGlyphs) { 1.5944 + mDetailedGlyphs = new DetailedGlyphStore(); 1.5945 + } 1.5946 + 1.5947 + DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount); 1.5948 + if (!details) { 1.5949 + GetCharacterGlyphs()[aIndex].SetMissing(0); 1.5950 + return nullptr; 1.5951 + } 1.5952 + 1.5953 + return details; 1.5954 +} 1.5955 + 1.5956 +void 1.5957 +gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph, 1.5958 + const DetailedGlyph *aGlyphs) 1.5959 +{ 1.5960 + NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here"); 1.5961 + NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(), 1.5962 + "First character can't be a ligature continuation!"); 1.5963 + 1.5964 + uint32_t glyphCount = aGlyph.GetGlyphCount(); 1.5965 + if (glyphCount > 0) { 1.5966 + DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount); 1.5967 + if (!details) { 1.5968 + return; 1.5969 + } 1.5970 + memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount); 1.5971 + } 1.5972 + GetCharacterGlyphs()[aIndex] = aGlyph; 1.5973 +} 1.5974 + 1.5975 +#define ZWNJ 0x200C 1.5976 +#define ZWJ 0x200D 1.5977 +// U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE 1.5978 +// in a future Unicode update. Add it manually for now 1.5979 +#define ALM 0x061C 1.5980 +static inline bool 1.5981 +IsDefaultIgnorable(uint32_t aChar) 1.5982 +{ 1.5983 + return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE || 1.5984 + aChar == ZWNJ || aChar == ZWJ || aChar == ALM; 1.5985 +} 1.5986 + 1.5987 +void 1.5988 +gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont) 1.5989 +{ 1.5990 + uint8_t category = GetGeneralCategory(aChar); 1.5991 + if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK && 1.5992 + category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) 1.5993 + { 1.5994 + GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0); 1.5995 + } 1.5996 + 1.5997 + DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); 1.5998 + if (!details) { 1.5999 + return; 1.6000 + } 1.6001 + 1.6002 + details->mGlyphID = aChar; 1.6003 + if (IsDefaultIgnorable(aChar)) { 1.6004 + // Setting advance width to zero will prevent drawing the hexbox 1.6005 + details->mAdvance = 0; 1.6006 + } else { 1.6007 + gfxFloat width = 1.6008 + std::max(aFont->GetMetrics().aveCharWidth, 1.6009 + gfxFontMissingGlyphs::GetDesiredMinWidth(aChar, 1.6010 + mAppUnitsPerDevUnit)); 1.6011 + details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit); 1.6012 + } 1.6013 + details->mXOffset = 0; 1.6014 + details->mYOffset = 0; 1.6015 + GetCharacterGlyphs()[aIndex].SetMissing(1); 1.6016 +} 1.6017 + 1.6018 +bool 1.6019 +gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) 1.6020 +{ 1.6021 + if (IsDefaultIgnorable(aCh)) { 1.6022 + DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1); 1.6023 + if (details) { 1.6024 + details->mGlyphID = aCh; 1.6025 + details->mAdvance = 0; 1.6026 + details->mXOffset = 0; 1.6027 + details->mYOffset = 0; 1.6028 + GetCharacterGlyphs()[aIndex].SetMissing(1); 1.6029 + return true; 1.6030 + } 1.6031 + } 1.6032 + return false; 1.6033 +} 1.6034 + 1.6035 +void 1.6036 +gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset, 1.6037 + uint32_t aOffset, 1.6038 + uint32_t aLength) 1.6039 +{ 1.6040 + uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit; 1.6041 + CompressedGlyph *charGlyphs = GetCharacterGlyphs(); 1.6042 + for (uint32_t i = aOffset; i < aOffset + aLength; ++i) { 1.6043 + CompressedGlyph *glyphData = charGlyphs + i; 1.6044 + if (glyphData->IsSimpleGlyph()) { 1.6045 + // simple glyphs ==> just add the advance 1.6046 + int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset; 1.6047 + if (CompressedGlyph::IsSimpleAdvance(advance)) { 1.6048 + glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph()); 1.6049 + } else { 1.6050 + // rare case, tested by making this the default 1.6051 + uint32_t glyphIndex = glyphData->GetSimpleGlyph(); 1.6052 + glyphData->SetComplex(true, true, 1); 1.6053 + DetailedGlyph detail = {glyphIndex, advance, 0, 0}; 1.6054 + SetGlyphs(i, *glyphData, &detail); 1.6055 + } 1.6056 + } else { 1.6057 + // complex glyphs ==> add offset at cluster/ligature boundaries 1.6058 + uint32_t detailedLength = glyphData->GetGlyphCount(); 1.6059 + if (detailedLength) { 1.6060 + DetailedGlyph *details = GetDetailedGlyphs(i); 1.6061 + if (!details) { 1.6062 + continue; 1.6063 + } 1.6064 + if (IsRightToLeft()) { 1.6065 + details[0].mAdvance += synAppUnitOffset; 1.6066 + } else { 1.6067 + details[detailedLength - 1].mAdvance += synAppUnitOffset; 1.6068 + } 1.6069 + } 1.6070 + } 1.6071 + } 1.6072 +} 1.6073 + 1.6074 +bool 1.6075 +gfxTextRun::GlyphRunIterator::NextRun() { 1.6076 + if (mNextIndex >= mTextRun->mGlyphRuns.Length()) 1.6077 + return false; 1.6078 + mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex]; 1.6079 + if (mGlyphRun->mCharacterOffset >= mEndOffset) 1.6080 + return false; 1.6081 + 1.6082 + mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset); 1.6083 + uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length() 1.6084 + ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength(); 1.6085 + mStringEnd = std::min(mEndOffset, last); 1.6086 + 1.6087 + ++mNextIndex; 1.6088 + return true; 1.6089 +} 1.6090 + 1.6091 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.6092 +static void 1.6093 +AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign) 1.6094 +{ 1.6095 + // Ignores detailed glyphs... we don't know when those have been constructed 1.6096 + // Also ignores gfxSkipChars dynamic storage (which won't be anything 1.6097 + // for preformatted text) 1.6098 + // Also ignores GlyphRun array, again because it hasn't been constructed 1.6099 + // by the time this gets called. If there's only one glyphrun that's stored 1.6100 + // directly in the textrun anyway so no additional overhead. 1.6101 + uint32_t length = aTextRun->GetLength(); 1.6102 + int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph); 1.6103 + bytes += sizeof(gfxTextRun); 1.6104 + gTextRunStorage += bytes*aSign; 1.6105 + gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage); 1.6106 +} 1.6107 +#endif 1.6108 + 1.6109 +// Helper for textRun creation to preallocate storage for glyph records; 1.6110 +// this function returns a pointer to the newly-allocated glyph storage. 1.6111 +// Returns nullptr if allocation fails. 1.6112 +void * 1.6113 +gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength) 1.6114 +{ 1.6115 + // Allocate the storage we need, returning nullptr on failure rather than 1.6116 + // throwing an exception (because web content can create huge runs). 1.6117 + void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph)); 1.6118 + if (!storage) { 1.6119 + NS_WARNING("failed to allocate storage for text run!"); 1.6120 + return nullptr; 1.6121 + } 1.6122 + 1.6123 + // Initialize the glyph storage (beyond aSize) to zero 1.6124 + memset(reinterpret_cast<char*>(storage) + aSize, 0, 1.6125 + aLength * sizeof(CompressedGlyph)); 1.6126 + 1.6127 + return storage; 1.6128 +} 1.6129 + 1.6130 +gfxTextRun * 1.6131 +gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams, 1.6132 + uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags) 1.6133 +{ 1.6134 + void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength); 1.6135 + if (!storage) { 1.6136 + return nullptr; 1.6137 + } 1.6138 + 1.6139 + return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags); 1.6140 +} 1.6141 + 1.6142 +gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams, 1.6143 + uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags) 1.6144 + : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit) 1.6145 + , mUserData(aParams->mUserData) 1.6146 + , mFontGroup(aFontGroup) 1.6147 + , mReleasedFontGroup(false) 1.6148 +{ 1.6149 + NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale"); 1.6150 + MOZ_COUNT_CTOR(gfxTextRun); 1.6151 + NS_ADDREF(mFontGroup); 1.6152 + 1.6153 +#ifndef RELEASE_BUILD 1.6154 + gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics(); 1.6155 + if (tp) { 1.6156 + tp->current.textrunConst++; 1.6157 + } 1.6158 +#endif 1.6159 + 1.6160 + mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1); 1.6161 + 1.6162 + if (aParams->mSkipChars) { 1.6163 + mSkipChars.TakeFrom(aParams->mSkipChars); 1.6164 + } 1.6165 + 1.6166 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.6167 + AccountStorageForTextRun(this, 1); 1.6168 +#endif 1.6169 + 1.6170 + mSkipDrawing = mFontGroup->ShouldSkipDrawing(); 1.6171 +} 1.6172 + 1.6173 +gfxTextRun::~gfxTextRun() 1.6174 +{ 1.6175 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.6176 + AccountStorageForTextRun(this, -1); 1.6177 +#endif 1.6178 +#ifdef DEBUG 1.6179 + // Make it easy to detect a dead text run 1.6180 + mFlags = 0xFFFFFFFF; 1.6181 +#endif 1.6182 + 1.6183 + // The cached ellipsis textrun (if any) in a fontgroup will have already 1.6184 + // been told to release its reference to the group, so we mustn't do that 1.6185 + // again here. 1.6186 + if (!mReleasedFontGroup) { 1.6187 +#ifndef RELEASE_BUILD 1.6188 + gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics(); 1.6189 + if (tp) { 1.6190 + tp->current.textrunDestr++; 1.6191 + } 1.6192 +#endif 1.6193 + NS_RELEASE(mFontGroup); 1.6194 + } 1.6195 + 1.6196 + MOZ_COUNT_DTOR(gfxTextRun); 1.6197 +} 1.6198 + 1.6199 +void 1.6200 +gfxTextRun::ReleaseFontGroup() 1.6201 +{ 1.6202 + NS_ASSERTION(!mReleasedFontGroup, "doubly released!"); 1.6203 + NS_RELEASE(mFontGroup); 1.6204 + mReleasedFontGroup = true; 1.6205 +} 1.6206 + 1.6207 +bool 1.6208 +gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength, 1.6209 + uint8_t *aBreakBefore, 1.6210 + gfxContext *aRefContext) 1.6211 +{ 1.6212 + NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow"); 1.6213 + 1.6214 + uint32_t changed = 0; 1.6215 + uint32_t i; 1.6216 + CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart; 1.6217 + for (i = 0; i < aLength; ++i) { 1.6218 + uint8_t canBreak = aBreakBefore[i]; 1.6219 + if (canBreak && !charGlyphs[i].IsClusterStart()) { 1.6220 + // This can happen ... there is no guarantee that our linebreaking rules 1.6221 + // align with the platform's idea of what constitutes a cluster. 1.6222 + NS_WARNING("Break suggested inside cluster!"); 1.6223 + canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE; 1.6224 + } 1.6225 + changed |= charGlyphs[i].SetCanBreakBefore(canBreak); 1.6226 + } 1.6227 + return changed != 0; 1.6228 +} 1.6229 + 1.6230 +gfxTextRun::LigatureData 1.6231 +gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd, 1.6232 + PropertyProvider *aProvider) 1.6233 +{ 1.6234 + NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range"); 1.6235 + NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow"); 1.6236 + 1.6237 + LigatureData result; 1.6238 + CompressedGlyph *charGlyphs = mCharacterGlyphs; 1.6239 + 1.6240 + uint32_t i; 1.6241 + for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) { 1.6242 + NS_ASSERTION(i > 0, "Ligature at the start of the run??"); 1.6243 + } 1.6244 + result.mLigatureStart = i; 1.6245 + for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) { 1.6246 + } 1.6247 + result.mLigatureEnd = i; 1.6248 + 1.6249 + int32_t ligatureWidth = 1.6250 + GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd); 1.6251 + // Count the number of started clusters we have seen 1.6252 + uint32_t totalClusterCount = 0; 1.6253 + uint32_t partClusterIndex = 0; 1.6254 + uint32_t partClusterCount = 0; 1.6255 + for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) { 1.6256 + // Treat the first character of the ligature as the start of a 1.6257 + // cluster for our purposes of allocating ligature width to its 1.6258 + // characters. 1.6259 + if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) { 1.6260 + ++totalClusterCount; 1.6261 + if (i < aPartStart) { 1.6262 + ++partClusterIndex; 1.6263 + } else if (i < aPartEnd) { 1.6264 + ++partClusterCount; 1.6265 + } 1.6266 + } 1.6267 + } 1.6268 + NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??"); 1.6269 + result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount); 1.6270 + result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount); 1.6271 + 1.6272 + // Any rounding errors are apportioned to the final part of the ligature, 1.6273 + // so that measuring all parts of a ligature and summing them is equal to 1.6274 + // the ligature width. 1.6275 + if (aPartEnd == result.mLigatureEnd) { 1.6276 + gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount); 1.6277 + result.mPartWidth += ligatureWidth - allParts; 1.6278 + } 1.6279 + 1.6280 + if (partClusterCount == 0) { 1.6281 + // nothing to draw 1.6282 + result.mClipBeforePart = result.mClipAfterPart = true; 1.6283 + } else { 1.6284 + // Determine whether we should clip before or after this part when 1.6285 + // drawing its slice of the ligature. 1.6286 + // We need to clip before the part if any cluster is drawn before 1.6287 + // this part. 1.6288 + result.mClipBeforePart = partClusterIndex > 0; 1.6289 + // We need to clip after the part if any cluster is drawn after 1.6290 + // this part. 1.6291 + result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount; 1.6292 + } 1.6293 + 1.6294 + if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) { 1.6295 + gfxFont::Spacing spacing; 1.6296 + if (aPartStart == result.mLigatureStart) { 1.6297 + aProvider->GetSpacing(aPartStart, 1, &spacing); 1.6298 + result.mPartWidth += spacing.mBefore; 1.6299 + } 1.6300 + if (aPartEnd == result.mLigatureEnd) { 1.6301 + aProvider->GetSpacing(aPartEnd - 1, 1, &spacing); 1.6302 + result.mPartWidth += spacing.mAfter; 1.6303 + } 1.6304 + } 1.6305 + 1.6306 + return result; 1.6307 +} 1.6308 + 1.6309 +gfxFloat 1.6310 +gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd, 1.6311 + PropertyProvider *aProvider) 1.6312 +{ 1.6313 + if (aPartStart >= aPartEnd) 1.6314 + return 0; 1.6315 + LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider); 1.6316 + return data.mPartWidth; 1.6317 +} 1.6318 + 1.6319 +int32_t 1.6320 +gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd) 1.6321 +{ 1.6322 + const CompressedGlyph *glyphData = mCharacterGlyphs + aStart; 1.6323 + int32_t advance = 0; 1.6324 + uint32_t i; 1.6325 + for (i = aStart; i < aEnd; ++i, ++glyphData) { 1.6326 + if (glyphData->IsSimpleGlyph()) { 1.6327 + advance += glyphData->GetSimpleAdvance(); 1.6328 + } else { 1.6329 + uint32_t glyphCount = glyphData->GetGlyphCount(); 1.6330 + if (glyphCount == 0) { 1.6331 + continue; 1.6332 + } 1.6333 + const DetailedGlyph *details = GetDetailedGlyphs(i); 1.6334 + if (details) { 1.6335 + uint32_t j; 1.6336 + for (j = 0; j < glyphCount; ++j, ++details) { 1.6337 + advance += details->mAdvance; 1.6338 + } 1.6339 + } 1.6340 + } 1.6341 + } 1.6342 + return advance; 1.6343 +} 1.6344 + 1.6345 +static void 1.6346 +GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd, 1.6347 + gfxTextRun::PropertyProvider *aProvider, 1.6348 + gfxTextRun::PropertyProvider::Spacing *aSpacing) 1.6349 +{ 1.6350 + if (aStart >= aEnd) 1.6351 + return; 1.6352 + 1.6353 + aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing); 1.6354 + 1.6355 +#ifdef DEBUG 1.6356 + // Check to see if we have spacing inside ligatures 1.6357 + 1.6358 + const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs(); 1.6359 + uint32_t i; 1.6360 + 1.6361 + for (i = aStart; i < aEnd; ++i) { 1.6362 + if (!charGlyphs[i].IsLigatureGroupStart()) { 1.6363 + NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0, 1.6364 + "Before-spacing inside a ligature!"); 1.6365 + NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0, 1.6366 + "After-spacing inside a ligature!"); 1.6367 + } 1.6368 + } 1.6369 +#endif 1.6370 +} 1.6371 + 1.6372 +bool 1.6373 +gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd, 1.6374 + PropertyProvider *aProvider, 1.6375 + uint32_t aSpacingStart, uint32_t aSpacingEnd, 1.6376 + nsTArray<PropertyProvider::Spacing> *aSpacing) 1.6377 +{ 1.6378 + if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) 1.6379 + return false; 1.6380 + if (!aSpacing->AppendElements(aEnd - aStart)) 1.6381 + return false; 1.6382 + memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart)); 1.6383 + GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider, 1.6384 + aSpacing->Elements() + aSpacingStart - aStart); 1.6385 + memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd)); 1.6386 + return true; 1.6387 +} 1.6388 + 1.6389 +void 1.6390 +gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd) 1.6391 +{ 1.6392 + if (*aStart >= *aEnd) 1.6393 + return; 1.6394 + 1.6395 + CompressedGlyph *charGlyphs = mCharacterGlyphs; 1.6396 + 1.6397 + while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) { 1.6398 + ++(*aStart); 1.6399 + } 1.6400 + if (*aEnd < GetLength()) { 1.6401 + while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) { 1.6402 + --(*aEnd); 1.6403 + } 1.6404 + } 1.6405 +} 1.6406 + 1.6407 +void 1.6408 +gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext, 1.6409 + DrawMode aDrawMode, gfxPoint *aPt, 1.6410 + gfxTextContextPaint *aContextPaint, 1.6411 + uint32_t aStart, uint32_t aEnd, 1.6412 + PropertyProvider *aProvider, 1.6413 + uint32_t aSpacingStart, uint32_t aSpacingEnd, 1.6414 + gfxTextRunDrawCallbacks *aCallbacks) 1.6415 +{ 1.6416 + nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer; 1.6417 + bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider, 1.6418 + aSpacingStart, aSpacingEnd, &spacingBuffer); 1.6419 + aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt, 1.6420 + haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint, 1.6421 + aCallbacks); 1.6422 +} 1.6423 + 1.6424 +static void 1.6425 +ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight, 1.6426 + gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature) 1.6427 +{ 1.6428 + if (aLigature->mClipBeforePart) { 1.6429 + if (aTextRun->IsRightToLeft()) { 1.6430 + *aRight = std::min(*aRight, aXOrigin); 1.6431 + } else { 1.6432 + *aLeft = std::max(*aLeft, aXOrigin); 1.6433 + } 1.6434 + } 1.6435 + if (aLigature->mClipAfterPart) { 1.6436 + gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth; 1.6437 + if (aTextRun->IsRightToLeft()) { 1.6438 + *aLeft = std::max(*aLeft, endEdge); 1.6439 + } else { 1.6440 + *aRight = std::min(*aRight, endEdge); 1.6441 + } 1.6442 + } 1.6443 +} 1.6444 + 1.6445 +void 1.6446 +gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx, 1.6447 + uint32_t aStart, uint32_t aEnd, 1.6448 + gfxPoint *aPt, 1.6449 + PropertyProvider *aProvider, 1.6450 + gfxTextRunDrawCallbacks *aCallbacks) 1.6451 +{ 1.6452 + if (aStart >= aEnd) 1.6453 + return; 1.6454 + 1.6455 + // Draw partial ligature. We hack this by clipping the ligature. 1.6456 + LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider); 1.6457 + gfxRect clipExtents = aCtx->GetClipExtents(); 1.6458 + gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit; 1.6459 + gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit; 1.6460 + ClipPartialLigature(this, &left, &right, aPt->x, &data); 1.6461 + 1.6462 + { 1.6463 + // Need to preserve the path, otherwise this can break canvas text-on-path; 1.6464 + // in general it seems like a good thing, as naive callers probably won't 1.6465 + // expect gfxTextRun::Draw to implicitly destroy the current path. 1.6466 + gfxContextPathAutoSaveRestore savePath(aCtx); 1.6467 + 1.6468 + // use division here to ensure that when the rect is aligned on multiples 1.6469 + // of mAppUnitsPerDevUnit, we clip to true device unit boundaries. 1.6470 + // Also, make sure we snap the rectangle to device pixels. 1.6471 + aCtx->Save(); 1.6472 + aCtx->NewPath(); 1.6473 + aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit, 1.6474 + clipExtents.Y(), 1.6475 + (right - left) / mAppUnitsPerDevUnit, 1.6476 + clipExtents.Height()), true); 1.6477 + aCtx->Clip(); 1.6478 + } 1.6479 + 1.6480 + gfxFloat direction = GetDirection(); 1.6481 + gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y); 1.6482 + DrawGlyphs(aFont, aCtx, 1.6483 + aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt, 1.6484 + nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider, 1.6485 + aStart, aEnd, aCallbacks); 1.6486 + aCtx->Restore(); 1.6487 + 1.6488 + aPt->x += direction*data.mPartWidth; 1.6489 +} 1.6490 + 1.6491 +// returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise 1.6492 +static bool 1.6493 +HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength) 1.6494 +{ 1.6495 + gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength); 1.6496 + while (iter.NextRun()) { 1.6497 + gfxFont *font = iter.GetGlyphRun()->mFont; 1.6498 + if (font && font->IsSyntheticBold()) { 1.6499 + return true; 1.6500 + } 1.6501 + } 1.6502 + 1.6503 + return false; 1.6504 +} 1.6505 + 1.6506 +// returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise 1.6507 +// if true, color is set on output 1.6508 +static bool 1.6509 +HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor) 1.6510 +{ 1.6511 + if (aContext->GetDeviceColor(aCurrentColor)) { 1.6512 + if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) { 1.6513 + return true; 1.6514 + } 1.6515 + } 1.6516 + 1.6517 + return false; 1.6518 +} 1.6519 + 1.6520 +// helper class for double-buffering drawing with non-opaque color 1.6521 +struct BufferAlphaColor { 1.6522 + BufferAlphaColor(gfxContext *aContext) 1.6523 + : mContext(aContext) 1.6524 + { 1.6525 + 1.6526 + } 1.6527 + 1.6528 + ~BufferAlphaColor() {} 1.6529 + 1.6530 + void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit) 1.6531 + { 1.6532 + mContext->Save(); 1.6533 + mContext->NewPath(); 1.6534 + mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit, 1.6535 + aBounds.Y() / appsPerDevUnit, 1.6536 + aBounds.Width() / appsPerDevUnit, 1.6537 + aBounds.Height() / appsPerDevUnit), true); 1.6538 + mContext->Clip(); 1.6539 + mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b)); 1.6540 + mContext->PushGroup(gfxContentType::COLOR_ALPHA); 1.6541 + mAlpha = aAlphaColor.a; 1.6542 + } 1.6543 + 1.6544 + void PopAlpha() 1.6545 + { 1.6546 + // pop the text, using the color alpha as the opacity 1.6547 + mContext->PopGroupToSource(); 1.6548 + mContext->SetOperator(gfxContext::OPERATOR_OVER); 1.6549 + mContext->Paint(mAlpha); 1.6550 + mContext->Restore(); 1.6551 + } 1.6552 + 1.6553 + gfxContext *mContext; 1.6554 + gfxFloat mAlpha; 1.6555 +}; 1.6556 + 1.6557 +void 1.6558 +gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode, 1.6559 + uint32_t aStart, uint32_t aLength, 1.6560 + PropertyProvider *aProvider, gfxFloat *aAdvanceWidth, 1.6561 + gfxTextContextPaint *aContextPaint, 1.6562 + gfxTextRunDrawCallbacks *aCallbacks) 1.6563 +{ 1.6564 + NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); 1.6565 + NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)), 1.6566 + "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH"); 1.6567 + NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH"); 1.6568 + 1.6569 + bool skipDrawing = mSkipDrawing; 1.6570 + if (aDrawMode == DrawMode::GLYPH_FILL) { 1.6571 + gfxRGBA currentColor; 1.6572 + if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) { 1.6573 + skipDrawing = true; 1.6574 + } 1.6575 + } 1.6576 + 1.6577 + gfxFloat direction = GetDirection(); 1.6578 + 1.6579 + if (skipDrawing) { 1.6580 + // We don't need to draw anything; 1.6581 + // but if the caller wants advance width, we need to compute it here 1.6582 + if (aAdvanceWidth) { 1.6583 + gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, 1.6584 + gfxFont::LOOSE_INK_EXTENTS, 1.6585 + aContext, aProvider); 1.6586 + *aAdvanceWidth = metrics.mAdvanceWidth * direction; 1.6587 + } 1.6588 + 1.6589 + // return without drawing 1.6590 + return; 1.6591 + } 1.6592 + 1.6593 + gfxPoint pt = aPt; 1.6594 + 1.6595 + // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha 1.6596 + BufferAlphaColor syntheticBoldBuffer(aContext); 1.6597 + gfxRGBA currentColor; 1.6598 + bool needToRestore = false; 1.6599 + 1.6600 + if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor) 1.6601 + && HasSyntheticBold(this, aStart, aLength)) { 1.6602 + needToRestore = true; 1.6603 + // measure text, use the bounding box 1.6604 + gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS, 1.6605 + aContext, aProvider); 1.6606 + metrics.mBoundingBox.MoveBy(aPt); 1.6607 + syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit()); 1.6608 + } 1.6609 + 1.6610 + GlyphRunIterator iter(this, aStart, aLength); 1.6611 + while (iter.NextRun()) { 1.6612 + gfxFont *font = iter.GetGlyphRun()->mFont; 1.6613 + uint32_t start = iter.GetStringStart(); 1.6614 + uint32_t end = iter.GetStringEnd(); 1.6615 + uint32_t ligatureRunStart = start; 1.6616 + uint32_t ligatureRunEnd = end; 1.6617 + ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); 1.6618 + 1.6619 + bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL || 1.6620 + (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks); 1.6621 + 1.6622 + if (drawPartial) { 1.6623 + DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt, 1.6624 + aProvider, aCallbacks); 1.6625 + } 1.6626 + 1.6627 + DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart, 1.6628 + ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd, 1.6629 + aCallbacks); 1.6630 + 1.6631 + if (drawPartial) { 1.6632 + DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt, 1.6633 + aProvider, aCallbacks); 1.6634 + } 1.6635 + } 1.6636 + 1.6637 + // composite result when synthetic bolding used 1.6638 + if (needToRestore) { 1.6639 + syntheticBoldBuffer.PopAlpha(); 1.6640 + } 1.6641 + 1.6642 + if (aAdvanceWidth) { 1.6643 + *aAdvanceWidth = (pt.x - aPt.x)*direction; 1.6644 + } 1.6645 +} 1.6646 + 1.6647 +void 1.6648 +gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, 1.6649 + uint32_t aStart, uint32_t aEnd, 1.6650 + gfxFont::BoundingBoxType aBoundingBoxType, 1.6651 + gfxContext *aRefContext, 1.6652 + PropertyProvider *aProvider, 1.6653 + uint32_t aSpacingStart, uint32_t aSpacingEnd, 1.6654 + Metrics *aMetrics) 1.6655 +{ 1.6656 + nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer; 1.6657 + bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider, 1.6658 + aSpacingStart, aSpacingEnd, &spacingBuffer); 1.6659 + Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext, 1.6660 + haveSpacing ? spacingBuffer.Elements() : nullptr); 1.6661 + aMetrics->CombineWith(metrics, IsRightToLeft()); 1.6662 +} 1.6663 + 1.6664 +void 1.6665 +gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, 1.6666 + uint32_t aStart, uint32_t aEnd, 1.6667 + gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext, 1.6668 + PropertyProvider *aProvider, Metrics *aMetrics) 1.6669 +{ 1.6670 + if (aStart >= aEnd) 1.6671 + return; 1.6672 + 1.6673 + // Measure partial ligature. We hack this by clipping the metrics in the 1.6674 + // same way we clip the drawing. 1.6675 + LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider); 1.6676 + 1.6677 + // First measure the complete ligature 1.6678 + Metrics metrics; 1.6679 + AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd, 1.6680 + aBoundingBoxType, aRefContext, 1.6681 + aProvider, aStart, aEnd, &metrics); 1.6682 + 1.6683 + // Clip the bounding box to the ligature part 1.6684 + gfxFloat bboxLeft = metrics.mBoundingBox.X(); 1.6685 + gfxFloat bboxRight = metrics.mBoundingBox.XMost(); 1.6686 + // Where we are going to start "drawing" relative to our left baseline origin 1.6687 + gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0; 1.6688 + ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data); 1.6689 + metrics.mBoundingBox.x = bboxLeft; 1.6690 + metrics.mBoundingBox.width = bboxRight - bboxLeft; 1.6691 + 1.6692 + // mBoundingBox is now relative to the left baseline origin for the entire 1.6693 + // ligature. Shift it left. 1.6694 + metrics.mBoundingBox.x -= 1.6695 + IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth) 1.6696 + : data.mPartAdvance; 1.6697 + metrics.mAdvanceWidth = data.mPartWidth; 1.6698 + 1.6699 + aMetrics->CombineWith(metrics, IsRightToLeft()); 1.6700 +} 1.6701 + 1.6702 +gfxTextRun::Metrics 1.6703 +gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength, 1.6704 + gfxFont::BoundingBoxType aBoundingBoxType, 1.6705 + gfxContext *aRefContext, 1.6706 + PropertyProvider *aProvider) 1.6707 +{ 1.6708 + NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); 1.6709 + 1.6710 + Metrics accumulatedMetrics; 1.6711 + GlyphRunIterator iter(this, aStart, aLength); 1.6712 + while (iter.NextRun()) { 1.6713 + gfxFont *font = iter.GetGlyphRun()->mFont; 1.6714 + uint32_t start = iter.GetStringStart(); 1.6715 + uint32_t end = iter.GetStringEnd(); 1.6716 + uint32_t ligatureRunStart = start; 1.6717 + uint32_t ligatureRunEnd = end; 1.6718 + ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); 1.6719 + 1.6720 + AccumulatePartialLigatureMetrics(font, start, ligatureRunStart, 1.6721 + aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics); 1.6722 + 1.6723 + // XXX This sucks. We have to get glyph extents just so we can detect 1.6724 + // glyphs outside the font box, even when aBoundingBoxType is LOOSE, 1.6725 + // even though in almost all cases we could get correct results just 1.6726 + // by getting some ascent/descent from the font and using our stored 1.6727 + // advance widths. 1.6728 + AccumulateMetricsForRun(font, 1.6729 + ligatureRunStart, ligatureRunEnd, aBoundingBoxType, 1.6730 + aRefContext, aProvider, ligatureRunStart, ligatureRunEnd, 1.6731 + &accumulatedMetrics); 1.6732 + 1.6733 + AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end, 1.6734 + aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics); 1.6735 + } 1.6736 + 1.6737 + return accumulatedMetrics; 1.6738 +} 1.6739 + 1.6740 +#define MEASUREMENT_BUFFER_SIZE 100 1.6741 + 1.6742 +uint32_t 1.6743 +gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength, 1.6744 + bool aLineBreakBefore, gfxFloat aWidth, 1.6745 + PropertyProvider *aProvider, 1.6746 + bool aSuppressInitialBreak, 1.6747 + gfxFloat *aTrimWhitespace, 1.6748 + Metrics *aMetrics, 1.6749 + gfxFont::BoundingBoxType aBoundingBoxType, 1.6750 + gfxContext *aRefContext, 1.6751 + bool *aUsedHyphenation, 1.6752 + uint32_t *aLastBreak, 1.6753 + bool aCanWordWrap, 1.6754 + gfxBreakPriority *aBreakPriority) 1.6755 +{ 1.6756 + aMaxLength = std::min(aMaxLength, GetLength() - aStart); 1.6757 + 1.6758 + NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range"); 1.6759 + 1.6760 + uint32_t bufferStart = aStart; 1.6761 + uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE); 1.6762 + PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE]; 1.6763 + bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0; 1.6764 + if (haveSpacing) { 1.6765 + GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider, 1.6766 + spacingBuffer); 1.6767 + } 1.6768 + bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE]; 1.6769 + bool haveHyphenation = aProvider && 1.6770 + (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO || 1.6771 + (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL && 1.6772 + (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0)); 1.6773 + if (haveHyphenation) { 1.6774 + aProvider->GetHyphenationBreaks(bufferStart, bufferLength, 1.6775 + hyphenBuffer); 1.6776 + } 1.6777 + 1.6778 + gfxFloat width = 0; 1.6779 + gfxFloat advance = 0; 1.6780 + // The number of space characters that can be trimmed 1.6781 + uint32_t trimmableChars = 0; 1.6782 + // The amount of space removed by ignoring trimmableChars 1.6783 + gfxFloat trimmableAdvance = 0; 1.6784 + int32_t lastBreak = -1; 1.6785 + int32_t lastBreakTrimmableChars = -1; 1.6786 + gfxFloat lastBreakTrimmableAdvance = -1; 1.6787 + bool aborted = false; 1.6788 + uint32_t end = aStart + aMaxLength; 1.6789 + bool lastBreakUsedHyphenation = false; 1.6790 + 1.6791 + uint32_t ligatureRunStart = aStart; 1.6792 + uint32_t ligatureRunEnd = end; 1.6793 + ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); 1.6794 + 1.6795 + uint32_t i; 1.6796 + for (i = aStart; i < end; ++i) { 1.6797 + if (i >= bufferStart + bufferLength) { 1.6798 + // Fetch more spacing and hyphenation data 1.6799 + bufferStart = i; 1.6800 + bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i; 1.6801 + if (haveSpacing) { 1.6802 + GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider, 1.6803 + spacingBuffer); 1.6804 + } 1.6805 + if (haveHyphenation) { 1.6806 + aProvider->GetHyphenationBreaks(bufferStart, bufferLength, 1.6807 + hyphenBuffer); 1.6808 + } 1.6809 + } 1.6810 + 1.6811 + // There can't be a word-wrap break opportunity at the beginning of the 1.6812 + // line: if the width is too small for even one character to fit, it 1.6813 + // could be the first and last break opportunity on the line, and that 1.6814 + // would trigger an infinite loop. 1.6815 + if (!aSuppressInitialBreak || i > aStart) { 1.6816 + bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1; 1.6817 + bool atHyphenationBreak = 1.6818 + !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart]; 1.6819 + bool atBreak = atNaturalBreak || atHyphenationBreak; 1.6820 + bool wordWrapping = 1.6821 + aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() && 1.6822 + *aBreakPriority <= gfxBreakPriority::eWordWrapBreak; 1.6823 + 1.6824 + if (atBreak || wordWrapping) { 1.6825 + gfxFloat hyphenatedAdvance = advance; 1.6826 + if (atHyphenationBreak) { 1.6827 + hyphenatedAdvance += aProvider->GetHyphenWidth(); 1.6828 + } 1.6829 + 1.6830 + if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) { 1.6831 + // We can break here. 1.6832 + lastBreak = i; 1.6833 + lastBreakTrimmableChars = trimmableChars; 1.6834 + lastBreakTrimmableAdvance = trimmableAdvance; 1.6835 + lastBreakUsedHyphenation = atHyphenationBreak; 1.6836 + *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak 1.6837 + : gfxBreakPriority::eWordWrapBreak; 1.6838 + } 1.6839 + 1.6840 + width += advance; 1.6841 + advance = 0; 1.6842 + if (width - trimmableAdvance > aWidth) { 1.6843 + // No more text fits. Abort 1.6844 + aborted = true; 1.6845 + break; 1.6846 + } 1.6847 + } 1.6848 + } 1.6849 + 1.6850 + gfxFloat charAdvance; 1.6851 + if (i >= ligatureRunStart && i < ligatureRunEnd) { 1.6852 + charAdvance = GetAdvanceForGlyphs(i, i + 1); 1.6853 + if (haveSpacing) { 1.6854 + PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart]; 1.6855 + charAdvance += space->mBefore + space->mAfter; 1.6856 + } 1.6857 + } else { 1.6858 + charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider); 1.6859 + } 1.6860 + 1.6861 + advance += charAdvance; 1.6862 + if (aTrimWhitespace) { 1.6863 + if (mCharacterGlyphs[i].CharIsSpace()) { 1.6864 + ++trimmableChars; 1.6865 + trimmableAdvance += charAdvance; 1.6866 + } else { 1.6867 + trimmableAdvance = 0; 1.6868 + trimmableChars = 0; 1.6869 + } 1.6870 + } 1.6871 + } 1.6872 + 1.6873 + if (!aborted) { 1.6874 + width += advance; 1.6875 + } 1.6876 + 1.6877 + // There are three possibilities: 1.6878 + // 1) all the text fit (width <= aWidth) 1.6879 + // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0) 1.6880 + // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0) 1.6881 + uint32_t charsFit; 1.6882 + bool usedHyphenation = false; 1.6883 + if (width - trimmableAdvance <= aWidth) { 1.6884 + charsFit = aMaxLength; 1.6885 + } else if (lastBreak >= 0) { 1.6886 + charsFit = lastBreak - aStart; 1.6887 + trimmableChars = lastBreakTrimmableChars; 1.6888 + trimmableAdvance = lastBreakTrimmableAdvance; 1.6889 + usedHyphenation = lastBreakUsedHyphenation; 1.6890 + } else { 1.6891 + charsFit = aMaxLength; 1.6892 + } 1.6893 + 1.6894 + if (aMetrics) { 1.6895 + *aMetrics = MeasureText(aStart, charsFit - trimmableChars, 1.6896 + aBoundingBoxType, aRefContext, aProvider); 1.6897 + } 1.6898 + if (aTrimWhitespace) { 1.6899 + *aTrimWhitespace = trimmableAdvance; 1.6900 + } 1.6901 + if (aUsedHyphenation) { 1.6902 + *aUsedHyphenation = usedHyphenation; 1.6903 + } 1.6904 + if (aLastBreak && charsFit == aMaxLength) { 1.6905 + if (lastBreak < 0) { 1.6906 + *aLastBreak = UINT32_MAX; 1.6907 + } else { 1.6908 + *aLastBreak = lastBreak - aStart; 1.6909 + } 1.6910 + } 1.6911 + 1.6912 + return charsFit; 1.6913 +} 1.6914 + 1.6915 +gfxFloat 1.6916 +gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength, 1.6917 + PropertyProvider *aProvider) 1.6918 +{ 1.6919 + NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range"); 1.6920 + 1.6921 + uint32_t ligatureRunStart = aStart; 1.6922 + uint32_t ligatureRunEnd = aStart + aLength; 1.6923 + ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd); 1.6924 + 1.6925 + gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) + 1.6926 + ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider); 1.6927 + 1.6928 + // Account for all remaining spacing here. This is more efficient than 1.6929 + // processing it along with the glyphs. 1.6930 + if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) { 1.6931 + uint32_t i; 1.6932 + nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer; 1.6933 + if (spacingBuffer.AppendElements(aLength)) { 1.6934 + GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider, 1.6935 + spacingBuffer.Elements()); 1.6936 + for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) { 1.6937 + PropertyProvider::Spacing *space = &spacingBuffer[i]; 1.6938 + result += space->mBefore + space->mAfter; 1.6939 + } 1.6940 + } 1.6941 + } 1.6942 + 1.6943 + return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd); 1.6944 +} 1.6945 + 1.6946 +bool 1.6947 +gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength, 1.6948 + bool aLineBreakBefore, bool aLineBreakAfter, 1.6949 + gfxFloat *aAdvanceWidthDelta, 1.6950 + gfxContext *aRefContext) 1.6951 +{ 1.6952 + // Do nothing because our shaping does not currently take linebreaks into 1.6953 + // account. There is no change in advance width. 1.6954 + if (aAdvanceWidthDelta) { 1.6955 + *aAdvanceWidthDelta = 0; 1.6956 + } 1.6957 + return false; 1.6958 +} 1.6959 + 1.6960 +uint32_t 1.6961 +gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) 1.6962 +{ 1.6963 + NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun"); 1.6964 + NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0, 1.6965 + "non-empty text but no glyph runs present!"); 1.6966 + if (aOffset == GetLength()) 1.6967 + return mGlyphRuns.Length(); 1.6968 + uint32_t start = 0; 1.6969 + uint32_t end = mGlyphRuns.Length(); 1.6970 + while (end - start > 1) { 1.6971 + uint32_t mid = (start + end)/2; 1.6972 + if (mGlyphRuns[mid].mCharacterOffset <= aOffset) { 1.6973 + start = mid; 1.6974 + } else { 1.6975 + end = mid; 1.6976 + } 1.6977 + } 1.6978 + NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset, 1.6979 + "Hmm, something went wrong, aOffset should have been found"); 1.6980 + return start; 1.6981 +} 1.6982 + 1.6983 +nsresult 1.6984 +gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType, 1.6985 + uint32_t aUTF16Offset, bool aForceNewRun) 1.6986 +{ 1.6987 + NS_ASSERTION(aFont, "adding glyph run for null font!"); 1.6988 + if (!aFont) { 1.6989 + return NS_OK; 1.6990 + } 1.6991 + uint32_t numGlyphRuns = mGlyphRuns.Length(); 1.6992 + if (!aForceNewRun && numGlyphRuns > 0) { 1.6993 + GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1]; 1.6994 + 1.6995 + NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset, 1.6996 + "Glyph runs out of order (and run not forced)"); 1.6997 + 1.6998 + // Don't append a run if the font is already the one we want 1.6999 + if (lastGlyphRun->mFont == aFont && 1.7000 + lastGlyphRun->mMatchType == aMatchType) 1.7001 + { 1.7002 + return NS_OK; 1.7003 + } 1.7004 + 1.7005 + // If the offset has not changed, avoid leaving a zero-length run 1.7006 + // by overwriting the last entry instead of appending... 1.7007 + if (lastGlyphRun->mCharacterOffset == aUTF16Offset) { 1.7008 + 1.7009 + // ...except that if the run before the last entry had the same 1.7010 + // font as the new one wants, merge with it instead of creating 1.7011 + // adjacent runs with the same font 1.7012 + if (numGlyphRuns > 1 && 1.7013 + mGlyphRuns[numGlyphRuns - 2].mFont == aFont && 1.7014 + mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType) 1.7015 + { 1.7016 + mGlyphRuns.TruncateLength(numGlyphRuns - 1); 1.7017 + return NS_OK; 1.7018 + } 1.7019 + 1.7020 + lastGlyphRun->mFont = aFont; 1.7021 + lastGlyphRun->mMatchType = aMatchType; 1.7022 + return NS_OK; 1.7023 + } 1.7024 + } 1.7025 + 1.7026 + NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0, 1.7027 + "First run doesn't cover the first character (and run not forced)?"); 1.7028 + 1.7029 + GlyphRun *glyphRun = mGlyphRuns.AppendElement(); 1.7030 + if (!glyphRun) 1.7031 + return NS_ERROR_OUT_OF_MEMORY; 1.7032 + glyphRun->mFont = aFont; 1.7033 + glyphRun->mCharacterOffset = aUTF16Offset; 1.7034 + glyphRun->mMatchType = aMatchType; 1.7035 + return NS_OK; 1.7036 +} 1.7037 + 1.7038 +void 1.7039 +gfxTextRun::SortGlyphRuns() 1.7040 +{ 1.7041 + if (mGlyphRuns.Length() <= 1) 1.7042 + return; 1.7043 + 1.7044 + nsTArray<GlyphRun> runs(mGlyphRuns); 1.7045 + GlyphRunOffsetComparator comp; 1.7046 + runs.Sort(comp); 1.7047 + 1.7048 + // Now copy back, coalescing adjacent glyph runs that have the same font 1.7049 + mGlyphRuns.Clear(); 1.7050 + uint32_t i, count = runs.Length(); 1.7051 + for (i = 0; i < count; ++i) { 1.7052 + // a GlyphRun with the same font as the previous GlyphRun can just 1.7053 + // be skipped; the last GlyphRun will cover its character range. 1.7054 + if (i == 0 || runs[i].mFont != runs[i - 1].mFont) { 1.7055 + mGlyphRuns.AppendElement(runs[i]); 1.7056 + // If two fonts have the same character offset, Sort() will have 1.7057 + // randomized the order. 1.7058 + NS_ASSERTION(i == 0 || 1.7059 + runs[i].mCharacterOffset != 1.7060 + runs[i - 1].mCharacterOffset, 1.7061 + "Two fonts for the same run, glyph indices may not match the font"); 1.7062 + } 1.7063 + } 1.7064 +} 1.7065 + 1.7066 +// Note that SanitizeGlyphRuns scans all glyph runs in the textrun; 1.7067 +// therefore we only call it once, at the end of textrun construction, 1.7068 +// NOT incrementally as each glyph run is added (bug 680402). 1.7069 +void 1.7070 +gfxTextRun::SanitizeGlyphRuns() 1.7071 +{ 1.7072 + if (mGlyphRuns.Length() <= 1) 1.7073 + return; 1.7074 + 1.7075 + // If any glyph run starts with ligature-continuation characters, we need to advance it 1.7076 + // to the first "real" character to avoid drawing partial ligature glyphs from wrong font 1.7077 + // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes 1.7078 + // it appear as if a ligature has been formed) 1.7079 + int32_t i, lastRunIndex = mGlyphRuns.Length() - 1; 1.7080 + const CompressedGlyph *charGlyphs = mCharacterGlyphs; 1.7081 + for (i = lastRunIndex; i >= 0; --i) { 1.7082 + GlyphRun& run = mGlyphRuns[i]; 1.7083 + while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() && 1.7084 + run.mCharacterOffset < GetLength()) { 1.7085 + run.mCharacterOffset++; 1.7086 + } 1.7087 + // if the run has become empty, eliminate it 1.7088 + if ((i < lastRunIndex && 1.7089 + run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) || 1.7090 + (i == lastRunIndex && run.mCharacterOffset == GetLength())) { 1.7091 + mGlyphRuns.RemoveElementAt(i); 1.7092 + --lastRunIndex; 1.7093 + } 1.7094 + } 1.7095 +} 1.7096 + 1.7097 +uint32_t 1.7098 +gfxTextRun::CountMissingGlyphs() 1.7099 +{ 1.7100 + uint32_t i; 1.7101 + uint32_t count = 0; 1.7102 + for (i = 0; i < GetLength(); ++i) { 1.7103 + if (mCharacterGlyphs[i].IsMissing()) { 1.7104 + ++count; 1.7105 + } 1.7106 + } 1.7107 + return count; 1.7108 +} 1.7109 + 1.7110 +gfxTextRun::DetailedGlyph * 1.7111 +gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount) 1.7112 +{ 1.7113 + NS_ASSERTION(aIndex < GetLength(), "Index out of range"); 1.7114 + 1.7115 + if (!mDetailedGlyphs) { 1.7116 + mDetailedGlyphs = new DetailedGlyphStore(); 1.7117 + } 1.7118 + 1.7119 + DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount); 1.7120 + if (!details) { 1.7121 + mCharacterGlyphs[aIndex].SetMissing(0); 1.7122 + return nullptr; 1.7123 + } 1.7124 + 1.7125 + return details; 1.7126 +} 1.7127 + 1.7128 +void 1.7129 +gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset) 1.7130 +{ 1.7131 + uint32_t wordLen = aShapedWord->GetLength(); 1.7132 + NS_ASSERTION(aOffset + wordLen <= GetLength(), 1.7133 + "word overruns end of textrun!"); 1.7134 + 1.7135 + CompressedGlyph *charGlyphs = GetCharacterGlyphs(); 1.7136 + const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs(); 1.7137 + if (aShapedWord->HasDetailedGlyphs()) { 1.7138 + for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) { 1.7139 + const CompressedGlyph& g = wordGlyphs[i]; 1.7140 + if (g.IsSimpleGlyph()) { 1.7141 + charGlyphs[aOffset] = g; 1.7142 + } else { 1.7143 + const DetailedGlyph *details = 1.7144 + g.GetGlyphCount() > 0 ? 1.7145 + aShapedWord->GetDetailedGlyphs(i) : nullptr; 1.7146 + SetGlyphs(aOffset, g, details); 1.7147 + } 1.7148 + } 1.7149 + } else { 1.7150 + memcpy(charGlyphs + aOffset, wordGlyphs, 1.7151 + wordLen * sizeof(CompressedGlyph)); 1.7152 + } 1.7153 +} 1.7154 + 1.7155 +void 1.7156 +gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart, 1.7157 + uint32_t aLength, uint32_t aDest) 1.7158 +{ 1.7159 + NS_ASSERTION(aStart + aLength <= aSource->GetLength(), 1.7160 + "Source substring out of range"); 1.7161 + NS_ASSERTION(aDest + aLength <= GetLength(), 1.7162 + "Destination substring out of range"); 1.7163 + 1.7164 + if (aSource->mSkipDrawing) { 1.7165 + mSkipDrawing = true; 1.7166 + } 1.7167 + 1.7168 + // Copy base glyph data, and DetailedGlyph data where present 1.7169 + const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart; 1.7170 + CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest; 1.7171 + for (uint32_t i = 0; i < aLength; ++i) { 1.7172 + CompressedGlyph g = srcGlyphs[i]; 1.7173 + g.SetCanBreakBefore(!g.IsClusterStart() ? 1.7174 + CompressedGlyph::FLAG_BREAK_TYPE_NONE : 1.7175 + dstGlyphs[i].CanBreakBefore()); 1.7176 + if (!g.IsSimpleGlyph()) { 1.7177 + uint32_t count = g.GetGlyphCount(); 1.7178 + if (count > 0) { 1.7179 + DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count); 1.7180 + if (dst) { 1.7181 + DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart); 1.7182 + if (src) { 1.7183 + ::memcpy(dst, src, count * sizeof(DetailedGlyph)); 1.7184 + } else { 1.7185 + g.SetMissing(0); 1.7186 + } 1.7187 + } else { 1.7188 + g.SetMissing(0); 1.7189 + } 1.7190 + } 1.7191 + } 1.7192 + dstGlyphs[i] = g; 1.7193 + } 1.7194 + 1.7195 + // Copy glyph runs 1.7196 + GlyphRunIterator iter(aSource, aStart, aLength); 1.7197 +#ifdef DEBUG 1.7198 + gfxFont *lastFont = nullptr; 1.7199 +#endif 1.7200 + while (iter.NextRun()) { 1.7201 + gfxFont *font = iter.GetGlyphRun()->mFont; 1.7202 + NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?"); 1.7203 +#ifdef DEBUG 1.7204 + lastFont = font; 1.7205 + uint32_t end = iter.GetStringEnd(); 1.7206 +#endif 1.7207 + uint32_t start = iter.GetStringStart(); 1.7208 + 1.7209 + // These used to be NS_ASSERTION()s, but WARNING is more appropriate. 1.7210 + // Although it's unusual (and not desirable), it's possible for us to assign 1.7211 + // different fonts to a base character and a following diacritic. 1.7212 + // Example on OSX 10.5/10.6 with default fonts installed: 1.7213 + // data:text/html,<p style="font-family:helvetica, arial, sans-serif;"> 1.7214 + // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486; 1.7215 + // This means the rendering of the cluster will probably not be very good, 1.7216 + // but it's the best we can do for now if the specified font only covered the 1.7217 + // initial base character and not its applied marks. 1.7218 + NS_WARN_IF_FALSE(aSource->IsClusterStart(start), 1.7219 + "Started font run in the middle of a cluster"); 1.7220 + NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end), 1.7221 + "Ended font run in the middle of a cluster"); 1.7222 + 1.7223 + nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType, 1.7224 + start - aStart + aDest, false); 1.7225 + if (NS_FAILED(rv)) 1.7226 + return; 1.7227 + } 1.7228 +} 1.7229 + 1.7230 +void 1.7231 +gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext, 1.7232 + uint32_t aCharIndex) 1.7233 +{ 1.7234 + if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) { 1.7235 + return; 1.7236 + } 1.7237 + 1.7238 + aFont->InitWordCache(); 1.7239 + static const uint8_t space = ' '; 1.7240 + gfxShapedWord *sw = aFont->GetShapedWord(aContext, 1.7241 + &space, 1, 1.7242 + HashMix(0, ' '), 1.7243 + MOZ_SCRIPT_LATIN, 1.7244 + mAppUnitsPerDevUnit, 1.7245 + gfxTextRunFactory::TEXT_IS_8BIT | 1.7246 + gfxTextRunFactory::TEXT_IS_ASCII | 1.7247 + gfxTextRunFactory::TEXT_IS_PERSISTENT, 1.7248 + nullptr); 1.7249 + if (sw) { 1.7250 + AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false); 1.7251 + CopyGlyphDataFrom(sw, aCharIndex); 1.7252 + } 1.7253 +} 1.7254 + 1.7255 +bool 1.7256 +gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext, 1.7257 + uint32_t aCharIndex, char16_t aSpaceChar) 1.7258 +{ 1.7259 + uint32_t spaceGlyph = aFont->GetSpaceGlyph(); 1.7260 + if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) { 1.7261 + return false; 1.7262 + } 1.7263 + 1.7264 + uint32_t spaceWidthAppUnits = 1.7265 + NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit); 1.7266 + if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) { 1.7267 + return false; 1.7268 + } 1.7269 + 1.7270 + AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false); 1.7271 + CompressedGlyph g; 1.7272 + g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph); 1.7273 + if (aSpaceChar == ' ') { 1.7274 + g.SetIsSpace(); 1.7275 + } 1.7276 + GetCharacterGlyphs()[aCharIndex] = g; 1.7277 + return true; 1.7278 +} 1.7279 + 1.7280 +void 1.7281 +gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext) 1.7282 +{ 1.7283 + bool needsGlyphExtents = NeedsGlyphExtents(this); 1.7284 + if (!needsGlyphExtents && !mDetailedGlyphs) 1.7285 + return; 1.7286 + 1.7287 + uint32_t i, runCount = mGlyphRuns.Length(); 1.7288 + CompressedGlyph *charGlyphs = mCharacterGlyphs; 1.7289 + for (i = 0; i < runCount; ++i) { 1.7290 + const GlyphRun& run = mGlyphRuns[i]; 1.7291 + gfxFont *font = run.mFont; 1.7292 + uint32_t start = run.mCharacterOffset; 1.7293 + uint32_t end = i + 1 < runCount ? 1.7294 + mGlyphRuns[i + 1].mCharacterOffset : GetLength(); 1.7295 + bool fontIsSetup = false; 1.7296 + uint32_t j; 1.7297 + gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit); 1.7298 + 1.7299 + for (j = start; j < end; ++j) { 1.7300 + const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j]; 1.7301 + if (glyphData->IsSimpleGlyph()) { 1.7302 + // If we're in speed mode, don't set up glyph extents here; we'll 1.7303 + // just return "optimistic" glyph bounds later 1.7304 + if (needsGlyphExtents) { 1.7305 + uint32_t glyphIndex = glyphData->GetSimpleGlyph(); 1.7306 + if (!extents->IsGlyphKnown(glyphIndex)) { 1.7307 + if (!fontIsSetup) { 1.7308 + if (!font->SetupCairoFont(aRefContext)) { 1.7309 + NS_WARNING("failed to set up font for glyph extents"); 1.7310 + break; 1.7311 + } 1.7312 + fontIsSetup = true; 1.7313 + } 1.7314 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.7315 + ++gGlyphExtentsSetupEagerSimple; 1.7316 +#endif 1.7317 + font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents); 1.7318 + } 1.7319 + } 1.7320 + } else if (!glyphData->IsMissing()) { 1.7321 + uint32_t glyphCount = glyphData->GetGlyphCount(); 1.7322 + if (glyphCount == 0) { 1.7323 + continue; 1.7324 + } 1.7325 + const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j); 1.7326 + if (!details) { 1.7327 + continue; 1.7328 + } 1.7329 + for (uint32_t k = 0; k < glyphCount; ++k, ++details) { 1.7330 + uint32_t glyphIndex = details->mGlyphID; 1.7331 + if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) { 1.7332 + if (!fontIsSetup) { 1.7333 + if (!font->SetupCairoFont(aRefContext)) { 1.7334 + NS_WARNING("failed to set up font for glyph extents"); 1.7335 + break; 1.7336 + } 1.7337 + fontIsSetup = true; 1.7338 + } 1.7339 +#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS 1.7340 + ++gGlyphExtentsSetupEagerTight; 1.7341 +#endif 1.7342 + font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents); 1.7343 + } 1.7344 + } 1.7345 + } 1.7346 + } 1.7347 + } 1.7348 +} 1.7349 + 1.7350 + 1.7351 +gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun) 1.7352 + : mTextRun(aTextRun), mCurrentChar(uint32_t(-1)) 1.7353 +{ 1.7354 +} 1.7355 + 1.7356 +void 1.7357 +gfxTextRun::ClusterIterator::Reset() 1.7358 +{ 1.7359 + mCurrentChar = uint32_t(-1); 1.7360 +} 1.7361 + 1.7362 +bool 1.7363 +gfxTextRun::ClusterIterator::NextCluster() 1.7364 +{ 1.7365 + uint32_t len = mTextRun->GetLength(); 1.7366 + while (++mCurrentChar < len) { 1.7367 + if (mTextRun->IsClusterStart(mCurrentChar)) { 1.7368 + return true; 1.7369 + } 1.7370 + } 1.7371 + 1.7372 + mCurrentChar = uint32_t(-1); 1.7373 + return false; 1.7374 +} 1.7375 + 1.7376 +uint32_t 1.7377 +gfxTextRun::ClusterIterator::ClusterLength() const 1.7378 +{ 1.7379 + if (mCurrentChar == uint32_t(-1)) { 1.7380 + return 0; 1.7381 + } 1.7382 + 1.7383 + uint32_t i = mCurrentChar, 1.7384 + len = mTextRun->GetLength(); 1.7385 + while (++i < len) { 1.7386 + if (mTextRun->IsClusterStart(i)) { 1.7387 + break; 1.7388 + } 1.7389 + } 1.7390 + 1.7391 + return i - mCurrentChar; 1.7392 +} 1.7393 + 1.7394 +gfxFloat 1.7395 +gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const 1.7396 +{ 1.7397 + if (mCurrentChar == uint32_t(-1)) { 1.7398 + return 0; 1.7399 + } 1.7400 + 1.7401 + return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider); 1.7402 +} 1.7403 + 1.7404 +size_t 1.7405 +gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) 1.7406 +{ 1.7407 + // The second arg is how much gfxTextRun::AllocateStorage would have 1.7408 + // allocated. 1.7409 + size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf); 1.7410 + 1.7411 + if (mDetailedGlyphs) { 1.7412 + total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf); 1.7413 + } 1.7414 + 1.7415 + return total; 1.7416 +} 1.7417 + 1.7418 +size_t 1.7419 +gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) 1.7420 +{ 1.7421 + return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 1.7422 +} 1.7423 + 1.7424 + 1.7425 +#ifdef DEBUG 1.7426 +void 1.7427 +gfxTextRun::Dump(FILE* aOutput) { 1.7428 + if (!aOutput) { 1.7429 + aOutput = stdout; 1.7430 + } 1.7431 + 1.7432 + uint32_t i; 1.7433 + fputc('[', aOutput); 1.7434 + for (i = 0; i < mGlyphRuns.Length(); ++i) { 1.7435 + if (i > 0) { 1.7436 + fputc(',', aOutput); 1.7437 + } 1.7438 + gfxFont* font = mGlyphRuns[i].mFont; 1.7439 + const gfxFontStyle* style = font->GetStyle(); 1.7440 + NS_ConvertUTF16toUTF8 fontName(font->GetName()); 1.7441 + nsAutoCString lang; 1.7442 + style->language->ToUTF8String(lang); 1.7443 + fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset, 1.7444 + fontName.get(), style->size, 1.7445 + style->weight, style->style, lang.get()); 1.7446 + } 1.7447 + fputc(']', aOutput); 1.7448 +} 1.7449 +#endif