gfx/thebes/gfxFont.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 #include "mozilla/DebugOnly.h"
     7 #include "mozilla/MathAlgorithms.h"
     9 #ifdef MOZ_LOGGING
    10 #define FORCE_PR_LOG /* Allow logging in the release build */
    11 #endif
    12 #include "prlog.h"
    14 #include "nsServiceManagerUtils.h"
    15 #include "nsExpirationTracker.h"
    16 #include "nsILanguageAtomService.h"
    17 #include "nsITimer.h"
    19 #include "gfxFont.h"
    20 #include "gfxPlatform.h"
    21 #include "nsGkAtoms.h"
    23 #include "gfxTypes.h"
    24 #include "gfxContext.h"
    25 #include "gfxFontMissingGlyphs.h"
    26 #include "gfxHarfBuzzShaper.h"
    27 #include "gfxUserFontSet.h"
    28 #include "gfxPlatformFontList.h"
    29 #include "gfxScriptItemizer.h"
    30 #include "nsUnicodeProperties.h"
    31 #include "nsMathUtils.h"
    32 #include "nsBidiUtils.h"
    33 #include "nsUnicodeRange.h"
    34 #include "nsStyleConsts.h"
    35 #include "mozilla/FloatingPoint.h"
    36 #include "mozilla/Likely.h"
    37 #include "mozilla/MemoryReporting.h"
    38 #include "mozilla/Preferences.h"
    39 #include "mozilla/Services.h"
    40 #include "mozilla/Telemetry.h"
    41 #include "gfxSVGGlyphs.h"
    42 #include "gfxMathTable.h"
    43 #include "gfx2DGlue.h"
    45 #if defined(XP_MACOSX)
    46 #include "nsCocoaFeatures.h"
    47 #endif
    49 #include "cairo.h"
    50 #include "gfxFontTest.h"
    52 #include "harfbuzz/hb.h"
    53 #include "harfbuzz/hb-ot.h"
    54 #include "graphite2/Font.h"
    56 #include "nsCRT.h"
    57 #include "GeckoProfiler.h"
    58 #include "gfxFontConstants.h"
    60 #include <algorithm>
    62 using namespace mozilla;
    63 using namespace mozilla::gfx;
    64 using namespace mozilla::unicode;
    65 using mozilla::services::GetObserverService;
    67 gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
    69 static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
    70 static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
    72 #ifdef DEBUG_roc
    73 #define DEBUG_TEXT_RUN_STORAGE_METRICS
    74 #endif
    76 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
    77 static uint32_t gTextRunStorageHighWaterMark = 0;
    78 static uint32_t gTextRunStorage = 0;
    79 static uint32_t gFontCount = 0;
    80 static uint32_t gGlyphExtentsCount = 0;
    81 static uint32_t gGlyphExtentsWidthsTotalSize = 0;
    82 static uint32_t gGlyphExtentsSetupEagerSimple = 0;
    83 static uint32_t gGlyphExtentsSetupEagerTight = 0;
    84 static uint32_t gGlyphExtentsSetupLazyTight = 0;
    85 static uint32_t gGlyphExtentsSetupFallBackToTight = 0;
    86 #endif
    88 #ifdef PR_LOGGING
    89 #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
    90                                   PR_LOG_DEBUG, args)
    91 #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
    92                                         gfxPlatform::GetLog(eGfxLog_fontinit), \
    93                                         PR_LOG_DEBUG)
    94 #endif // PR_LOGGING
    96 void
    97 gfxCharacterMap::NotifyReleased()
    98 {
    99     gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
   100     if (mShared) {
   101         fontlist->RemoveCmap(this);
   102     }
   103     delete this;
   104 }
   106 gfxFontEntry::gfxFontEntry() :
   107     mItalic(false), mFixedPitch(false),
   108     mIsProxy(false), mIsValid(true),
   109     mIsBadUnderlineFont(false),
   110     mIsUserFont(false),
   111     mIsLocalUserFont(false),
   112     mStandardFace(false),
   113     mSymbolFont(false),
   114     mIgnoreGDEF(false),
   115     mIgnoreGSUB(false),
   116     mSVGInitialized(false),
   117     mMathInitialized(false),
   118     mHasSpaceFeaturesInitialized(false),
   119     mHasSpaceFeatures(false),
   120     mHasSpaceFeaturesKerning(false),
   121     mHasSpaceFeaturesNonKerning(false),
   122     mSkipDefaultFeatureSpaceCheck(false),
   123     mCheckedForGraphiteTables(false),
   124     mHasCmapTable(false),
   125     mGrFaceInitialized(false),
   126     mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
   127     mUVSOffset(0), mUVSData(nullptr),
   128     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
   129     mUnitsPerEm(0),
   130     mHBFace(nullptr),
   131     mGrFace(nullptr),
   132     mGrFaceRefCnt(0)
   133 {
   134     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
   135     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
   136 }
   138 gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
   139     mName(aName), mItalic(false), mFixedPitch(false),
   140     mIsProxy(false), mIsValid(true),
   141     mIsBadUnderlineFont(false), mIsUserFont(false),
   142     mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
   143     mSymbolFont(false),
   144     mIgnoreGDEF(false),
   145     mIgnoreGSUB(false),
   146     mSVGInitialized(false),
   147     mMathInitialized(false),
   148     mHasSpaceFeaturesInitialized(false),
   149     mHasSpaceFeatures(false),
   150     mHasSpaceFeaturesKerning(false),
   151     mHasSpaceFeaturesNonKerning(false),
   152     mSkipDefaultFeatureSpaceCheck(false),
   153     mCheckedForGraphiteTables(false),
   154     mHasCmapTable(false),
   155     mGrFaceInitialized(false),
   156     mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
   157     mUVSOffset(0), mUVSData(nullptr),
   158     mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
   159     mUnitsPerEm(0),
   160     mHBFace(nullptr),
   161     mGrFace(nullptr),
   162     mGrFaceRefCnt(0)
   163 {
   164     memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
   165     memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
   166 }
   168 gfxFontEntry::~gfxFontEntry()
   169 {
   170     // For downloaded fonts, we need to tell the user font cache that this
   171     // entry is being deleted.
   172     if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
   173         gfxUserFontSet::UserFontCache::ForgetFont(this);
   174     }
   176     // By the time the entry is destroyed, all font instances that were
   177     // using it should already have been deleted, and so the HB and/or Gr
   178     // face objects should have been released.
   179     MOZ_ASSERT(!mHBFace);
   180     MOZ_ASSERT(!mGrFaceInitialized);
   181 }
   183 bool gfxFontEntry::IsSymbolFont() 
   184 {
   185     return mSymbolFont;
   186 }
   188 bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
   189 {
   190     if (!mCharacterMap) {
   191         ReadCMAP();
   192         NS_ASSERTION(mCharacterMap, "failed to initialize character map");
   193     }
   194     return mCharacterMap->test(aCh);
   195 }
   197 nsresult gfxFontEntry::InitializeUVSMap()
   198 {
   199     // mUVSOffset will not be initialized
   200     // until cmap is initialized.
   201     if (!mCharacterMap) {
   202         ReadCMAP();
   203         NS_ASSERTION(mCharacterMap, "failed to initialize character map");
   204     }
   206     if (!mUVSOffset) {
   207         return NS_ERROR_FAILURE;
   208     }
   210     if (!mUVSData) {
   211         const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
   212         AutoTable cmapTable(this, kCmapTag);
   213         if (!cmapTable) {
   214             mUVSOffset = 0; // don't bother to read the table again
   215             return NS_ERROR_FAILURE;
   216         }
   218         uint8_t* uvsData;
   219         unsigned int cmapLen;
   220         const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
   221         nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
   222                           (const uint8_t*)cmapData + mUVSOffset,
   223                           cmapLen - mUVSOffset, uvsData);
   225         if (NS_FAILED(rv)) {
   226             mUVSOffset = 0; // don't bother to read the table again
   227             return rv;
   228         }
   230         mUVSData = uvsData;
   231     }
   233     return NS_OK;
   234 }
   236 uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
   237 {
   238     InitializeUVSMap();
   240     if (mUVSData) {
   241         return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
   242     }
   244     return 0;
   245 }
   247 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
   248 {
   249     hb_face_t *face = GetHBFace();
   250     if (!face) {
   251         return false;
   252     }
   254     unsigned int index;
   255     hb_tag_t     chosenScript;
   256     bool found =
   257         hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
   258                                          aScriptTags, &index, &chosenScript);
   259     hb_face_destroy(face);
   261     return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
   262 }
   264 nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
   265 {
   266     NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
   267     mCharacterMap = new gfxCharacterMap();
   268     return NS_OK;
   269 }
   271 nsString
   272 gfxFontEntry::RealFaceName()
   273 {
   274     AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
   275     if (nameTable) {
   276         nsAutoString name;
   277         nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
   278         if (NS_SUCCEEDED(rv)) {
   279             return name;
   280         }
   281     }
   282     return Name();
   283 }
   285 already_AddRefed<gfxFont>
   286 gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
   287 {
   288     // the font entry name is the psname, not the family name
   289     nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
   291     if (!font) {
   292         gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
   293         if (!newFont)
   294             return nullptr;
   295         if (!newFont->Valid()) {
   296             delete newFont;
   297             return nullptr;
   298         }
   299         font = newFont;
   300         gfxFontCache::GetCache()->AddNew(font);
   301     }
   302     return font.forget();
   303 }
   305 uint16_t
   306 gfxFontEntry::UnitsPerEm()
   307 {
   308     if (!mUnitsPerEm) {
   309         AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
   310         if (headTable) {
   311             uint32_t len;
   312             const HeadTable* head =
   313                 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
   314                                                                     &len));
   315             if (len >= sizeof(HeadTable)) {
   316                 mUnitsPerEm = head->unitsPerEm;
   317             }
   318         }
   320         // if we didn't find a usable 'head' table, or if the value was
   321         // outside the valid range, record it as invalid
   322         if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
   323             mUnitsPerEm = kInvalidUPEM;
   324         }
   325     }
   326     return mUnitsPerEm;
   327 }
   329 bool
   330 gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
   331 {
   332     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
   333     return mSVGGlyphs->HasSVGGlyph(aGlyphId);
   334 }
   336 bool
   337 gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
   338                                  gfxRect *aResult)
   339 {
   340     NS_ABORT_IF_FALSE(mSVGInitialized,
   341                       "SVG data has not yet been loaded. TryGetSVGData() first.");
   342     NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
   343                       "font has invalid unitsPerEm");
   345     gfxContextAutoSaveRestore matrixRestore(aContext);
   346     cairo_matrix_t fontMatrix;
   347     cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
   349     gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
   350     svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
   352     return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
   353 }
   355 bool
   356 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
   357                              int aDrawMode, gfxTextContextPaint *aContextPaint)
   358 {
   359     NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
   360     return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
   361                                    aContextPaint);
   362 }
   364 bool
   365 gfxFontEntry::TryGetSVGData(gfxFont* aFont)
   366 {
   367     if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
   368         return false;
   369     }
   371     if (!mSVGInitialized) {
   372         mSVGInitialized = true;
   374         // If UnitsPerEm is not known/valid, we can't use SVG glyphs
   375         if (UnitsPerEm() == kInvalidUPEM) {
   376             return false;
   377         }
   379         // We don't use AutoTable here because we'll pass ownership of this
   380         // blob to the gfxSVGGlyphs, once we've confirmed the table exists
   381         hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
   382         if (!svgTable) {
   383             return false;
   384         }
   386         // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
   387         // with it.
   388         mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
   389     }
   391     if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
   392         mFontsUsingSVGGlyphs.AppendElement(aFont);
   393     }
   395     return !!mSVGGlyphs;
   396 }
   398 void
   399 gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
   400 {
   401     mFontsUsingSVGGlyphs.RemoveElement(aFont);
   402 }
   404 void
   405 gfxFontEntry::NotifyGlyphsChanged()
   406 {
   407     for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
   408         gfxFont* font = mFontsUsingSVGGlyphs[i];
   409         font->NotifyGlyphsChanged();
   410     }
   411 }
   413 bool
   414 gfxFontEntry::TryGetMathTable(gfxFont* aFont)
   415 {
   416     if (!mMathInitialized) {
   417         mMathInitialized = true;
   419         // If UnitsPerEm is not known/valid, we can't use MATH table
   420         if (UnitsPerEm() == kInvalidUPEM) {
   421             return false;
   422         }
   424         // We don't use AutoTable here because we'll pass ownership of this
   425         // blob to the gfxMathTable, once we've confirmed the table exists
   426         hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
   427         if (!mathTable) {
   428             return false;
   429         }
   431         // gfxMathTable will hb_blob_destroy() the table when it is finished
   432         // with it.
   433         mMathTable = new gfxMathTable(mathTable);
   434         if (!mMathTable->HasValidHeaders()) {
   435             mMathTable = nullptr;
   436             return false;
   437         }
   438     }
   440     return !!mMathTable;
   441 }
   443 gfxFloat
   444 gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
   445 {
   446     NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   447     gfxFloat value = mMathTable->GetMathConstant(aConstant);
   448     if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
   449         aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
   450         aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
   451         return value / 100.0;
   452     }
   453     return value / mUnitsPerEm;
   454 }
   456 bool
   457 gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
   458                                        gfxFloat* aItalicCorrection)
   459 {
   460     NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   461     int16_t italicCorrection;
   462     if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
   463         return false;
   464     }
   465     *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
   466     return true;
   467 }
   469 uint32_t
   470 gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
   471                                   uint16_t aSize)
   472 {
   473     NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   474     return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
   475 }
   477 bool
   478 gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
   479                                    uint32_t aGlyphs[4])
   480 {
   481     NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
   482     return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
   483 }
   485 /**
   486  * FontTableBlobData
   487  *
   488  * See FontTableHashEntry for the general strategy.
   489  */
   491 class gfxFontEntry::FontTableBlobData {
   492 public:
   493     // Adopts the content of aBuffer.
   494     FontTableBlobData(FallibleTArray<uint8_t>& aBuffer)
   495         : mHashtable(nullptr), mHashKey(0)
   496     {
   497         MOZ_COUNT_CTOR(FontTableBlobData);
   498         mTableData.SwapElements(aBuffer);
   499     }
   501     ~FontTableBlobData() {
   502         MOZ_COUNT_DTOR(FontTableBlobData);
   503         if (mHashtable && mHashKey) {
   504             mHashtable->RemoveEntry(mHashKey);
   505         }
   506     }
   508     // Useful for creating blobs
   509     const char *GetTable() const
   510     {
   511         return reinterpret_cast<const char*>(mTableData.Elements());
   512     }
   513     uint32_t GetTableLength() const { return mTableData.Length(); }
   515     // Tell this FontTableBlobData to remove the HashEntry when this is
   516     // destroyed.
   517     void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
   518                          uint32_t aHashKey)
   519     {
   520         mHashtable = aHashtable;
   521         mHashKey = aHashKey;
   522     }
   524     // Disconnect from the HashEntry (because the blob has already been
   525     // removed from the hashtable).
   526     void ForgetHashEntry()
   527     {
   528         mHashtable = nullptr;
   529         mHashKey = 0;
   530     }
   532     size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
   533         return mTableData.SizeOfExcludingThis(aMallocSizeOf);
   534     }
   535     size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
   536         return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
   537     }
   539 private:
   540     // The font table data block, owned (via adoption)
   541     FallibleTArray<uint8_t> mTableData;
   543     // The blob destroy function needs to know the owning hashtable
   544     // and the hashtable key, so that it can remove the entry.
   545     nsTHashtable<FontTableHashEntry> *mHashtable;
   546     uint32_t                          mHashKey;
   548     // not implemented
   549     FontTableBlobData(const FontTableBlobData&);
   550 };
   552 hb_blob_t *
   553 gfxFontEntry::FontTableHashEntry::
   554 ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
   555                      nsTHashtable<FontTableHashEntry> *aHashtable)
   556 {
   557     Clear();
   558     // adopts elements of aTable
   559     mSharedBlobData = new FontTableBlobData(aTable);
   560     mBlob = hb_blob_create(mSharedBlobData->GetTable(),
   561                            mSharedBlobData->GetTableLength(),
   562                            HB_MEMORY_MODE_READONLY,
   563                            mSharedBlobData, DeleteFontTableBlobData);
   564     if (!mSharedBlobData) {
   565         // The FontTableBlobData was destroyed during hb_blob_create().
   566         // The (empty) blob is still be held in the hashtable with a strong
   567         // reference.
   568         return hb_blob_reference(mBlob);
   569     }
   571     // Tell the FontTableBlobData to remove this hash entry when destroyed.
   572     // The hashtable does not keep a strong reference.
   573     mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
   574     return mBlob;
   575 }
   577 void
   578 gfxFontEntry::FontTableHashEntry::Clear()
   579 {
   580     // If the FontTableBlobData is managing the hash entry, then the blob is
   581     // not owned by this HashEntry; otherwise there is strong reference to the
   582     // blob that must be removed.
   583     if (mSharedBlobData) {
   584         mSharedBlobData->ForgetHashEntry();
   585         mSharedBlobData = nullptr;
   586     } else if (mBlob) {
   587         hb_blob_destroy(mBlob);
   588     }
   589     mBlob = nullptr;
   590 }
   592 // a hb_destroy_func for hb_blob_create
   594 /* static */ void
   595 gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
   596 {
   597     delete static_cast<FontTableBlobData*>(aBlobData);
   598 }
   600 hb_blob_t *
   601 gfxFontEntry::FontTableHashEntry::GetBlob() const
   602 {
   603     return hb_blob_reference(mBlob);
   604 }
   606 bool
   607 gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
   608 {
   609     if (!mFontTableCache) {
   610         // we do this here rather than on fontEntry construction
   611         // because not all shapers will access the table cache at all
   612         mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
   613     }
   615     FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
   616     if (!entry) {
   617         return false;
   618     }
   620     *aBlob = entry->GetBlob();
   621     return true;
   622 }
   624 hb_blob_t *
   625 gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
   626                                        FallibleTArray<uint8_t>* aBuffer)
   627 {
   628     if (MOZ_UNLIKELY(!mFontTableCache)) {
   629         // we do this here rather than on fontEntry construction
   630         // because not all shapers will access the table cache at all
   631       mFontTableCache = new nsTHashtable<FontTableHashEntry>(10);
   632     }
   634     FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
   635     if (MOZ_UNLIKELY(!entry)) { // OOM
   636         return nullptr;
   637     }
   639     if (!aBuffer) {
   640         // ensure the entry is null
   641         entry->Clear();
   642         return nullptr;
   643     }
   645     return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache);
   646 }
   648 static int
   649 DirEntryCmp(const void* aKey, const void* aItem)
   650 {
   651     int32_t tag = *static_cast<const int32_t*>(aKey);
   652     const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
   653     return tag - int32_t(entry->tag);
   654 }
   656 hb_blob_t*
   657 gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
   658 {
   659     const SFNTHeader* header =
   660         reinterpret_cast<const SFNTHeader*>(aFontData);
   661     const TableDirEntry* dir =
   662         reinterpret_cast<const TableDirEntry*>(header + 1);
   663     dir = static_cast<const TableDirEntry*>
   664         (bsearch(&aTableTag, dir, uint16_t(header->numTables),
   665                  sizeof(TableDirEntry), DirEntryCmp));
   666     if (dir) {
   667         return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
   668                                   dir->offset, dir->length,
   669                               HB_MEMORY_MODE_READONLY, nullptr, nullptr);
   671     }
   672     return nullptr;
   673 }
   675 already_AddRefed<gfxCharacterMap>
   676 gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
   677                                   uint32_t& aUVSOffset,
   678                                   bool& aSymbolFont)
   679 {
   680     if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
   681         return nullptr;
   682     }
   684     return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
   685 }
   687 hb_blob_t *
   688 gfxFontEntry::GetFontTable(uint32_t aTag)
   689 {
   690     hb_blob_t *blob;
   691     if (GetExistingFontTable(aTag, &blob)) {
   692         return blob;
   693     }
   695     FallibleTArray<uint8_t> buffer;
   696     bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
   698     return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
   699 }
   701 // callback for HarfBuzz to get a font table (in hb_blob_t form)
   702 // from the font entry (passed as aUserData)
   703 /*static*/ hb_blob_t *
   704 gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
   705 {
   706     gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
   708     // bug 589682 - ignore the GDEF table in buggy fonts (applies to
   709     // Italic and BoldItalic faces of Times New Roman)
   710     if (aTag == TRUETYPE_TAG('G','D','E','F') &&
   711         fontEntry->IgnoreGDEF()) {
   712         return nullptr;
   713     }
   715     // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
   716     // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
   717     if (aTag == TRUETYPE_TAG('G','S','U','B') &&
   718         fontEntry->IgnoreGSUB()) {
   719         return nullptr;
   720     }
   722     return fontEntry->GetFontTable(aTag);
   723 }
   725 /*static*/ void
   726 gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
   727 {
   728     gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
   729     fe->ForgetHBFace();
   730 }
   732 void
   733 gfxFontEntry::ForgetHBFace()
   734 {
   735     mHBFace = nullptr;
   736 }
   738 hb_face_t*
   739 gfxFontEntry::GetHBFace()
   740 {
   741     if (!mHBFace) {
   742         mHBFace = hb_face_create_for_tables(HBGetTable, this,
   743                                             HBFaceDeletedCallback);
   744         return mHBFace;
   745     }
   746     return hb_face_reference(mHBFace);
   747 }
   749 /*static*/ const void*
   750 gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
   751                          size_t *aLen)
   752 {
   753     gfxFontEntry *fontEntry =
   754         static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
   755     hb_blob_t *blob = fontEntry->GetFontTable(aName);
   756     if (blob) {
   757         unsigned int blobLength;
   758         const void *tableData = hb_blob_get_data(blob, &blobLength);
   759         fontEntry->mGrTableMap->Put(tableData, blob);
   760         *aLen = blobLength;
   761         return tableData;
   762     }
   763     *aLen = 0;
   764     return nullptr;
   765 }
   767 /*static*/ void
   768 gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
   769                              const void *aTableBuffer)
   770 {
   771     gfxFontEntry *fontEntry =
   772         static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
   773     void *data;
   774     if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
   775         fontEntry->mGrTableMap->Remove(aTableBuffer);
   776         hb_blob_destroy(static_cast<hb_blob_t*>(data));
   777     }
   778 }
   780 gr_face*
   781 gfxFontEntry::GetGrFace()
   782 {
   783     if (!mGrFaceInitialized) {
   784         gr_face_ops faceOps = {
   785             sizeof(gr_face_ops),
   786             GrGetTable,
   787             GrReleaseTable
   788         };
   789         mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
   790         mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
   791         mGrFaceInitialized = true;
   792     }
   793     ++mGrFaceRefCnt;
   794     return mGrFace;
   795 }
   797 void
   798 gfxFontEntry::ReleaseGrFace(gr_face *aFace)
   799 {
   800     MOZ_ASSERT(aFace == mGrFace); // sanity-check
   801     MOZ_ASSERT(mGrFaceRefCnt > 0);
   802     if (--mGrFaceRefCnt == 0) {
   803         gr_face_destroy(mGrFace);
   804         mGrFace = nullptr;
   805         mGrFaceInitialized = false;
   806         delete mGrTableMap;
   807         mGrTableMap = nullptr;
   808     }
   809 }
   811 void
   812 gfxFontEntry::DisconnectSVG()
   813 {
   814     if (mSVGInitialized && mSVGGlyphs) {
   815         mSVGGlyphs = nullptr;
   816         mSVGInitialized = false;
   817     }
   818 }
   820 bool
   821 gfxFontEntry::HasFontTable(uint32_t aTableTag)
   822 {
   823     AutoTable table(this, aTableTag);
   824     return table && hb_blob_get_length(table) > 0;
   825 }
   827 void
   828 gfxFontEntry::CheckForGraphiteTables()
   829 {
   830     mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
   831 }
   833 /* static */ size_t
   834 gfxFontEntry::FontTableHashEntry::SizeOfEntryExcludingThis
   835     (FontTableHashEntry *aEntry,
   836      MallocSizeOf aMallocSizeOf,
   837      void* aUserArg)
   838 {
   839     size_t n = 0;
   840     if (aEntry->mBlob) {
   841         n += aMallocSizeOf(aEntry->mBlob);
   842     }
   843     if (aEntry->mSharedBlobData) {
   844         n += aEntry->mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
   845     }
   846     return n;
   847 }
   849 void
   850 gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
   851                                      FontListSizes* aSizes) const
   852 {
   853     aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
   855     // cmaps are shared so only non-shared cmaps are included here
   856     if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
   857         aSizes->mCharMapsSize +=
   858             mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
   859     }
   860     if (mFontTableCache) {
   861         aSizes->mFontTableCacheSize +=
   862             mFontTableCache->SizeOfExcludingThis(
   863                 FontTableHashEntry::SizeOfEntryExcludingThis,
   864                 aMallocSizeOf);
   865     }
   866 }
   868 void
   869 gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
   870                                      FontListSizes* aSizes) const
   871 {
   872     aSizes->mFontListSize += aMallocSizeOf(this);
   873     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
   874 }
   876 //////////////////////////////////////////////////////////////////////////////
   877 //
   878 // class gfxFontFamily
   879 //
   880 //////////////////////////////////////////////////////////////////////////////
   882 // we consider faces with mStandardFace == true to be "greater than" those with false,
   883 // because during style matching, later entries will replace earlier ones
   884 class FontEntryStandardFaceComparator {
   885   public:
   886     bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
   887         return a->mStandardFace == b->mStandardFace;
   888     }
   889     bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
   890         return (a->mStandardFace == false && b->mStandardFace == true);
   891     }
   892 };
   894 void
   895 gfxFontFamily::SortAvailableFonts()
   896 {
   897     mAvailableFonts.Sort(FontEntryStandardFaceComparator());
   898 }
   900 bool
   901 gfxFontFamily::HasOtherFamilyNames()
   902 {
   903     // need to read in other family names to determine this
   904     if (!mOtherFamilyNamesInitialized) {
   905         ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList());  // sets mHasOtherFamilyNames
   906     }
   907     return mHasOtherFamilyNames;
   908 }
   910 gfxFontEntry*
   911 gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle, 
   912                                 bool& aNeedsSyntheticBold)
   913 {
   914     if (!mHasStyles)
   915         FindStyleVariations(); // collect faces for the family, if not already done
   917     NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
   919     aNeedsSyntheticBold = false;
   921     int8_t baseWeight = aFontStyle.ComputeWeight();
   922     bool wantBold = baseWeight >= 6;
   924     // If the family has only one face, we simply return it; no further checking needed
   925     if (mAvailableFonts.Length() == 1) {
   926         gfxFontEntry *fe = mAvailableFonts[0];
   927         aNeedsSyntheticBold = wantBold && !fe->IsBold();
   928         return fe;
   929     }
   931     bool wantItalic = (aFontStyle.style &
   932                        (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
   934     // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
   935     // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
   936     // stored in the above order; note that some of the entries may be nullptr.
   937     // We can then pick the required entry based on whether the request is for
   938     // bold or non-bold, italic or non-italic, without running the more complex
   939     // matching algorithm used for larger families with many weights and/or widths.
   941     if (mIsSimpleFamily) {
   942         // Family has no more than the "standard" 4 faces, at fixed indexes;
   943         // calculate which one we want.
   944         // Note that we cannot simply return it as not all 4 faces are necessarily present.
   945         uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
   946                             (wantBold ? kBoldMask : 0);
   948         // if the desired style is available, return it directly
   949         gfxFontEntry *fe = mAvailableFonts[faceIndex];
   950         if (fe) {
   951             // no need to set aNeedsSyntheticBold here as we matched the boldness request
   952             return fe;
   953         }
   955         // order to check fallback faces in a simple family, depending on requested style
   956         static const uint8_t simpleFallbacks[4][3] = {
   957             { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex },   // fallbacks for Regular
   958             { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
   959             { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex },  // Italic
   960             { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex }       // BoldItalic
   961         };
   962         const uint8_t *order = simpleFallbacks[faceIndex];
   964         for (uint8_t trial = 0; trial < 3; ++trial) {
   965             // check remaining faces in order of preference to find the first that actually exists
   966             fe = mAvailableFonts[order[trial]];
   967             if (fe) {
   968                 aNeedsSyntheticBold = wantBold && !fe->IsBold();
   969                 return fe;
   970             }
   971         }
   973         // this can't happen unless we have totally broken the font-list manager!
   974         NS_NOTREACHED("no face found in simple font family!");
   975         return nullptr;
   976     }
   978     // This is a large/rich font family, so we do full style- and weight-matching:
   979     // first collect a list of weights that are the best match for the requested
   980     // font-stretch and font-style, then pick the best weight match among those
   981     // available.
   983     gfxFontEntry *weightList[10] = { 0 };
   984     bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
   985     if (!foundWeights) {
   986         return nullptr;
   987     }
   989     // First find a match for the best weight
   990     int8_t matchBaseWeight = 0;
   991     int8_t i = baseWeight;
   993     // Need to special case when normal face doesn't exist but medium does.
   994     // In that case, use medium otherwise weights < 400
   995     if (baseWeight == 4 && !weightList[4]) {
   996         i = 5; // medium
   997     }
   999     // Loop through weights, since one exists loop will terminate
  1000     int8_t direction = (baseWeight > 5) ? 1 : -1;
  1001     for (; ; i += direction) {
  1002         if (weightList[i]) {
  1003             matchBaseWeight = i;
  1004             break;
  1007         // If we've reached one side without finding a font,
  1008         // start over and go the other direction until we find a match
  1009         if (i == 1 || i == 9) {
  1010             i = baseWeight;
  1011             direction = -direction;
  1015     NS_ASSERTION(matchBaseWeight != 0, 
  1016                  "weight mapping should always find at least one font in a family");
  1018     gfxFontEntry *matchFE = weightList[matchBaseWeight];
  1020     NS_ASSERTION(matchFE,
  1021                  "weight mapping should always find at least one font in a family");
  1023     if (!matchFE->IsBold() && baseWeight >= 6)
  1025         aNeedsSyntheticBold = true;
  1028     return matchFE;
  1031 void
  1032 gfxFontFamily::CheckForSimpleFamily()
  1034     // already checked this family
  1035     if (mIsSimpleFamily) {
  1036         return;
  1037     };
  1039     uint32_t count = mAvailableFonts.Length();
  1040     if (count > 4 || count == 0) {
  1041         return; // can't be "simple" if there are >4 faces;
  1042                 // if none then the family is unusable anyway
  1045     if (count == 1) {
  1046         mIsSimpleFamily = true;
  1047         return;
  1050     int16_t firstStretch = mAvailableFonts[0]->Stretch();
  1052     gfxFontEntry *faces[4] = { 0 };
  1053     for (uint8_t i = 0; i < count; ++i) {
  1054         gfxFontEntry *fe = mAvailableFonts[i];
  1055         if (fe->Stretch() != firstStretch) {
  1056             return; // font-stretch doesn't match, don't treat as simple family
  1058         uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
  1059                             (fe->Weight() >= 600 ? kBoldMask : 0);
  1060         if (faces[faceIndex]) {
  1061             return; // two faces resolve to the same slot; family isn't "simple"
  1063         faces[faceIndex] = fe;
  1066     // we have successfully slotted the available faces into the standard
  1067     // 4-face framework
  1068     mAvailableFonts.SetLength(4);
  1069     for (uint8_t i = 0; i < 4; ++i) {
  1070         if (mAvailableFonts[i].get() != faces[i]) {
  1071             mAvailableFonts[i].swap(faces[i]);
  1075     mIsSimpleFamily = true;
  1078 static inline uint32_t
  1079 StyleDistance(gfxFontEntry *aFontEntry,
  1080               bool anItalic, int16_t aStretch)
  1082     // Compute a measure of the "distance" between the requested style
  1083     // and the given fontEntry,
  1084     // considering italicness and font-stretch but not weight.
  1086     int32_t distance = 0;
  1087     if (aStretch != aFontEntry->mStretch) {
  1088         // stretch values are in the range -4 .. +4
  1089         // if aStretch is positive, we prefer more-positive values;
  1090         // if zero or negative, prefer more-negative
  1091         if (aStretch > 0) {
  1092             distance = (aFontEntry->mStretch - aStretch) * 2;
  1093         } else {
  1094             distance = (aStretch - aFontEntry->mStretch) * 2;
  1096         // if the computed "distance" here is negative, it means that
  1097         // aFontEntry lies in the "non-preferred" direction from aStretch,
  1098         // so we treat that as larger than any preferred-direction distance
  1099         // (max possible is 8) by adding an extra 10 to the absolute value
  1100         if (distance < 0) {
  1101             distance = -distance + 10;
  1104     if (aFontEntry->IsItalic() != anItalic) {
  1105         distance += 1;
  1107     return uint32_t(distance);
  1110 bool
  1111 gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
  1112                                    bool anItalic, int16_t aStretch)
  1114     uint32_t foundWeights = 0;
  1115     uint32_t bestMatchDistance = 0xffffffff;
  1117     uint32_t count = mAvailableFonts.Length();
  1118     for (uint32_t i = 0; i < count; i++) {
  1119         // this is not called for "simple" families, and therefore it does not
  1120         // need to check the mAvailableFonts entries for nullptr.
  1121         gfxFontEntry *fe = mAvailableFonts[i];
  1122         uint32_t distance = StyleDistance(fe, anItalic, aStretch);
  1123         if (distance <= bestMatchDistance) {
  1124             int8_t wt = fe->mWeight / 100;
  1125             NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
  1126             if (!aFontsForWeights[wt]) {
  1127                 // record this as a possible candidate for weight matching
  1128                 aFontsForWeights[wt] = fe;
  1129                 ++foundWeights;
  1130             } else {
  1131                 uint32_t prevDistance =
  1132                     StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
  1133                 if (prevDistance >= distance) {
  1134                     // replacing a weight we already found,
  1135                     // so don't increment foundWeights
  1136                     aFontsForWeights[wt] = fe;
  1139             bestMatchDistance = distance;
  1143     NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
  1145     if (foundWeights == 1) {
  1146         // no need to cull entries if we only found one weight
  1147         return true;
  1150     // we might have recorded some faces that were a partial style match, but later found
  1151     // others that were closer; in this case, we need to cull the poorer matches from the
  1152     // weight list we'll return
  1153     for (uint32_t i = 0; i < 10; ++i) {
  1154         if (aFontsForWeights[i] &&
  1155             StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
  1157             aFontsForWeights[i] = 0;
  1161     return (foundWeights > 0);
  1165 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
  1167     // just return the primary name; subclasses should override
  1168     aLocalizedName = mName;
  1171 // metric for how close a given font matches a style
  1172 static int32_t
  1173 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
  1175     int32_t rank = 0;
  1176     if (aStyle) {
  1177          // italics
  1178          bool wantItalic =
  1179              (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
  1180          if (aFontEntry->IsItalic() == wantItalic) {
  1181              rank += 10;
  1184         // measure of closeness of weight to the desired value
  1185         rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
  1186     } else {
  1187         // if no font to match, prefer non-bold, non-italic fonts
  1188         if (!aFontEntry->IsItalic()) {
  1189             rank += 3;
  1191         if (!aFontEntry->IsBold()) {
  1192             rank += 2;
  1196     return rank;
  1199 #define RANK_MATCHED_CMAP   20
  1201 void
  1202 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
  1204     if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
  1205         // none of the faces in the family support the required char,
  1206         // so bail out immediately
  1207         return;
  1210     bool needsBold;
  1211     gfxFontStyle normal;
  1212     gfxFontEntry *fe = FindFontForStyle(
  1213                   (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal,
  1214                   needsBold);
  1216     if (fe && !fe->SkipDuringSystemFallback()) {
  1217         int32_t rank = 0;
  1219         if (fe->TestCharacterMap(aMatchData->mCh)) {
  1220             rank += RANK_MATCHED_CMAP;
  1221             aMatchData->mCount++;
  1222 #ifdef PR_LOGGING
  1223             PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
  1225             if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) {
  1226                 uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
  1227                 uint32_t script = GetScriptCode(aMatchData->mCh);
  1228                 PR_LOG(log, PR_LOG_DEBUG,\
  1229                        ("(textrun-systemfallback-fonts) char: u+%6.6x "
  1230                         "unicode-range: %d script: %d match: [%s]\n",
  1231                         aMatchData->mCh,
  1232                         unicodeRange, script,
  1233                         NS_ConvertUTF16toUTF8(fe->Name()).get()));
  1235 #endif
  1238         aMatchData->mCmapsTested++;
  1239         if (rank == 0) {
  1240             return;
  1243          // omitting from original windows code -- family name, lang group, pitch
  1244          // not available in current FontEntry implementation
  1245         rank += CalcStyleMatch(fe, aMatchData->mStyle);
  1247         // xxx - add whether AAT font with morphing info for specific lang groups
  1249         if (rank > aMatchData->mMatchRank
  1250             || (rank == aMatchData->mMatchRank &&
  1251                 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
  1253             aMatchData->mBestMatch = fe;
  1254             aMatchData->mMatchedFamily = this;
  1255             aMatchData->mMatchRank = rank;
  1260 void
  1261 gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
  1263     uint32_t i, numFonts = mAvailableFonts.Length();
  1264     for (i = 0; i < numFonts; i++) {
  1265         gfxFontEntry *fe = mAvailableFonts[i];
  1266         if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
  1267             int32_t rank = RANK_MATCHED_CMAP;
  1268             rank += CalcStyleMatch(fe, aMatchData->mStyle);
  1269             if (rank > aMatchData->mMatchRank
  1270                 || (rank == aMatchData->mMatchRank &&
  1271                     Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
  1273                 aMatchData->mBestMatch = fe;
  1274                 aMatchData->mMatchedFamily = this;
  1275                 aMatchData->mMatchRank = rank;
  1281 /*static*/ void
  1282 gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
  1283                                            const char *aNameData,
  1284                                            uint32_t aDataLength,
  1285                                            nsTArray<nsString>& aOtherFamilyNames,
  1286                                            bool useFullName)
  1288     const gfxFontUtils::NameHeader *nameHeader =
  1289         reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
  1291     uint32_t nameCount = nameHeader->count;
  1292     if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
  1293         NS_WARNING("invalid font (name records)");
  1294         return;
  1297     const gfxFontUtils::NameRecord *nameRecord =
  1298         reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
  1299     uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
  1301     for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
  1302         uint32_t nameLen = nameRecord->length;
  1303         uint32_t nameOff = nameRecord->offset;  // offset from base of string storage
  1305         if (stringsBase + nameOff + nameLen > aDataLength) {
  1306             NS_WARNING("invalid font (name table strings)");
  1307             return;
  1310         uint16_t nameID = nameRecord->nameID;
  1311         if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
  1312             (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
  1313                               nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
  1314             nsAutoString otherFamilyName;
  1315             bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
  1316                                                      nameLen,
  1317                                                      uint32_t(nameRecord->platformID),
  1318                                                      uint32_t(nameRecord->encodingID),
  1319                                                      uint32_t(nameRecord->languageID),
  1320                                                      otherFamilyName);
  1321             // add if not same as canonical family name
  1322             if (ok && otherFamilyName != aFamilyName) {
  1323                 aOtherFamilyNames.AppendElement(otherFamilyName);
  1329 // returns true if other names were found, false otherwise
  1330 bool
  1331 gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
  1332                                            hb_blob_t           *aNameTable,
  1333                                            bool                 useFullName)
  1335     uint32_t dataLength;
  1336     const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
  1337     nsAutoTArray<nsString,4> otherFamilyNames;
  1339     ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
  1340                                 otherFamilyNames, useFullName);
  1342     uint32_t n = otherFamilyNames.Length();
  1343     for (uint32_t i = 0; i < n; i++) {
  1344         aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
  1347     return n != 0;
  1350 void
  1351 gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
  1353     if (mOtherFamilyNamesInitialized) 
  1354         return;
  1355     mOtherFamilyNamesInitialized = true;
  1357     FindStyleVariations();
  1359     // read in other family names for the first face in the list
  1360     uint32_t i, numFonts = mAvailableFonts.Length();
  1361     const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
  1363     for (i = 0; i < numFonts; ++i) {
  1364         gfxFontEntry *fe = mAvailableFonts[i];
  1365         if (!fe) {
  1366             continue;
  1368         gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1369         if (!nameTable) {
  1370             continue;
  1372         mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
  1373                                                            nameTable);
  1374         break;
  1377     // read in other names for the first face in the list with the assumption
  1378     // that if extra names don't exist in that face then they don't exist in
  1379     // other faces for the same font
  1380     if (!mHasOtherFamilyNames) 
  1381         return;
  1383     // read in names for all faces, needed to catch cases where fonts have
  1384     // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
  1385     for ( ; i < numFonts; i++) {
  1386         gfxFontEntry *fe = mAvailableFonts[i];
  1387         if (!fe) {
  1388             continue;
  1390         gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1391         if (!nameTable) {
  1392             continue;
  1394         ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
  1398 void
  1399 gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList, 
  1400                              bool aNeedFullnamePostscriptNames,
  1401                              FontInfoData *aFontInfoData)
  1403     // if all needed names have already been read, skip
  1404     if (mOtherFamilyNamesInitialized &&
  1405         (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
  1406         return;
  1408     bool asyncFontLoaderDisabled = false;
  1410 #if defined(XP_MACOSX)
  1411     // bug 975460 - async font loader crashes sometimes under 10.6, disable
  1412     if (!nsCocoaFeatures::OnLionOrLater()) {
  1413         asyncFontLoaderDisabled = true;
  1415 #endif
  1417     if (!mOtherFamilyNamesInitialized &&
  1418         aFontInfoData &&
  1419         aFontInfoData->mLoadOtherNames &&
  1420         !asyncFontLoaderDisabled)
  1422         nsAutoTArray<nsString,4> otherFamilyNames;
  1423         bool foundOtherNames =
  1424             aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
  1425         if (foundOtherNames) {
  1426             uint32_t i, n = otherFamilyNames.Length();
  1427             for (i = 0; i < n; i++) {
  1428                 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
  1431         mOtherFamilyNamesInitialized = true;
  1434     // if all needed data has been initialized, return
  1435     if (mOtherFamilyNamesInitialized &&
  1436         (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
  1437         return;
  1440     FindStyleVariations(aFontInfoData);
  1442     // check again, as style enumeration code may have loaded names
  1443     if (mOtherFamilyNamesInitialized &&
  1444         (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
  1445         return;
  1448     uint32_t i, numFonts = mAvailableFonts.Length();
  1449     const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
  1451     bool firstTime = true, readAllFaces = false;
  1452     for (i = 0; i < numFonts; ++i) {
  1453         gfxFontEntry *fe = mAvailableFonts[i];
  1454         if (!fe) {
  1455             continue;
  1458         nsAutoString fullname, psname;
  1459         bool foundFaceNames = false;
  1460         if (!mFaceNamesInitialized &&
  1461             aNeedFullnamePostscriptNames &&
  1462             aFontInfoData &&
  1463             aFontInfoData->mLoadFaceNames) {
  1464             aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
  1465             if (!fullname.IsEmpty()) {
  1466                 aPlatformFontList->AddFullname(fe, fullname);
  1468             if (!psname.IsEmpty()) {
  1469                 aPlatformFontList->AddPostscriptName(fe, psname);
  1471             foundFaceNames = true;
  1473             // found everything needed? skip to next font
  1474             if (mOtherFamilyNamesInitialized) {
  1475                 continue;
  1479         // load directly from the name table
  1480         gfxFontEntry::AutoTable nameTable(fe, kNAME);
  1481         if (!nameTable) {
  1482             continue;
  1485         if (aNeedFullnamePostscriptNames && !foundFaceNames) {
  1486             if (gfxFontUtils::ReadCanonicalName(
  1487                     nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
  1489                 aPlatformFontList->AddFullname(fe, fullname);
  1492             if (gfxFontUtils::ReadCanonicalName(
  1493                     nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
  1495                 aPlatformFontList->AddPostscriptName(fe, psname);
  1499         if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
  1500             bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
  1501                                                               nameTable);
  1503             // if the first face has a different name, scan all faces, otherwise
  1504             // assume the family doesn't have other names
  1505             if (firstTime && foundOtherName) {
  1506                 mHasOtherFamilyNames = true;
  1507                 readAllFaces = true;
  1509             firstTime = false;
  1512         // if not reading in any more names, skip other faces
  1513         if (!readAllFaces && !aNeedFullnamePostscriptNames) {
  1514             break;
  1518     mFaceNamesInitialized = true;
  1519     mOtherFamilyNamesInitialized = true;
  1523 gfxFontEntry*
  1524 gfxFontFamily::FindFont(const nsAString& aPostscriptName)
  1526     // find the font using a simple linear search
  1527     uint32_t numFonts = mAvailableFonts.Length();
  1528     for (uint32_t i = 0; i < numFonts; i++) {
  1529         gfxFontEntry *fe = mAvailableFonts[i].get();
  1530         if (fe && fe->Name() == aPostscriptName)
  1531             return fe;
  1533     return nullptr;
  1536 void
  1537 gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
  1539     FindStyleVariations(aFontInfoData);
  1541     uint32_t i, numFonts = mAvailableFonts.Length();
  1542     for (i = 0; i < numFonts; i++) {
  1543         gfxFontEntry *fe = mAvailableFonts[i];
  1544         // don't try to load cmaps for downloadable fonts not yet loaded
  1545         if (!fe || fe->mIsProxy) {
  1546             continue;
  1548         fe->ReadCMAP(aFontInfoData);
  1549         mFamilyCharacterMap.Union(*(fe->mCharacterMap));
  1551     mFamilyCharacterMap.Compact();
  1552     mFamilyCharacterMapInitialized = true;
  1555 void
  1556 gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  1557                                       FontListSizes* aSizes) const
  1559     aSizes->mFontListSize +=
  1560         mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
  1561     aSizes->mCharMapsSize +=
  1562         mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
  1564     aSizes->mFontListSize +=
  1565         mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
  1566     for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
  1567         gfxFontEntry *fe = mAvailableFonts[i];
  1568         if (fe) {
  1569             fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
  1574 void
  1575 gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  1576                                       FontListSizes* aSizes) const
  1578     aSizes->mFontListSize += aMallocSizeOf(this);
  1579     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  1582 /*
  1583  * gfxFontCache - global cache of gfxFont instances.
  1584  * Expires unused fonts after a short interval;
  1585  * notifies fonts to age their cached shaped-word records;
  1586  * observes memory-pressure notification and tells fonts to clear their
  1587  * shaped-word caches to free up memory.
  1588  */
  1590 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
  1592 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
  1594 NS_IMETHODIMP
  1595 gfxFontCache::MemoryReporter::CollectReports
  1596     (nsIMemoryReporterCallback* aCb,
  1597      nsISupports* aClosure)
  1599     FontCacheSizes sizes;
  1601     gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
  1602                                                      &sizes);
  1604     aCb->Callback(EmptyCString(),
  1605                   NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
  1606                   KIND_HEAP, UNITS_BYTES, sizes.mFontInstances,
  1607                   NS_LITERAL_CSTRING("Memory used for active font instances."),
  1608                   aClosure);
  1610     aCb->Callback(EmptyCString(),
  1611                   NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"),
  1612                   KIND_HEAP, UNITS_BYTES, sizes.mShapedWords,
  1613                   NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."),
  1614                   aClosure);
  1616     return NS_OK;
  1619 NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
  1621 NS_IMETHODIMP
  1622 gfxFontCache::Observer::Observe(nsISupports *aSubject,
  1623                                 const char *aTopic,
  1624                                 const char16_t *someData)
  1626     if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
  1627         gfxFontCache *fontCache = gfxFontCache::GetCache();
  1628         if (fontCache) {
  1629             fontCache->FlushShapedWordCaches();
  1631     } else {
  1632         NS_NOTREACHED("unexpected notification topic");
  1634     return NS_OK;
  1637 nsresult
  1638 gfxFontCache::Init()
  1640     NS_ASSERTION(!gGlobalCache, "Where did this come from?");
  1641     gGlobalCache = new gfxFontCache();
  1642     if (!gGlobalCache) {
  1643         return NS_ERROR_OUT_OF_MEMORY;
  1645     RegisterStrongMemoryReporter(new MemoryReporter());
  1646     return NS_OK;
  1649 void
  1650 gfxFontCache::Shutdown()
  1652     delete gGlobalCache;
  1653     gGlobalCache = nullptr;
  1655 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  1656     printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
  1657     printf("Total number of fonts=%d\n", gFontCount);
  1658     printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
  1659             int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
  1660     printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
  1661     printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
  1662     printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
  1663     printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
  1664     printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
  1665 #endif
  1668 gfxFontCache::gfxFontCache()
  1669     : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
  1671     nsCOMPtr<nsIObserverService> obs = GetObserverService();
  1672     if (obs) {
  1673         obs->AddObserver(new Observer, "memory-pressure", false);
  1676 #ifndef RELEASE_BUILD
  1677     // Currently disabled for release builds, due to unexplained crashes
  1678     // during expiration; see bug 717175 & 894798.
  1679     mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
  1680     if (mWordCacheExpirationTimer) {
  1681         mWordCacheExpirationTimer->
  1682             InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
  1683                                  SHAPED_WORD_TIMEOUT_SECONDS * 1000,
  1684                                  nsITimer::TYPE_REPEATING_SLACK);
  1686 #endif
  1689 gfxFontCache::~gfxFontCache()
  1691     // Ensure the user font cache releases its references to font entries,
  1692     // so they aren't kept alive after the font instances and font-list
  1693     // have been shut down.
  1694     gfxUserFontSet::UserFontCache::Shutdown();
  1696     if (mWordCacheExpirationTimer) {
  1697         mWordCacheExpirationTimer->Cancel();
  1698         mWordCacheExpirationTimer = nullptr;
  1701     // Expire everything that has a zero refcount, so we don't leak them.
  1702     AgeAllGenerations();
  1703     // All fonts should be gone.
  1704     NS_WARN_IF_FALSE(mFonts.Count() == 0,
  1705                      "Fonts still alive while shutting down gfxFontCache");
  1706     // Note that we have to delete everything through the expiration
  1707     // tracker, since there might be fonts not in the hashtable but in
  1708     // the tracker.
  1711 bool
  1712 gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
  1714     return aKey->mFontEntry == mFont->GetFontEntry() &&
  1715            aKey->mStyle->Equals(*mFont->GetStyle());
  1718 already_AddRefed<gfxFont>
  1719 gfxFontCache::Lookup(const gfxFontEntry *aFontEntry,
  1720                      const gfxFontStyle *aStyle)
  1722     Key key(aFontEntry, aStyle);
  1723     HashEntry *entry = mFonts.GetEntry(key);
  1725     Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
  1726     if (!entry)
  1727         return nullptr;
  1729     nsRefPtr<gfxFont> font = entry->mFont;
  1730     return font.forget();
  1733 void
  1734 gfxFontCache::AddNew(gfxFont *aFont)
  1736     Key key(aFont->GetFontEntry(), aFont->GetStyle());
  1737     HashEntry *entry = mFonts.PutEntry(key);
  1738     if (!entry)
  1739         return;
  1740     gfxFont *oldFont = entry->mFont;
  1741     entry->mFont = aFont;
  1742     // Assert that we can find the entry we just put in (this fails if the key
  1743     // has a NaN float value in it, e.g. 'sizeAdjust').
  1744     MOZ_ASSERT(entry == mFonts.GetEntry(key));
  1745     // If someone's asked us to replace an existing font entry, then that's a
  1746     // bit weird, but let it happen, and expire the old font if it's not used.
  1747     if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
  1748         // if oldFont == aFont, recount should be > 0,
  1749         // so we shouldn't be here.
  1750         NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
  1751         NotifyExpired(oldFont);
  1755 void
  1756 gfxFontCache::NotifyReleased(gfxFont *aFont)
  1758     nsresult rv = AddObject(aFont);
  1759     if (NS_FAILED(rv)) {
  1760         // We couldn't track it for some reason. Kill it now.
  1761         DestroyFont(aFont);
  1763     // Note that we might have fonts that aren't in the hashtable, perhaps because
  1764     // of OOM adding to the hashtable or because someone did an AddNew where
  1765     // we already had a font. These fonts are added to the expiration tracker
  1766     // anyway, even though Lookup can't resurrect them. Eventually they will
  1767     // expire and be deleted.
  1770 void
  1771 gfxFontCache::NotifyExpired(gfxFont *aFont)
  1773     aFont->ClearCachedWords();
  1774     RemoveObject(aFont);
  1775     DestroyFont(aFont);
  1778 void
  1779 gfxFontCache::DestroyFont(gfxFont *aFont)
  1781     Key key(aFont->GetFontEntry(), aFont->GetStyle());
  1782     HashEntry *entry = mFonts.GetEntry(key);
  1783     if (entry && entry->mFont == aFont) {
  1784         mFonts.RemoveEntry(key);
  1786     NS_ASSERTION(aFont->GetRefCount() == 0,
  1787                  "Destroying with non-zero ref count!");
  1788     delete aFont;
  1791 /*static*/
  1792 PLDHashOperator
  1793 gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
  1795     aHashEntry->mFont->AgeCachedWords();
  1796     return PL_DHASH_NEXT;
  1799 /*static*/
  1800 void
  1801 gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
  1803     gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
  1804     cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nullptr);
  1807 /*static*/
  1808 PLDHashOperator
  1809 gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
  1811     aHashEntry->mFont->ClearCachedWords();
  1812     return PL_DHASH_NEXT;
  1815 /*static*/
  1816 size_t
  1817 gfxFontCache::AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry,
  1818                                               MallocSizeOf aMallocSizeOf,
  1819                                               void* aUserArg)
  1821     HashEntry *entry = static_cast<HashEntry*>(aHashEntry);
  1822     FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg);
  1823     entry->mFont->AddSizeOfExcludingThis(aMallocSizeOf, sizes);
  1825     // The entry's size is recorded in the |sizes| parameter, so we return zero
  1826     // here to the hashtable enumerator.
  1827     return 0;
  1830 void
  1831 gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  1832                                      FontCacheSizes* aSizes) const
  1834     // TODO: add the overhead of the expiration tracker (generation arrays)
  1836     aSizes->mFontInstances +=
  1837         mFonts.SizeOfExcludingThis(AddSizeOfFontEntryExcludingThis,
  1838                                    aMallocSizeOf, aSizes);
  1841 void
  1842 gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  1843                                      FontCacheSizes* aSizes) const
  1845     aSizes->mFontInstances += aMallocSizeOf(this);
  1846     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  1849 #define MAX_SSXX_VALUE 99
  1850 #define MAX_CVXX_VALUE 99
  1852 static void
  1853 LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
  1854                       const nsAString& aFamily,
  1855                       const nsTArray<gfxAlternateValue>& altValue,
  1856                       nsTArray<gfxFontFeature>& aFontFeatures)
  1858     uint32_t numAlternates = altValue.Length();
  1859     for (uint32_t i = 0; i < numAlternates; i++) {
  1860         const gfxAlternateValue& av = altValue.ElementAt(i);
  1861         nsAutoTArray<uint32_t,4> values;
  1863         // map <family, name, feature> ==> <values>
  1864         bool found =
  1865             featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
  1866                                                    av.value, values);
  1867         uint32_t numValues = values.Length();
  1869         // nothing defined, skip
  1870         if (!found || numValues == 0) {
  1871             continue;
  1874         gfxFontFeature feature;
  1875         if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
  1876             NS_ASSERTION(numValues <= 2,
  1877                          "too many values allowed for character-variant");
  1878             // character-variant(12 3) ==> 'cv12' = 3
  1879             uint32_t nn = values.ElementAt(0);
  1880             // ignore values greater than 99
  1881             if (nn == 0 || nn > MAX_CVXX_VALUE) {
  1882                 continue;
  1884             feature.mValue = 1;
  1885             if (numValues > 1) {
  1886                 feature.mValue = values.ElementAt(1);
  1888             feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
  1889             aFontFeatures.AppendElement(feature);
  1891         } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
  1892             // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
  1893             feature.mValue = 1;
  1894             for (uint32_t v = 0; v < numValues; v++) {
  1895                 uint32_t nn = values.ElementAt(v);
  1896                 if (nn == 0 || nn > MAX_SSXX_VALUE) {
  1897                     continue;
  1899                 feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
  1900                 aFontFeatures.AppendElement(feature);
  1903         } else {
  1904             NS_ASSERTION(numValues == 1,
  1905                    "too many values for font-specific font-variant-alternates");
  1906             feature.mValue = values.ElementAt(0);
  1908             switch (av.alternate) {
  1909                 case NS_FONT_VARIANT_ALTERNATES_STYLISTIC:  // salt
  1910                     feature.mTag = HB_TAG('s','a','l','t');
  1911                     break;
  1912                 case NS_FONT_VARIANT_ALTERNATES_SWASH:  // swsh, cswh
  1913                     feature.mTag = HB_TAG('s','w','s','h');
  1914                     aFontFeatures.AppendElement(feature);
  1915                     feature.mTag = HB_TAG('c','s','w','h');
  1916                     break;
  1917                 case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
  1918                     feature.mTag = HB_TAG('o','r','n','m');
  1919                     break;
  1920                 case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
  1921                     feature.mTag = HB_TAG('n','a','l','t');
  1922                     break;
  1923                 default:
  1924                     feature.mTag = 0;
  1925                     break;
  1928             NS_ASSERTION(feature.mTag, "unsupported alternate type");
  1929             if (!feature.mTag) {
  1930                 continue;
  1932             aFontFeatures.AppendElement(feature);
  1937 /* static */ bool
  1938 gfxFontShaper::MergeFontFeatures(
  1939     const gfxFontStyle *aStyle,
  1940     const nsTArray<gfxFontFeature>& aFontFeatures,
  1941     bool aDisableLigatures,
  1942     const nsAString& aFamilyName,
  1943     nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures)
  1945     uint32_t numAlts = aStyle->alternateValues.Length();
  1946     const nsTArray<gfxFontFeature>& styleRuleFeatures =
  1947         aStyle->featureSettings;
  1949     // bail immediately if nothing to do
  1950     if (styleRuleFeatures.IsEmpty() &&
  1951         aFontFeatures.IsEmpty() &&
  1952         !aDisableLigatures &&
  1953         numAlts == 0) {
  1954         return false;
  1957     // Ligature features are enabled by default in the generic shaper,
  1958     // so we explicitly turn them off if necessary (for letter-spacing)
  1959     if (aDisableLigatures) {
  1960         aMergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
  1961         aMergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
  1964     // add feature values from font
  1965     uint32_t i, count;
  1967     count = aFontFeatures.Length();
  1968     for (i = 0; i < count; i++) {
  1969         const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
  1970         aMergedFeatures.Put(feature.mTag, feature.mValue);
  1973     // add font-specific feature values from style rules
  1974     if (aStyle->featureValueLookup && numAlts > 0) {
  1975         nsAutoTArray<gfxFontFeature,4> featureList;
  1977         // insert list of alternate feature settings
  1978         LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
  1979                               aStyle->alternateValues, featureList);
  1981         count = featureList.Length();
  1982         for (i = 0; i < count; i++) {
  1983             const gfxFontFeature& feature = featureList.ElementAt(i);
  1984             aMergedFeatures.Put(feature.mTag, feature.mValue);
  1988     // add feature values from style rules
  1989     count = styleRuleFeatures.Length();
  1990     for (i = 0; i < count; i++) {
  1991         const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
  1992         aMergedFeatures.Put(feature.mTag, feature.mValue);
  1995     return aMergedFeatures.Count() != 0;
  1998 void
  1999 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
  2001     mAscent = std::max(mAscent, aOther.mAscent);
  2002     mDescent = std::max(mDescent, aOther.mDescent);
  2003     if (aOtherIsOnLeft) {
  2004         mBoundingBox =
  2005             (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
  2006     } else {
  2007         mBoundingBox =
  2008             mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
  2010     mAdvanceWidth += aOther.mAdvanceWidth;
  2013 gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
  2014                  AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
  2015     mScaledFont(aScaledFont),
  2016     mFontEntry(aFontEntry), mIsValid(true),
  2017     mApplySyntheticBold(false),
  2018     mStyle(*aFontStyle),
  2019     mAdjustedSize(0.0),
  2020     mFUnitsConvFactor(0.0f),
  2021     mAntialiasOption(anAAOption)
  2023 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  2024     ++gFontCount;
  2025 #endif
  2026     mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
  2029 static PLDHashOperator
  2030 NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
  2031                     void* aClosure)
  2033     aKey->GetKey()->ForgetFont();
  2034     return PL_DHASH_NEXT;
  2037 gfxFont::~gfxFont()
  2039     uint32_t i, count = mGlyphExtentsArray.Length();
  2040     // We destroy the contents of mGlyphExtentsArray explicitly instead of
  2041     // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
  2042     // of classes that lack a proper copy constructor
  2043     for (i = 0; i < count; ++i) {
  2044         delete mGlyphExtentsArray[i];
  2047     mFontEntry->NotifyFontDestroyed(this);
  2049     if (mGlyphChangeObservers) {
  2050         mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
  2054 gfxFloat
  2055 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
  2057     if (!SetupCairoFont(aCtx)) {
  2058         return 0;
  2060     if (ProvidesGlyphWidths()) {
  2061         return GetGlyphWidth(aCtx, aGID) / 65536.0;
  2063     if (mFUnitsConvFactor == 0.0f) {
  2064         GetMetrics();
  2066     NS_ASSERTION(mFUnitsConvFactor > 0.0f,
  2067                  "missing font unit conversion factor");
  2068     if (!mHarfBuzzShaper) {
  2069         mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
  2071     gfxHarfBuzzShaper* shaper =
  2072         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
  2073     if (!shaper->Initialize()) {
  2074         return 0;
  2076     return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
  2079 /*static*/
  2080 PLDHashOperator
  2081 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
  2083     if (!aEntry->mShapedWord) {
  2084         NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
  2085         return PL_DHASH_REMOVE;
  2087     if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
  2088         return PL_DHASH_REMOVE;
  2090     return PL_DHASH_NEXT;
  2093 static void
  2094 CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
  2095                         uint32_t aFeatureIndex, hb_set_t *aLookups)
  2097     uint32_t lookups[32];
  2098     uint32_t i, len, offset;
  2100     offset = 0;
  2101     do {
  2102         len = ArrayLength(lookups);
  2103         hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
  2104                                          offset, &len, lookups);
  2105         for (i = 0; i < len; i++) {
  2106             hb_set_add(aLookups, lookups[i]);
  2108         offset += len;
  2109     } while (len == ArrayLength(lookups));
  2112 static void
  2113 CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
  2114                          const nsTHashtable<nsUint32HashKey>&
  2115                              aSpecificFeatures,
  2116                          hb_set_t *aOtherLookups,
  2117                          hb_set_t *aSpecificFeatureLookups,
  2118                          uint32_t aScriptIndex, uint32_t aLangIndex)
  2120     uint32_t reqFeatureIndex;
  2121     if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
  2122                                                          aScriptIndex,
  2123                                                          aLangIndex,
  2124                                                          &reqFeatureIndex)) {
  2125         CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
  2126                                 aOtherLookups);
  2129     uint32_t featureIndexes[32];
  2130     uint32_t i, len, offset;
  2132     offset = 0;
  2133     do {
  2134         len = ArrayLength(featureIndexes);
  2135         hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
  2136                                                   aScriptIndex, aLangIndex,
  2137                                                   offset, &len, featureIndexes);
  2139         for (i = 0; i < len; i++) {
  2140             uint32_t featureIndex = featureIndexes[i];
  2142             // get the feature tag
  2143             hb_tag_t featureTag;
  2144             uint32_t tagLen = 1;
  2145             hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
  2146                                                    aScriptIndex, aLangIndex,
  2147                                                    offset + i, &tagLen,
  2148                                                    &featureTag);
  2150             // collect lookups
  2151             hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
  2152                                     aSpecificFeatureLookups : aOtherLookups;
  2153             CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
  2155         offset += len;
  2156     } while (len == ArrayLength(featureIndexes));
  2159 static bool
  2160 HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
  2161                                hb_tag_t aScriptTag, uint32_t aScriptIndex,
  2162                                uint16_t aGlyph,
  2163                                const nsTHashtable<nsUint32HashKey>&
  2164                                    aDefaultFeatures,
  2165                                bool& aHasDefaultFeatureWithGlyph)
  2167     uint32_t numLangs, lang;
  2168     hb_set_t *defaultFeatureLookups = hb_set_create();
  2169     hb_set_t *nonDefaultFeatureLookups = hb_set_create();
  2171     // default lang
  2172     CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
  2173                              nonDefaultFeatureLookups, defaultFeatureLookups,
  2174                              aScriptIndex,
  2175                              HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  2177     // iterate over langs
  2178     numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
  2179                                                      aScriptIndex, 0,
  2180                                                      nullptr, nullptr);
  2181     for (lang = 0; lang < numLangs; lang++) {
  2182         CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
  2183                                  nonDefaultFeatureLookups,
  2184                                  defaultFeatureLookups,
  2185                                  aScriptIndex, lang);
  2188     // look for the glyph among default feature lookups
  2189     aHasDefaultFeatureWithGlyph = false;
  2190     hb_set_t *glyphs = hb_set_create();
  2191     hb_codepoint_t index = -1;
  2192     while (hb_set_next(defaultFeatureLookups, &index)) {
  2193         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  2194                                            glyphs, glyphs, glyphs,
  2195                                            glyphs);
  2196         if (hb_set_has(glyphs, aGlyph)) {
  2197             aHasDefaultFeatureWithGlyph = true;
  2198             break;
  2202     // look for the glyph among non-default feature lookups
  2203     // if no default feature lookups contained spaces
  2204     bool hasNonDefaultFeatureWithGlyph = false;
  2205     if (!aHasDefaultFeatureWithGlyph) {
  2206         hb_set_clear(glyphs);
  2207         index = -1;
  2208         while (hb_set_next(nonDefaultFeatureLookups, &index)) {
  2209             hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  2210                                                glyphs, glyphs, glyphs,
  2211                                                glyphs);
  2212             if (hb_set_has(glyphs, aGlyph)) {
  2213                 hasNonDefaultFeatureWithGlyph = true;
  2214                 break;
  2219     hb_set_destroy(glyphs);
  2220     hb_set_destroy(defaultFeatureLookups);
  2221     hb_set_destroy(nonDefaultFeatureLookups);
  2223     return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
  2226 static void
  2227 HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
  2228                        hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
  2229                        uint16_t aGlyph)
  2231     // iterate over the scripts in the font
  2232     uint32_t numScripts, numLangs, script, lang;
  2233     hb_set_t *otherLookups = hb_set_create();
  2234     hb_set_t *specificFeatureLookups = hb_set_create();
  2235     nsTHashtable<nsUint32HashKey> specificFeature;
  2237     specificFeature.PutEntry(aSpecificFeature);
  2239     numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
  2240                                                     nullptr, nullptr);
  2242     for (script = 0; script < numScripts; script++) {
  2243         // default lang
  2244         CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
  2245                                  otherLookups, specificFeatureLookups,
  2246                                  script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
  2248         // iterate over langs
  2249         numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
  2250                                                          script, 0,
  2251                                                          nullptr, nullptr);
  2252         for (lang = 0; lang < numLangs; lang++) {
  2253             CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
  2254                                      otherLookups, specificFeatureLookups,
  2255                                      script, lang);
  2259     // look for the glyph among non-specific feature lookups
  2260     hb_set_t *glyphs = hb_set_create();
  2261     hb_codepoint_t index = -1;
  2262     while (hb_set_next(otherLookups, &index)) {
  2263         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  2264                                            glyphs, glyphs, glyphs,
  2265                                            glyphs);
  2266         if (hb_set_has(glyphs, aGlyph)) {
  2267             aHasGlyph = true;
  2268             break;
  2272     // look for the glyph among specific feature lookups
  2273     hb_set_clear(glyphs);
  2274     index = -1;
  2275     while (hb_set_next(specificFeatureLookups, &index)) {
  2276         hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
  2277                                            glyphs, glyphs, glyphs,
  2278                                            glyphs);
  2279         if (hb_set_has(glyphs, aGlyph)) {
  2280             aHasGlyphSpecific = true;
  2281             break;
  2285     hb_set_destroy(glyphs);
  2286     hb_set_destroy(specificFeatureLookups);
  2287     hb_set_destroy(otherLookups);
  2290 nsDataHashtable<nsUint32HashKey, int32_t> *gfxFont::sScriptTagToCode = nullptr;
  2291 nsTHashtable<nsUint32HashKey>             *gfxFont::sDefaultFeatures = nullptr;
  2293 static inline bool
  2294 HasSubstitution(uint32_t *aBitVector, uint32_t aBit) {
  2295     return (aBitVector[aBit >> 5] & (1 << (aBit & 0x1f))) != 0;
  2298 // union of all default substitution features across scripts
  2299 static const hb_tag_t defaultFeatures[] = {
  2300     HB_TAG('a','b','v','f'),
  2301     HB_TAG('a','b','v','s'),
  2302     HB_TAG('a','k','h','n'),
  2303     HB_TAG('b','l','w','f'),
  2304     HB_TAG('b','l','w','s'),
  2305     HB_TAG('c','a','l','t'),
  2306     HB_TAG('c','c','m','p'),
  2307     HB_TAG('c','f','a','r'),
  2308     HB_TAG('c','j','c','t'),
  2309     HB_TAG('c','l','i','g'),
  2310     HB_TAG('f','i','n','2'),
  2311     HB_TAG('f','i','n','3'),
  2312     HB_TAG('f','i','n','a'),
  2313     HB_TAG('h','a','l','f'),
  2314     HB_TAG('h','a','l','n'),
  2315     HB_TAG('i','n','i','t'),
  2316     HB_TAG('i','s','o','l'),
  2317     HB_TAG('l','i','g','a'),
  2318     HB_TAG('l','j','m','o'),
  2319     HB_TAG('l','o','c','l'),
  2320     HB_TAG('l','t','r','a'),
  2321     HB_TAG('l','t','r','m'),
  2322     HB_TAG('m','e','d','2'),
  2323     HB_TAG('m','e','d','i'),
  2324     HB_TAG('m','s','e','t'),
  2325     HB_TAG('n','u','k','t'),
  2326     HB_TAG('p','r','e','f'),
  2327     HB_TAG('p','r','e','s'),
  2328     HB_TAG('p','s','t','f'),
  2329     HB_TAG('p','s','t','s'),
  2330     HB_TAG('r','c','l','t'),
  2331     HB_TAG('r','l','i','g'),
  2332     HB_TAG('r','k','r','f'),
  2333     HB_TAG('r','p','h','f'),
  2334     HB_TAG('r','t','l','a'),
  2335     HB_TAG('r','t','l','m'),
  2336     HB_TAG('t','j','m','o'),
  2337     HB_TAG('v','a','t','u'),
  2338     HB_TAG('v','e','r','t'),
  2339     HB_TAG('v','j','m','o')
  2340 };
  2342 void
  2343 gfxFont::CheckForFeaturesInvolvingSpace()
  2345     mFontEntry->mHasSpaceFeaturesInitialized = true;
  2347 #ifdef PR_LOGGING
  2348     bool log = LOG_FONTINIT_ENABLED();
  2349     TimeStamp start;
  2350     if (MOZ_UNLIKELY(log)) {
  2351         start = TimeStamp::Now();
  2353 #endif
  2355     bool result = false;
  2357     uint32_t spaceGlyph = GetSpaceGlyph();
  2358     if (!spaceGlyph) {
  2359         return;
  2362     hb_face_t *face = GetFontEntry()->GetHBFace();
  2364     // GSUB lookups - examine per script
  2365     if (hb_ot_layout_has_substitution(face)) {
  2367         // set up the script ==> code hashtable if needed
  2368         if (!sScriptTagToCode) {
  2369             sScriptTagToCode =
  2370                 new nsDataHashtable<nsUint32HashKey,
  2371                                     int32_t>(MOZ_NUM_SCRIPT_CODES);
  2372             sScriptTagToCode->Put(HB_TAG('D','F','L','T'), MOZ_SCRIPT_COMMON);
  2373             for (int32_t s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) {
  2374                 hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
  2375                 hb_tag_t s1, s2;
  2376                 hb_ot_tags_from_script(scriptTag, &s1, &s2);
  2377                 sScriptTagToCode->Put(s1, s);
  2378                 if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
  2379                     sScriptTagToCode->Put(s2, s);
  2383             uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
  2384             sDefaultFeatures =
  2385                 new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
  2386             for (uint32_t i = 0; i < numDefaultFeatures; i++) {
  2387                 sDefaultFeatures->PutEntry(defaultFeatures[i]);
  2391         // iterate over the scripts in the font
  2392         hb_tag_t scriptTags[8];
  2394         uint32_t len, offset = 0;
  2395         do {
  2396             len = ArrayLength(scriptTags);
  2397             hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
  2398                                                &len, scriptTags);
  2399             for (uint32_t i = 0; i < len; i++) {
  2400                 bool isDefaultFeature = false;
  2401                 int32_t s;
  2402                 if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
  2403                                                     scriptTags[i], offset + i,
  2404                                                     spaceGlyph,
  2405                                                     *sDefaultFeatures,
  2406                                                     isDefaultFeature) ||
  2407                     !sScriptTagToCode->Get(scriptTags[i], &s))
  2409                     continue;
  2411                 result = true;
  2412                 uint32_t index = s >> 5;
  2413                 uint32_t bit = s & 0x1f;
  2414                 if (isDefaultFeature) {
  2415                     mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
  2416                 } else {
  2417                     mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
  2420             offset += len;
  2421         } while (len == ArrayLength(scriptTags));
  2424     // spaces in default features of default script?
  2425     // ==> can't use word cache, skip GPOS analysis
  2426     bool canUseWordCache = true;
  2427     if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  2428                         MOZ_SCRIPT_COMMON)) {
  2429         canUseWordCache = false;
  2432     // GPOS lookups - distinguish kerning from non-kerning features
  2433     mFontEntry->mHasSpaceFeaturesKerning = false;
  2434     mFontEntry->mHasSpaceFeaturesNonKerning = false;
  2436     if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
  2437         bool hasKerning = false, hasNonKerning = false;
  2438         HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
  2439                                HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
  2440         if (hasKerning || hasNonKerning) {
  2441             result = true;
  2443         mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
  2444         mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
  2447     hb_face_destroy(face);
  2448     mFontEntry->mHasSpaceFeatures = result;
  2450 #ifdef PR_LOGGING
  2451     if (MOZ_UNLIKELY(log)) {
  2452         TimeDuration elapsed = TimeStamp::Now() - start;
  2453         LOG_FONTINIT((
  2454             "(fontinit-spacelookups) font: %s - "
  2455             "subst default: %8.8x %8.8x %8.8x %8.8x "
  2456             "subst non-default: %8.8x %8.8x %8.8x %8.8x "
  2457             "kerning: %s non-kerning: %s time: %6.3f\n",
  2458             NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
  2459             mFontEntry->mDefaultSubSpaceFeatures[3],
  2460             mFontEntry->mDefaultSubSpaceFeatures[2],
  2461             mFontEntry->mDefaultSubSpaceFeatures[1],
  2462             mFontEntry->mDefaultSubSpaceFeatures[0],
  2463             mFontEntry->mNonDefaultSubSpaceFeatures[3],
  2464             mFontEntry->mNonDefaultSubSpaceFeatures[2],
  2465             mFontEntry->mNonDefaultSubSpaceFeatures[1],
  2466             mFontEntry->mNonDefaultSubSpaceFeatures[0],
  2467             (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
  2468             (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
  2469             elapsed.ToMilliseconds()
  2470         ));
  2472 #endif
  2475 bool
  2476 gfxFont::HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript)
  2478     NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
  2479                  "need to initialize space lookup flags");
  2480     NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code");
  2481     if (aRunScript == MOZ_SCRIPT_INVALID ||
  2482         aRunScript >= MOZ_NUM_SCRIPT_CODES) {
  2483         return false;
  2486     // default features have space lookups ==> true
  2487     if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  2488                         MOZ_SCRIPT_COMMON) ||
  2489         HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
  2490                         aRunScript))
  2492         return true;
  2495     // non-default features have space lookups and some type of
  2496     // font feature, in font or style is specified ==> true
  2497     if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
  2498                          MOZ_SCRIPT_COMMON) ||
  2499          HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
  2500                          aRunScript)) &&
  2501         (!mStyle.featureSettings.IsEmpty() ||
  2502          !mFontEntry->mFeatureSettings.IsEmpty()))
  2504         return true;
  2507     return false;
  2510 bool
  2511 gfxFont::SpaceMayParticipateInShaping(int32_t aRunScript)
  2513     // avoid checking fonts known not to include default space-dependent features
  2514     if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
  2515         if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
  2516             mFontEntry->mFeatureSettings.IsEmpty()) {
  2517             return false;
  2521     // We record the presence of space-dependent features in the font entry
  2522     // so that subsequent instantiations for the same font face won't
  2523     // require us to re-check the tables; however, the actual check is done
  2524     // by gfxFont because not all font entry subclasses know how to create
  2525     // a harfbuzz face for introspection.
  2526     if (!mFontEntry->mHasSpaceFeaturesInitialized) {
  2527         CheckForFeaturesInvolvingSpace();
  2530     if (!mFontEntry->mHasSpaceFeatures) {
  2531         return false;
  2534     // if font has substitution rules or non-kerning positioning rules
  2535     // that involve spaces, bypass
  2536     if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
  2537         mFontEntry->mHasSpaceFeaturesNonKerning) {
  2538         return true;
  2541     // if kerning explicitly enabled/disabled via font-feature-settings or
  2542     // font-kerning and kerning rules use spaces, only bypass when enabled
  2543     if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
  2544         return mKerningEnabled;
  2547     return false;
  2550 bool
  2551 gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
  2553     aFeatureOn = false;
  2555     if (mStyle.featureSettings.IsEmpty() &&
  2556         GetFontEntry()->mFeatureSettings.IsEmpty()) {
  2557         return false;
  2560     // add feature values from font
  2561     bool featureSet = false;
  2562     uint32_t i, count;
  2564     nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
  2565     count = fontFeatures.Length();
  2566     for (i = 0; i < count; i++) {
  2567         const gfxFontFeature& feature = fontFeatures.ElementAt(i);
  2568         if (feature.mTag == aFeature) {
  2569             featureSet = true;
  2570             aFeatureOn = (feature.mValue != 0);
  2574     // add feature values from style rules
  2575     nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
  2576     count = styleFeatures.Length();
  2577     for (i = 0; i < count; i++) {
  2578         const gfxFontFeature& feature = styleFeatures.ElementAt(i);
  2579         if (feature.mTag == aFeature) {
  2580             featureSet = true;
  2581             aFeatureOn = (feature.mValue != 0);
  2585     return featureSet;
  2588 /**
  2589  * A helper function in case we need to do any rounding or other
  2590  * processing here.
  2591  */
  2592 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
  2593     (double(aAppUnits)*double(aDevUnitsPerAppUnit))
  2595 struct GlyphBuffer {
  2596 #define GLYPH_BUFFER_SIZE (2048/sizeof(cairo_glyph_t))
  2597     cairo_glyph_t mGlyphBuffer[GLYPH_BUFFER_SIZE];
  2598     unsigned int mNumGlyphs;
  2600     GlyphBuffer()
  2601         : mNumGlyphs(0) { }
  2603     cairo_glyph_t *AppendGlyph() {
  2604         return &mGlyphBuffer[mNumGlyphs++];
  2607     void Flush(cairo_t *aCR, DrawMode aDrawMode, bool aReverse,
  2608                gfxTextContextPaint *aContextPaint,
  2609                const gfxMatrix& aGlobalMatrix, bool aFinish = false) {
  2610         // Ensure there's enough room for a glyph to be added to the buffer
  2611         // and we actually have glyphs to draw
  2612         if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
  2613             return;
  2616         if (aReverse) {
  2617             for (uint32_t i = 0; i < mNumGlyphs/2; ++i) {
  2618                 cairo_glyph_t tmp = mGlyphBuffer[i];
  2619                 mGlyphBuffer[i] = mGlyphBuffer[mNumGlyphs - 1 - i];
  2620                 mGlyphBuffer[mNumGlyphs - 1 - i] = tmp;
  2624         if (aDrawMode == DrawMode::GLYPH_PATH) {
  2625             cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
  2626         } else {
  2627             if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  2628                                   (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
  2629                 FlushStroke(aCR, aContextPaint, aGlobalMatrix);
  2631             if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
  2632                 PROFILER_LABEL("GlyphBuffer", "cairo_show_glyphs");
  2633                 nsRefPtr<gfxPattern> pattern;
  2634                 if (aContextPaint &&
  2635                     !!(pattern = aContextPaint->GetFillPattern(aGlobalMatrix))) {
  2636                     cairo_save(aCR);
  2637                     cairo_set_source(aCR, pattern->CairoPattern());
  2640                 cairo_show_glyphs(aCR, mGlyphBuffer, mNumGlyphs);
  2642                 if (pattern) {
  2643                     cairo_restore(aCR);
  2646             if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  2647                                   int(DrawMode::GLYPH_STROKE)) {
  2648                 FlushStroke(aCR, aContextPaint, aGlobalMatrix);
  2652         mNumGlyphs = 0;
  2655 private:
  2656     void FlushStroke(cairo_t *aCR, gfxTextContextPaint *aContextPaint,
  2657                      const gfxMatrix& aGlobalMatrix) {
  2658         nsRefPtr<gfxPattern> pattern;
  2659         if (aContextPaint &&
  2660             !!(pattern = aContextPaint->GetStrokePattern(aGlobalMatrix))) {
  2661             cairo_save(aCR);
  2662             cairo_set_source(aCR, pattern->CairoPattern());
  2665         cairo_new_path(aCR);
  2666         cairo_glyph_path(aCR, mGlyphBuffer, mNumGlyphs);
  2667         cairo_stroke(aCR);
  2669         if (pattern) {
  2670             cairo_restore(aCR);
  2674 #undef GLYPH_BUFFER_SIZE
  2675 };
  2677 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
  2678   switch (aAAOption) {
  2679   case gfxFont::kAntialiasSubpixel:
  2680     return AntialiasMode::SUBPIXEL;
  2681   case gfxFont::kAntialiasGrayscale:
  2682     return AntialiasMode::GRAY;
  2683   case gfxFont::kAntialiasNone:
  2684     return AntialiasMode::NONE;
  2685   default:
  2686     return AntialiasMode::DEFAULT;
  2690 struct GlyphBufferAzure {
  2691 #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
  2692     Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
  2693     unsigned int mNumGlyphs;
  2695     GlyphBufferAzure()
  2696         : mNumGlyphs(0) { }
  2698     Glyph *AppendGlyph() {
  2699         return &mGlyphBuffer[mNumGlyphs++];
  2702     void Flush(DrawTarget *aDT, gfxTextContextPaint *aContextPaint, ScaledFont *aFont,
  2703                DrawMode aDrawMode, bool aReverse, const GlyphRenderingOptions *aOptions,
  2704                gfxContext *aThebesContext, const Matrix *aInvFontMatrix, const DrawOptions &aDrawOptions,
  2705                bool aFinish = false)
  2707         // Ensure there's enough room for a glyph to be added to the buffer
  2708         if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
  2709             return;
  2712         if (aReverse) {
  2713             Glyph *begin = &mGlyphBuffer[0];
  2714             Glyph *end = &mGlyphBuffer[mNumGlyphs];
  2715             std::reverse(begin, end);
  2718         gfx::GlyphBuffer buf;
  2719         buf.mGlyphs = mGlyphBuffer;
  2720         buf.mNumGlyphs = mNumGlyphs;
  2722         gfxContext::AzureState state = aThebesContext->CurrentState();
  2723         if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  2724                               (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
  2725             FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
  2727         if (int(aDrawMode) & int(DrawMode::GLYPH_FILL)) {
  2728             if (state.pattern || aContextPaint) {
  2729                 Pattern *pat;
  2731                 nsRefPtr<gfxPattern> fillPattern;
  2732                 if (!aContextPaint ||
  2733                     !(fillPattern = aContextPaint->GetFillPattern(aThebesContext->CurrentMatrix()))) {
  2734                     if (state.pattern) {
  2735                         pat = state.pattern->GetPattern(aDT, state.patternTransformChanged ? &state.patternTransform : nullptr);
  2736                     } else {
  2737                         pat = nullptr;
  2739                 } else {
  2740                     pat = fillPattern->GetPattern(aDT);
  2743                 if (pat) {
  2744                     Matrix saved;
  2745                     Matrix *mat = nullptr;
  2746                     if (aInvFontMatrix) {
  2747                         // The brush matrix needs to be multiplied with the inverted matrix
  2748                         // as well, to move the brush into the space of the glyphs. Before
  2749                         // the render target transformation
  2751                         // This relies on the returned Pattern not to be reused by
  2752                         // others, but regenerated on GetPattern calls. This is true!
  2753                         if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
  2754                             mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
  2755                         } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
  2756                             mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
  2757                         } else if (pat->GetType() == PatternType::SURFACE) {
  2758                             mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
  2761                         if (mat) {
  2762                             saved = *mat;
  2763                             *mat = (*mat) * (*aInvFontMatrix);
  2767                     aDT->FillGlyphs(aFont, buf, *pat,
  2768                                     aDrawOptions, aOptions);
  2770                     if (mat) {
  2771                         *mat = saved;
  2774             } else if (state.sourceSurface) {
  2775                 aDT->FillGlyphs(aFont, buf, SurfacePattern(state.sourceSurface,
  2776                                                            ExtendMode::CLAMP,
  2777                                                            state.surfTransform),
  2778                                 aDrawOptions, aOptions);
  2779             } else {
  2780                 aDT->FillGlyphs(aFont, buf, ColorPattern(state.color),
  2781                                 aDrawOptions, aOptions);
  2784         if (int(aDrawMode) & int(DrawMode::GLYPH_PATH)) {
  2785             aThebesContext->EnsurePathBuilder();
  2786 			Matrix mat = aDT->GetTransform();
  2787             aFont->CopyGlyphsToBuilder(buf, aThebesContext->mPathBuilder, aDT->GetType(), &mat);
  2789         if ((int(aDrawMode) & (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
  2790                               int(DrawMode::GLYPH_STROKE)) {
  2791             FlushStroke(aDT, aContextPaint, aFont, aThebesContext, buf, state);
  2794         mNumGlyphs = 0;
  2797 private:
  2798     void FlushStroke(DrawTarget *aDT, gfxTextContextPaint *aContextPaint,
  2799                      ScaledFont *aFont, gfxContext *aThebesContext,
  2800                      gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
  2802         RefPtr<Path> path = aFont->GetPathForGlyphs(aBuf, aDT);
  2803         if (aContextPaint) {
  2804             nsRefPtr<gfxPattern> strokePattern =
  2805               aContextPaint->GetStrokePattern(aThebesContext->CurrentMatrix());
  2806             if (strokePattern) {
  2807                 aDT->Stroke(path, *strokePattern->GetPattern(aDT), aState.strokeOptions);
  2812 #undef GLYPH_BUFFER_SIZE
  2813 };
  2815 // Bug 674909. When synthetic bolding text by drawing twice, need to
  2816 // render using a pixel offset in device pixels, otherwise text
  2817 // doesn't appear bolded, it appears as if a bad text shadow exists
  2818 // when a non-identity transform exists.  Use an offset factor so that
  2819 // the second draw occurs at a constant offset in device pixels.
  2821 double
  2822 gfxFont::CalcXScale(gfxContext *aContext)
  2824     // determine magnitude of a 1px x offset in device space
  2825     gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
  2826     if (t.width == 1.0 && t.height == 0.0) {
  2827         // short-circuit the most common case to avoid sqrt() and division
  2828         return 1.0;
  2831     double m = sqrt(t.width * t.width + t.height * t.height);
  2833     NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
  2834     if (m == 0.0) {
  2835         return 0.0; // effectively disables offset
  2838     // scale factor so that offsets are 1px in device pixels
  2839     return 1.0 / m;
  2842 static DrawMode
  2843 ForcePaintingDrawMode(DrawMode aDrawMode)
  2845     return aDrawMode == DrawMode::GLYPH_PATH ?
  2846         DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
  2847         aDrawMode;
  2850 void
  2851 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
  2852               gfxContext *aContext, DrawMode aDrawMode, gfxPoint *aPt,
  2853               Spacing *aSpacing, gfxTextContextPaint *aContextPaint,
  2854               gfxTextRunDrawCallbacks *aCallbacks)
  2856     NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
  2857                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
  2859     if (aStart >= aEnd)
  2860         return;
  2862     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  2863     const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  2864     const double devUnitsPerAppUnit = 1.0/double(appUnitsPerDevUnit);
  2865     bool isRTL = aTextRun->IsRightToLeft();
  2866     double direction = aTextRun->GetDirection();
  2867     gfxMatrix globalMatrix = aContext->CurrentMatrix();
  2869     bool haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
  2870     nsAutoPtr<gfxTextContextPaint> contextPaint;
  2871     if (haveSVGGlyphs && !aContextPaint) {
  2872         // If no pattern is specified for fill, use the current pattern
  2873         NS_ASSERTION((int(aDrawMode) & int(DrawMode::GLYPH_STROKE)) == 0, "no pattern supplied for stroking text");
  2874         nsRefPtr<gfxPattern> fillPattern = aContext->GetPattern();
  2875         contextPaint = new SimpleTextContextPaint(fillPattern, nullptr,
  2876                                                  aContext->CurrentMatrix());
  2877         aContextPaint = contextPaint;
  2880     // synthetic-bold strikes are each offset one device pixel in run direction
  2881     // (these values are only needed if IsSyntheticBold() is true)
  2882     double synBoldOnePixelOffset = 0;
  2883     int32_t strikes = 1;
  2884     if (IsSyntheticBold()) {
  2885         double xscale = CalcXScale(aContext);
  2886         synBoldOnePixelOffset = direction * xscale;
  2887         if (xscale != 0.0) {
  2888             // use as many strikes as needed for the the increased advance
  2889             strikes = NS_lroundf(GetSyntheticBoldOffset() / xscale);
  2893     uint32_t i;
  2894     // Current position in appunits
  2895     double x = aPt->x;
  2896     double y = aPt->y;
  2898     cairo_t *cr = aContext->GetCairo();
  2899     RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
  2901     bool paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
  2902     bool emittedGlyphs = false;
  2904     if (aContext->IsCairo()) {
  2905       bool success = SetupCairoFont(aContext);
  2906       if (MOZ_UNLIKELY(!success))
  2907           return;
  2909       ::GlyphBuffer glyphs;
  2910       cairo_glyph_t *glyph;
  2912       if (aSpacing) {
  2913           x += direction*aSpacing[0].mBefore;
  2915       for (i = aStart; i < aEnd; ++i) {
  2916           const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  2917           if (glyphData->IsSimpleGlyph()) {
  2918               double advance = glyphData->GetSimpleAdvance();
  2919               double glyphX;
  2920               if (isRTL) {
  2921                   x -= advance;
  2922                   glyphX = x;
  2923               } else {
  2924                   glyphX = x;
  2925                   x += advance;
  2928               if (haveSVGGlyphs) {
  2929                   if (!paintSVGGlyphs) {
  2930                       continue;
  2932                   gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  2933                                  ToDeviceUnits(y, devUnitsPerAppUnit));
  2934                   DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  2935                   if (RenderSVGGlyph(aContext, point, mode,
  2936                                      glyphData->GetSimpleGlyph(), aContextPaint,
  2937                                      aCallbacks, emittedGlyphs)) {
  2938                       continue;
  2942               // Perhaps we should put a scale in the cairo context instead of
  2943               // doing this scaling here...
  2944               // Multiplying by the reciprocal may introduce tiny error here,
  2945               // but we assume cairo is going to round coordinates at some stage
  2946               // and this is faster
  2947               glyph = glyphs.AppendGlyph();
  2948               glyph->index = glyphData->GetSimpleGlyph();
  2949               glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  2950               glyph->y = ToDeviceUnits(y, devUnitsPerAppUnit);
  2951               glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  2953               // synthetic bolding by multi-striking with 1-pixel offsets
  2954               // at least once, more if there's room (large font sizes)
  2955               if (IsSyntheticBold()) {
  2956                   double strikeOffset = synBoldOnePixelOffset;
  2957                   int32_t strikeCount = strikes;
  2958                   do {
  2959                       cairo_glyph_t *doubleglyph;
  2960                       doubleglyph = glyphs.AppendGlyph();
  2961                       doubleglyph->index = glyph->index;
  2962                       doubleglyph->x =
  2963                           ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
  2964                                         devUnitsPerAppUnit);
  2965                       doubleglyph->y = glyph->y;
  2966                       strikeOffset += synBoldOnePixelOffset;
  2967                       glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  2968                   } while (--strikeCount > 0);
  2970               emittedGlyphs = true;
  2971           } else {
  2972               uint32_t glyphCount = glyphData->GetGlyphCount();
  2973               if (glyphCount > 0) {
  2974                   const gfxTextRun::DetailedGlyph *details =
  2975                       aTextRun->GetDetailedGlyphs(i);
  2976                   NS_ASSERTION(details, "detailedGlyph should not be missing!");
  2977                   double advance;
  2978                   for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
  2979                       advance = details->mAdvance;
  2980                       if (glyphData->IsMissing()) {
  2981                           // default ignorable characters will have zero advance width.
  2982                           // we don't have to draw the hexbox for them
  2983                           if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
  2984                               double glyphX = x;
  2985                               if (isRTL) {
  2986                                   glyphX -= advance;
  2988                               gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  2989                                           ToDeviceUnits(y, devUnitsPerAppUnit));
  2990                               gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
  2991                               gfxFloat height = GetMetrics().maxAscent;
  2992                               gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
  2993                               gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
  2994                                                                      glyphRect,
  2995                                                                      details->mGlyphID,
  2996                                                                      appUnitsPerDevUnit);
  2998                       } else {
  2999                           double glyphX = x + details->mXOffset;
  3000                           if (isRTL) {
  3001                               glyphX -= advance;
  3004                           gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  3005                                          ToDeviceUnits(y, devUnitsPerAppUnit));
  3007                           if (haveSVGGlyphs) {
  3008                               if (!paintSVGGlyphs) {
  3009                                   continue;
  3011                               DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  3012                               if (RenderSVGGlyph(aContext, point, mode,
  3013                                                   details->mGlyphID,
  3014                                                   aContextPaint, aCallbacks,
  3015                                                   emittedGlyphs)) {
  3016                                   continue;
  3020                           glyph = glyphs.AppendGlyph();
  3021                           glyph->index = details->mGlyphID;
  3022                           glyph->x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  3023                           glyph->y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
  3024                           glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  3026                           if (IsSyntheticBold()) {
  3027                               double strikeOffset = synBoldOnePixelOffset;
  3028                               int32_t strikeCount = strikes;
  3029                               do {
  3030                                   cairo_glyph_t *doubleglyph;
  3031                                   doubleglyph = glyphs.AppendGlyph();
  3032                                   doubleglyph->index = glyph->index;
  3033                                   doubleglyph->x =
  3034                                       ToDeviceUnits(glyphX + strikeOffset *
  3035                                               appUnitsPerDevUnit,
  3036                                               devUnitsPerAppUnit);
  3037                                   doubleglyph->y = glyph->y;
  3038                                   strikeOffset += synBoldOnePixelOffset;
  3039                                   glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix);
  3040                               } while (--strikeCount > 0);
  3042                           emittedGlyphs = true;
  3048           if (aSpacing) {
  3049               double space = aSpacing[i - aStart].mAfter;
  3050               if (i + 1 < aEnd) {
  3051                   space += aSpacing[i + 1 - aStart].mBefore;
  3053               x += direction*space;
  3057       if (gfxFontTestStore::CurrentStore()) {
  3058           /* This assumes that the tests won't have anything that results
  3059            * in more than GLYPH_BUFFER_SIZE glyphs.  Do this before we
  3060            * flush, since that'll blow away the num_glyphs.
  3061            */
  3062           gfxFontTestStore::CurrentStore()->AddItem(GetName(),
  3063                                                     glyphs.mGlyphBuffer,
  3064                                                     glyphs.mNumGlyphs);
  3067       // draw any remaining glyphs
  3068       glyphs.Flush(cr, aDrawMode, isRTL, aContextPaint, globalMatrix, true);
  3069       if (aCallbacks && emittedGlyphs) {
  3070           aCallbacks->NotifyGlyphPathEmitted();
  3073     } else {
  3074       RefPtr<ScaledFont> scaledFont = GetScaledFont(dt);
  3076       if (!scaledFont) {
  3077         return;
  3080       bool oldSubpixelAA = dt->GetPermitSubpixelAA();
  3082       if (!AllowSubpixelAA()) {
  3083           dt->SetPermitSubpixelAA(false);
  3086       GlyphBufferAzure glyphs;
  3087       Glyph *glyph;
  3089       Matrix mat, matInv;
  3090       Matrix oldMat = dt->GetTransform();
  3092       // This is nullptr when we have inverse-transformed glyphs and we need
  3093       // to transform the Brush inside flush.
  3094       Matrix *passedInvMatrix = nullptr;
  3096       RefPtr<GlyphRenderingOptions> renderingOptions =
  3097         GetGlyphRenderingOptions();
  3099       DrawOptions drawOptions;
  3100       drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
  3102       // The cairo DrawTarget backend uses the cairo_scaled_font directly
  3103       // and so has the font skew matrix applied already.
  3104       if (mScaledFont &&
  3105           dt->GetType() != BackendType::CAIRO) {
  3106         cairo_matrix_t matrix;
  3107         cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
  3108         if (matrix.xy != 0) {
  3109           // If this matrix applies a skew, which can happen when drawing
  3110           // oblique fonts, we will set the DrawTarget matrix to apply the
  3111           // skew. We'll need to move the glyphs by the inverse of the skew to
  3112           // get the glyphs positioned correctly in the new device space
  3113           // though, since the font matrix should only be applied to drawing
  3114           // the glyphs, and not to their position.
  3115           mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
  3117           mat._11 = mat._22 = 1.0;
  3118           float adjustedSize = mAdjustedSize > 0 ? mAdjustedSize : GetStyle()->size;
  3119           mat._21 /= adjustedSize;
  3121           dt->SetTransform(mat * oldMat);
  3123           matInv = mat;
  3124           matInv.Invert();
  3126           passedInvMatrix = &matInv;
  3130       if (aSpacing) {
  3131           x += direction*aSpacing[0].mBefore;
  3133       for (i = aStart; i < aEnd; ++i) {
  3134           const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  3135           if (glyphData->IsSimpleGlyph()) {
  3136               double advance = glyphData->GetSimpleAdvance();
  3137               double glyphX;
  3138               if (isRTL) {
  3139                   x -= advance;
  3140                   glyphX = x;
  3141               } else {
  3142                   glyphX = x;
  3143                   x += advance;
  3146               if (haveSVGGlyphs) {
  3147                   if (!paintSVGGlyphs) {
  3148                       continue;
  3150                   gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  3151                                  ToDeviceUnits(y, devUnitsPerAppUnit));
  3152                   DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  3153                   if (RenderSVGGlyph(aContext, point, mode,
  3154                                      glyphData->GetSimpleGlyph(), aContextPaint,
  3155                                      aCallbacks, emittedGlyphs)) {
  3156                       continue;
  3160               // Perhaps we should put a scale in the cairo context instead of
  3161               // doing this scaling here...
  3162               // Multiplying by the reciprocal may introduce tiny error here,
  3163               // but we assume cairo is going to round coordinates at some stage
  3164               // and this is faster
  3165               glyph = glyphs.AppendGlyph();
  3166               glyph->mIndex = glyphData->GetSimpleGlyph();
  3167               glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  3168               glyph->mPosition.y = ToDeviceUnits(y, devUnitsPerAppUnit);
  3169               glyph->mPosition = matInv * glyph->mPosition;
  3170               glyphs.Flush(dt, aContextPaint, scaledFont,
  3171                            aDrawMode, isRTL, renderingOptions,
  3172                            aContext, passedInvMatrix,
  3173                            drawOptions);
  3175               // synthetic bolding by multi-striking with 1-pixel offsets
  3176               // at least once, more if there's room (large font sizes)
  3177               if (IsSyntheticBold()) {
  3178                   double strikeOffset = synBoldOnePixelOffset;
  3179                   int32_t strikeCount = strikes;
  3180                   do {
  3181                       Glyph *doubleglyph;
  3182                       doubleglyph = glyphs.AppendGlyph();
  3183                       doubleglyph->mIndex = glyph->mIndex;
  3184                       doubleglyph->mPosition.x =
  3185                           ToDeviceUnits(glyphX + strikeOffset * appUnitsPerDevUnit,
  3186                                         devUnitsPerAppUnit);
  3187                       doubleglyph->mPosition.y = glyph->mPosition.y;
  3188                       doubleglyph->mPosition = matInv * doubleglyph->mPosition;
  3189                       strikeOffset += synBoldOnePixelOffset;
  3190                       glyphs.Flush(dt, aContextPaint, scaledFont,
  3191                                    aDrawMode, isRTL, renderingOptions,
  3192                                    aContext, passedInvMatrix,
  3193                                    drawOptions);
  3194                   } while (--strikeCount > 0);
  3196               emittedGlyphs = true;
  3197           } else {
  3198               uint32_t glyphCount = glyphData->GetGlyphCount();
  3199               if (glyphCount > 0) {
  3200                   const gfxTextRun::DetailedGlyph *details =
  3201                       aTextRun->GetDetailedGlyphs(i);
  3202                   NS_ASSERTION(details, "detailedGlyph should not be missing!");
  3203                   double advance;
  3204                   for (uint32_t j = 0; j < glyphCount; ++j, ++details, x += direction * advance) {
  3205                       advance = details->mAdvance;
  3206                       if (glyphData->IsMissing()) {
  3207                           // default ignorable characters will have zero advance width.
  3208                           // we don't have to draw the hexbox for them
  3209                           if (aDrawMode != DrawMode::GLYPH_PATH && advance > 0) {
  3210                               double glyphX = x;
  3211                               if (isRTL) {
  3212                                   glyphX -= advance;
  3214                               gfxPoint pt(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  3215                                           ToDeviceUnits(y, devUnitsPerAppUnit));
  3216                               gfxFloat advanceDevUnits = ToDeviceUnits(advance, devUnitsPerAppUnit);
  3217                               gfxFloat height = GetMetrics().maxAscent;
  3218                               gfxRect glyphRect(pt.x, pt.y - height, advanceDevUnits, height);
  3219                               gfxFontMissingGlyphs::DrawMissingGlyph(aContext,
  3220                                                                      glyphRect,
  3221                                                                      details->mGlyphID,
  3222                                                                      appUnitsPerDevUnit);
  3224                       } else {
  3225                           double glyphX = x + details->mXOffset;
  3226                           if (isRTL) {
  3227                               glyphX -= advance;
  3230                           gfxPoint point(ToDeviceUnits(glyphX, devUnitsPerAppUnit),
  3231                                          ToDeviceUnits(y, devUnitsPerAppUnit));
  3233                           if (haveSVGGlyphs) {
  3234                               if (!paintSVGGlyphs) {
  3235                                   continue;
  3237                               DrawMode mode = ForcePaintingDrawMode(aDrawMode);
  3238                               if (RenderSVGGlyph(aContext, point, mode,
  3239                                                  details->mGlyphID,
  3240                                                  aContextPaint, aCallbacks,
  3241                                                  emittedGlyphs)) {
  3242                                   continue;
  3246                           glyph = glyphs.AppendGlyph();
  3247                           glyph->mIndex = details->mGlyphID;
  3248                           glyph->mPosition.x = ToDeviceUnits(glyphX, devUnitsPerAppUnit);
  3249                           glyph->mPosition.y = ToDeviceUnits(y + details->mYOffset, devUnitsPerAppUnit);
  3250                           glyph->mPosition = matInv * glyph->mPosition;
  3251                           glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode,
  3252                                        isRTL, renderingOptions, aContext, passedInvMatrix,
  3253                                        drawOptions);
  3255                           if (IsSyntheticBold()) {
  3256                               double strikeOffset = synBoldOnePixelOffset;
  3257                               int32_t strikeCount = strikes;
  3258                               do {
  3259                                   Glyph *doubleglyph;
  3260                                   doubleglyph = glyphs.AppendGlyph();
  3261                                   doubleglyph->mIndex = glyph->mIndex;
  3262                                   doubleglyph->mPosition.x =
  3263                                       ToDeviceUnits(glyphX + strikeOffset *
  3264                                                     appUnitsPerDevUnit,
  3265                                                     devUnitsPerAppUnit);
  3266                                   doubleglyph->mPosition.y = glyph->mPosition.y;
  3267                                   strikeOffset += synBoldOnePixelOffset;
  3268                                   doubleglyph->mPosition = matInv * doubleglyph->mPosition;
  3269                                   glyphs.Flush(dt, aContextPaint, scaledFont,
  3270                                                aDrawMode, isRTL, renderingOptions,
  3271                                                aContext, passedInvMatrix, drawOptions);
  3272                               } while (--strikeCount > 0);
  3274                           emittedGlyphs = true;
  3280           if (aSpacing) {
  3281               double space = aSpacing[i - aStart].mAfter;
  3282               if (i + 1 < aEnd) {
  3283                   space += aSpacing[i + 1 - aStart].mBefore;
  3285               x += direction*space;
  3289       glyphs.Flush(dt, aContextPaint, scaledFont, aDrawMode, isRTL,
  3290                    renderingOptions, aContext, passedInvMatrix,
  3291                    drawOptions, true);
  3292       if (aCallbacks && emittedGlyphs) {
  3293           aCallbacks->NotifyGlyphPathEmitted();
  3296       dt->SetTransform(oldMat);
  3298       dt->SetPermitSubpixelAA(oldSubpixelAA);
  3301     *aPt = gfxPoint(x, y);
  3304 bool
  3305 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
  3306                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint)
  3308     if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
  3309         return false;
  3312     const gfxFloat devUnitsPerSVGUnit =
  3313         GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
  3314     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
  3316     aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
  3317     aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
  3319     aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
  3321     return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode),
  3322                                           aContextPaint);
  3325 bool
  3326 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
  3327                         uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
  3328                         gfxTextRunDrawCallbacks *aCallbacks,
  3329                         bool& aEmittedGlyphs)
  3331     if (aCallbacks) {
  3332         if (aEmittedGlyphs) {
  3333             aCallbacks->NotifyGlyphPathEmitted();
  3334             aEmittedGlyphs = false;
  3336         aCallbacks->NotifyBeforeSVGGlyphPainted();
  3338     bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId,
  3339                                    aContextPaint);
  3340     if (aCallbacks) {
  3341         aCallbacks->NotifyAfterSVGGlyphPainted();
  3343     return rendered;
  3346 static void
  3347 UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
  3349     *aDestMin = std::min(*aDestMin, aX);
  3350     *aDestMax = std::max(*aDestMax, aX);
  3353 // We get precise glyph extents if the textrun creator requested them, or
  3354 // if the font is a user font --- in which case the author may be relying
  3355 // on overflowing glyphs.
  3356 static bool
  3357 NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
  3359     return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
  3360         aFont->GetFontEntry()->IsUserFont();
  3363 static bool
  3364 NeedsGlyphExtents(gfxTextRun *aTextRun)
  3366     if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
  3367         return true;
  3368     uint32_t numRuns;
  3369     const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
  3370     for (uint32_t i = 0; i < numRuns; ++i) {
  3371         if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
  3372             return true;
  3374     return false;
  3377 gfxFont::RunMetrics
  3378 gfxFont::Measure(gfxTextRun *aTextRun,
  3379                  uint32_t aStart, uint32_t aEnd,
  3380                  BoundingBoxType aBoundingBoxType,
  3381                  gfxContext *aRefContext,
  3382                  Spacing *aSpacing)
  3384     // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
  3385     // and the underlying cairo font may be antialiased,
  3386     // we need to create a copy in order to avoid getting cached extents.
  3387     // This is only used by MathML layout at present.
  3388     if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
  3389         mAntialiasOption != kAntialiasNone) {
  3390         if (!mNonAAFont) {
  3391             mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
  3393         // if font subclass doesn't implement CopyWithAntialiasOption(),
  3394         // it will return null and we'll proceed to use the existing font
  3395         if (mNonAAFont) {
  3396             return mNonAAFont->Measure(aTextRun, aStart, aEnd,
  3397                                        TIGHT_HINTED_OUTLINE_EXTENTS,
  3398                                        aRefContext, aSpacing);
  3402     const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  3403     // Current position in appunits
  3404     const gfxFont::Metrics& fontMetrics = GetMetrics();
  3406     RunMetrics metrics;
  3407     metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
  3408     metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
  3409     if (aStart == aEnd) {
  3410         // exit now before we look at aSpacing[0], which is undefined
  3411         metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
  3412         return metrics;
  3415     gfxFloat advanceMin = 0, advanceMax = 0;
  3416     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  3417     bool isRTL = aTextRun->IsRightToLeft();
  3418     double direction = aTextRun->GetDirection();
  3419     bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
  3420     gfxGlyphExtents *extents =
  3421         (aBoundingBoxType == LOOSE_INK_EXTENTS &&
  3422             !needsGlyphExtents &&
  3423             !aTextRun->HasDetailedGlyphs()) ? nullptr
  3424         : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
  3425     double x = 0;
  3426     if (aSpacing) {
  3427         x += direction*aSpacing[0].mBefore;
  3429     uint32_t i;
  3430     for (i = aStart; i < aEnd; ++i) {
  3431         const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
  3432         if (glyphData->IsSimpleGlyph()) {
  3433             double advance = glyphData->GetSimpleAdvance();
  3434             // Only get the real glyph horizontal extent if we were asked
  3435             // for the tight bounding box or we're in quality mode
  3436             if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
  3437                 extents) {
  3438                 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  3439                 uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
  3440                 if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
  3441                     aBoundingBoxType == LOOSE_INK_EXTENTS) {
  3442                     UnionRange(x, &advanceMin, &advanceMax);
  3443                     UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
  3444                 } else {
  3445                     gfxRect glyphRect;
  3446                     if (!extents->GetTightGlyphExtentsAppUnits(this,
  3447                             aRefContext, glyphIndex, &glyphRect)) {
  3448                         glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
  3449                             advance, metrics.mBoundingBox.Height());
  3451                     if (isRTL) {
  3452                         glyphRect -= gfxPoint(advance, 0);
  3454                     glyphRect += gfxPoint(x, 0);
  3455                     metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
  3458             x += direction*advance;
  3459         } else {
  3460             uint32_t glyphCount = glyphData->GetGlyphCount();
  3461             if (glyphCount > 0) {
  3462                 const gfxTextRun::DetailedGlyph *details =
  3463                     aTextRun->GetDetailedGlyphs(i);
  3464                 NS_ASSERTION(details != nullptr,
  3465                              "detaiedGlyph record should not be missing!");
  3466                 uint32_t j;
  3467                 for (j = 0; j < glyphCount; ++j, ++details) {
  3468                     uint32_t glyphIndex = details->mGlyphID;
  3469                     gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
  3470                     double advance = details->mAdvance;
  3471                     gfxRect glyphRect;
  3472                     if (glyphData->IsMissing() || !extents ||
  3473                         !extents->GetTightGlyphExtentsAppUnits(this,
  3474                                 aRefContext, glyphIndex, &glyphRect)) {
  3475                         // We might have failed to get glyph extents due to
  3476                         // OOM or something
  3477                         glyphRect = gfxRect(0, -metrics.mAscent,
  3478                             advance, metrics.mAscent + metrics.mDescent);
  3480                     if (isRTL) {
  3481                         glyphRect -= gfxPoint(advance, 0);
  3483                     glyphRect += gfxPoint(x, 0);
  3484                     metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
  3485                     x += direction*advance;
  3489         // Every other glyph type is ignored
  3490         if (aSpacing) {
  3491             double space = aSpacing[i - aStart].mAfter;
  3492             if (i + 1 < aEnd) {
  3493                 space += aSpacing[i + 1 - aStart].mBefore;
  3495             x += direction*space;
  3499     if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
  3500         UnionRange(x, &advanceMin, &advanceMax);
  3501         gfxRect fontBox(advanceMin, -metrics.mAscent,
  3502                         advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
  3503         metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
  3505     if (isRTL) {
  3506         metrics.mBoundingBox -= gfxPoint(x, 0);
  3509     metrics.mAdvanceWidth = x*direction;
  3510     return metrics;
  3513 static PLDHashOperator
  3514 NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
  3515                            void* aClosure)
  3517     aKey->GetKey()->NotifyGlyphsChanged();
  3518     return PL_DHASH_NEXT;
  3521 void
  3522 gfxFont::NotifyGlyphsChanged()
  3524     uint32_t i, count = mGlyphExtentsArray.Length();
  3525     for (i = 0; i < count; ++i) {
  3526         // Flush cached extents array
  3527         mGlyphExtentsArray[i]->NotifyGlyphsChanged();
  3530     if (mGlyphChangeObservers) {
  3531         mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr);
  3535 static bool
  3536 IsBoundarySpace(char16_t aChar, char16_t aNextChar)
  3538     return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
  3541 static inline uint32_t
  3542 HashMix(uint32_t aHash, char16_t aCh)
  3544     return (aHash >> 28) ^ (aHash << 4) ^ aCh;
  3547 #ifdef __GNUC__
  3548 #define GFX_MAYBE_UNUSED __attribute__((unused))
  3549 #else
  3550 #define GFX_MAYBE_UNUSED
  3551 #endif
  3553 template<typename T>
  3554 gfxShapedWord*
  3555 gfxFont::GetShapedWord(gfxContext *aContext,
  3556                        const T    *aText,
  3557                        uint32_t    aLength,
  3558                        uint32_t    aHash,
  3559                        int32_t     aRunScript,
  3560                        int32_t     aAppUnitsPerDevUnit,
  3561                        uint32_t    aFlags,
  3562                        gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
  3564     // if the cache is getting too big, flush it and start over
  3565     uint32_t wordCacheMaxEntries =
  3566         gfxPlatform::GetPlatform()->WordCacheMaxEntries();
  3567     if (mWordCache->Count() > wordCacheMaxEntries) {
  3568         NS_WARNING("flushing shaped-word cache");
  3569         ClearCachedWords();
  3572     // if there's a cached entry for this word, just return it
  3573     CacheHashKey key(aText, aLength, aHash,
  3574                      aRunScript,
  3575                      aAppUnitsPerDevUnit,
  3576                      aFlags);
  3578     CacheHashEntry *entry = mWordCache->PutEntry(key);
  3579     if (!entry) {
  3580         NS_WARNING("failed to create word cache entry - expect missing text");
  3581         return nullptr;
  3583     gfxShapedWord *sw = entry->mShapedWord;
  3585     bool isContent = !mStyle.systemFont;
  3587     if (sw) {
  3588         sw->ResetAge();
  3589         Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
  3590                                    Telemetry::WORD_CACHE_HITS_CHROME),
  3591                               aLength);
  3592 #ifndef RELEASE_BUILD
  3593         if (aTextPerf) {
  3594             aTextPerf->current.wordCacheHit++;
  3596 #endif
  3597         return sw;
  3600     Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
  3601                                Telemetry::WORD_CACHE_MISSES_CHROME),
  3602                           aLength);
  3603 #ifndef RELEASE_BUILD
  3604     if (aTextPerf) {
  3605         aTextPerf->current.wordCacheMiss++;
  3607 #endif
  3609     sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
  3610                                                     aRunScript,
  3611                                                     aAppUnitsPerDevUnit,
  3612                                                     aFlags);
  3613     if (!sw) {
  3614         NS_WARNING("failed to create gfxShapedWord - expect missing text");
  3615         return nullptr;
  3618     DebugOnly<bool> ok =
  3619         ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
  3621     NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
  3623     return sw;
  3626 bool
  3627 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
  3629     const gfxShapedWord *sw = mShapedWord;
  3630     if (!sw) {
  3631         return false;
  3633     if (sw->GetLength() != aKey->mLength ||
  3634         sw->Flags() != aKey->mFlags ||
  3635         sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
  3636         sw->Script() != aKey->mScript) {
  3637         return false;
  3639     if (sw->TextIs8Bit()) {
  3640         if (aKey->mTextIs8Bit) {
  3641             return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
  3642                                 aKey->mLength * sizeof(uint8_t)));
  3644         // The key has 16-bit text, even though all the characters are < 256,
  3645         // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
  3646         // comparing with will have 8-bit text.
  3647         const uint8_t   *s1 = sw->Text8Bit();
  3648         const char16_t *s2 = aKey->mText.mDouble;
  3649         const char16_t *s2end = s2 + aKey->mLength;
  3650         while (s2 < s2end) {
  3651             if (*s1++ != *s2++) {
  3652                 return false;
  3655         return true;
  3657     NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
  3658                  !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
  3659     return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
  3660                         aKey->mLength * sizeof(char16_t)));
  3663 bool
  3664 gfxFont::ShapeText(gfxContext    *aContext,
  3665                    const uint8_t *aText,
  3666                    uint32_t       aOffset,
  3667                    uint32_t       aLength,
  3668                    int32_t        aScript,
  3669                    gfxShapedText *aShapedText,
  3670                    bool           aPreferPlatformShaping)
  3672     nsDependentCSubstring ascii((const char*)aText, aLength);
  3673     nsAutoString utf16;
  3674     AppendASCIItoUTF16(ascii, utf16);
  3675     if (utf16.Length() != aLength) {
  3676         return false;
  3678     return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
  3679                      aScript, aShapedText, aPreferPlatformShaping);
  3682 bool
  3683 gfxFont::ShapeText(gfxContext      *aContext,
  3684                    const char16_t *aText,
  3685                    uint32_t         aOffset,
  3686                    uint32_t         aLength,
  3687                    int32_t          aScript,
  3688                    gfxShapedText   *aShapedText,
  3689                    bool             aPreferPlatformShaping)
  3691     bool ok = false;
  3693     if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
  3694         ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
  3695                                         aScript, aShapedText);
  3698     if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) {
  3699         if (gfxPlatform::GetPlatform()->UseHarfBuzzForScript(aScript)) {
  3700             ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
  3701                                             aScript, aShapedText);
  3705     if (!ok) {
  3706         if (!mPlatformShaper) {
  3707             CreatePlatformShaper();
  3708             NS_ASSERTION(mPlatformShaper, "no platform shaper available!");
  3710         if (mPlatformShaper) {
  3711             ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength,
  3712                                             aScript, aShapedText);
  3716     PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
  3718     return ok;
  3721 void
  3722 gfxFont::PostShapingFixup(gfxContext      *aContext,
  3723                           const char16_t *aText,
  3724                           uint32_t         aOffset,
  3725                           uint32_t         aLength,
  3726                           gfxShapedText   *aShapedText)
  3728     if (IsSyntheticBold()) {
  3729         float synBoldOffset =
  3730                 GetSyntheticBoldOffset() * CalcXScale(aContext);
  3731         aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
  3732                                                     aOffset, aLength);
  3736 #define MAX_SHAPING_LENGTH  32760 // slightly less than 32K, trying to avoid
  3737                                   // over-stressing platform shapers
  3738 #define BACKTRACK_LIMIT     16 // backtrack this far looking for a good place
  3739                                // to split into fragments for separate shaping
  3741 template<typename T>
  3742 bool
  3743 gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
  3744                                        const T    *aText,
  3745                                        uint32_t    aOffset,
  3746                                        uint32_t    aLength,
  3747                                        int32_t     aScript,
  3748                                        gfxTextRun *aTextRun)
  3750     aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
  3752     bool ok = true;
  3754     while (ok && aLength > 0) {
  3755         uint32_t fragLen = aLength;
  3757         // limit the length of text we pass to shapers in a single call
  3758         if (fragLen > MAX_SHAPING_LENGTH) {
  3759             fragLen = MAX_SHAPING_LENGTH;
  3761             // in the 8-bit case, there are no multi-char clusters,
  3762             // so we don't need to do this check
  3763             if (sizeof(T) == sizeof(char16_t)) {
  3764                 uint32_t i;
  3765                 for (i = 0; i < BACKTRACK_LIMIT; ++i) {
  3766                     if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
  3767                         fragLen -= i;
  3768                         break;
  3771                 if (i == BACKTRACK_LIMIT) {
  3772                     // if we didn't find any cluster start while backtracking,
  3773                     // just check that we're not in the middle of a surrogate
  3774                     // pair; back up by one code unit if we are.
  3775                     if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
  3776                         NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
  3777                         --fragLen;
  3783         ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
  3785         aText += fragLen;
  3786         aOffset += fragLen;
  3787         aLength -= fragLen;
  3790     return ok;
  3793 // Check if aCh is an unhandled control character that should be displayed
  3794 // as a hexbox rather than rendered by some random font on the system.
  3795 // We exclude \r as stray &#13;s are rather common (bug 941940).
  3796 // Note that \n and \t don't come through here, as they have specific
  3797 // meanings that have already been handled.
  3798 static bool
  3799 IsInvalidControlChar(uint32_t aCh)
  3801     return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
  3804 template<typename T>
  3805 bool
  3806 gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
  3807                                    const T    *aText,
  3808                                    uint32_t    aOffset,
  3809                                    uint32_t    aLength,
  3810                                    int32_t     aScript,
  3811                                    gfxTextRun *aTextRun)
  3813     uint32_t fragStart = 0;
  3814     bool ok = true;
  3816     for (uint32_t i = 0; i <= aLength && ok; ++i) {
  3817         T ch = (i < aLength) ? aText[i] : '\n';
  3818         bool invalid = gfxFontGroup::IsInvalidChar(ch);
  3819         uint32_t length = i - fragStart;
  3821         // break into separate fragments when we hit an invalid char
  3822         if (!invalid) {
  3823             continue;
  3826         if (length > 0) {
  3827             ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
  3828                                                aOffset + fragStart, length,
  3829                                                aScript, aTextRun);
  3832         if (i == aLength) {
  3833             break;
  3836         // fragment was terminated by an invalid char: skip it,
  3837         // unless it's a control char that we want to show as a hexbox,
  3838         // but record where TAB or NEWLINE occur
  3839         if (ch == '\t') {
  3840             aTextRun->SetIsTab(aOffset + i);
  3841         } else if (ch == '\n') {
  3842             aTextRun->SetIsNewline(aOffset + i);
  3843         } else if (IsInvalidControlChar(ch) &&
  3844             !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
  3845             aTextRun->SetMissingGlyph(aOffset + i, ch, this);
  3847         fragStart = i + 1;
  3850     NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
  3851     return ok;
  3854 #ifndef RELEASE_BUILD
  3855 #define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
  3856 #else
  3857 #define TEXT_PERF_INCR(tp, m)
  3858 #endif
  3860 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
  3861 inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
  3863 inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
  3865     return memchr(aString, 0x20, aLen) != nullptr;
  3868 inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
  3870     for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
  3871         if (*ch == 0x20) {
  3872             return true;
  3875     return false;
  3878 template<typename T>
  3879 bool
  3880 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
  3881                              gfxTextRun *aTextRun,
  3882                              const T *aString,
  3883                              uint32_t aRunStart,
  3884                              uint32_t aRunLength,
  3885                              int32_t aRunScript)
  3887     if (aRunLength == 0) {
  3888         return true;
  3891     gfxTextPerfMetrics *tp = nullptr;
  3893 #ifndef RELEASE_BUILD
  3894     tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
  3895     if (tp) {
  3896         if (mStyle.systemFont) {
  3897             tp->current.numChromeTextRuns++;
  3898         } else {
  3899             tp->current.numContentTextRuns++;
  3901         tp->current.numChars += aRunLength;
  3902         if (aRunLength > tp->current.maxTextRunLen) {
  3903             tp->current.maxTextRunLen = aRunLength;
  3906 #endif
  3908     uint32_t wordCacheCharLimit =
  3909         gfxPlatform::GetPlatform()->WordCacheCharLimit();
  3911     // If spaces can participate in shaping (e.g. within lookups for automatic
  3912     // fractions), need to shape without using the word cache which segments
  3913     // textruns on space boundaries. Word cache can be used if the textrun
  3914     // is short enough to fit in the word cache and it lacks spaces.
  3915     if (SpaceMayParticipateInShaping(aRunScript)) {
  3916         if (aRunLength > wordCacheCharLimit ||
  3917             HasSpaces(aString + aRunStart, aRunLength)) {
  3918             TEXT_PERF_INCR(tp, wordCacheSpaceRules);
  3919             return ShapeTextWithoutWordCache(aContext, aString + aRunStart,
  3920                                              aRunStart, aRunLength, aRunScript,
  3921                                              aTextRun);
  3925     InitWordCache();
  3927     // the only flags we care about for ShapedWord construction/caching
  3928     uint32_t flags = aTextRun->GetFlags();
  3929     flags &= (gfxTextRunFactory::TEXT_IS_RTL |
  3930               gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
  3931               gfxTextRunFactory::TEXT_USE_MATH_SCRIPT);
  3932     if (sizeof(T) == sizeof(uint8_t)) {
  3933         flags |= gfxTextRunFactory::TEXT_IS_8BIT;
  3936     const T *text = aString + aRunStart;
  3937     uint32_t wordStart = 0;
  3938     uint32_t hash = 0;
  3939     bool wordIs8Bit = true;
  3940     int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
  3942     T nextCh = text[0];
  3943     for (uint32_t i = 0; i <= aRunLength; ++i) {
  3944         T ch = nextCh;
  3945         nextCh = (i < aRunLength - 1) ? text[i + 1] : '\n';
  3946         bool boundary = IsBoundarySpace(ch, nextCh);
  3947         bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
  3948         uint32_t length = i - wordStart;
  3950         // break into separate ShapedWords when we hit an invalid char,
  3951         // or a boundary space (always handled individually),
  3952         // or the first non-space after a space
  3953         if (!boundary && !invalid) {
  3954             if (!IsChar8Bit(ch)) {
  3955                 wordIs8Bit = false;
  3957             // include this character in the hash, and move on to next
  3958             hash = HashMix(hash, ch);
  3959             continue;
  3962         // We've decided to break here (i.e. we're at the end of a "word");
  3963         // shape the word and add it to the textrun.
  3964         // For words longer than the limit, we don't use the
  3965         // font's word cache but just shape directly into the textrun.
  3966         if (length > wordCacheCharLimit) {
  3967             TEXT_PERF_INCR(tp, wordCacheLong);
  3968             bool ok = ShapeFragmentWithoutWordCache(aContext,
  3969                                                     text + wordStart,
  3970                                                     aRunStart + wordStart,
  3971                                                     length,
  3972                                                     aRunScript,
  3973                                                     aTextRun);
  3974             if (!ok) {
  3975                 return false;
  3977         } else if (length > 0) {
  3978             uint32_t wordFlags = flags;
  3979             // in the 8-bit version of this method, TEXT_IS_8BIT was
  3980             // already set as part of |flags|, so no need for a per-word
  3981             // adjustment here
  3982             if (sizeof(T) == sizeof(char16_t)) {
  3983                 if (wordIs8Bit) {
  3984                     wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
  3987             gfxShapedWord *sw = GetShapedWord(aContext,
  3988                                               text + wordStart, length,
  3989                                               hash, aRunScript,
  3990                                               appUnitsPerDevUnit,
  3991                                               wordFlags, tp);
  3992             if (sw) {
  3993                 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
  3994             } else {
  3995                 return false; // failed, presumably out of memory?
  3999         if (boundary) {
  4000             // word was terminated by a space: add that to the textrun
  4001             if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
  4002                                                  aRunStart + i, ch))
  4004                 static const uint8_t space = ' ';
  4005                 gfxShapedWord *sw =
  4006                     GetShapedWord(aContext,
  4007                                   &space, 1,
  4008                                   HashMix(0, ' '), aRunScript,
  4009                                   appUnitsPerDevUnit,
  4010                                   flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
  4011                 if (sw) {
  4012                     aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
  4013                 } else {
  4014                     return false;
  4017             hash = 0;
  4018             wordStart = i + 1;
  4019             wordIs8Bit = true;
  4020             continue;
  4023         if (i == aRunLength) {
  4024             break;
  4027         NS_ASSERTION(invalid,
  4028                      "how did we get here except via an invalid char?");
  4030         // word was terminated by an invalid char: skip it,
  4031         // unless it's a control char that we want to show as a hexbox,
  4032         // but record where TAB or NEWLINE occur
  4033         if (ch == '\t') {
  4034             aTextRun->SetIsTab(aRunStart + i);
  4035         } else if (ch == '\n') {
  4036             aTextRun->SetIsNewline(aRunStart + i);
  4037         } else if (IsInvalidControlChar(ch) &&
  4038             !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
  4039             aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
  4042         hash = 0;
  4043         wordStart = i + 1;
  4044         wordIs8Bit = true;
  4047     return true;
  4050 gfxGlyphExtents *
  4051 gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
  4052     uint32_t i, count = mGlyphExtentsArray.Length();
  4053     for (i = 0; i < count; ++i) {
  4054         if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
  4055             return mGlyphExtentsArray[i];
  4057     gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
  4058     if (glyphExtents) {
  4059         mGlyphExtentsArray.AppendElement(glyphExtents);
  4060         // Initialize the extents of a space glyph, assuming that spaces don't
  4061         // render anything!
  4062         glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
  4064     return glyphExtents;
  4067 void
  4068 gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight,
  4069                            gfxGlyphExtents *aExtents)
  4071     gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
  4072     aContext->IdentityMatrix();
  4074     gfxRect svgBounds;
  4075     if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
  4076         mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
  4077         gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
  4078         aExtents->SetTightGlyphExtents(aGlyphID,
  4079                                        gfxRect(svgBounds.x * d2a,
  4080                                                svgBounds.y * d2a,
  4081                                                svgBounds.width * d2a,
  4082                                                svgBounds.height * d2a));
  4083         return;
  4086     cairo_glyph_t glyph;
  4087     glyph.index = aGlyphID;
  4088     glyph.x = 0;
  4089     glyph.y = 0;
  4090     cairo_text_extents_t extents;
  4091     cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
  4093     const Metrics& fontMetrics = GetMetrics();
  4094     int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
  4095     if (!aNeedTight && extents.x_bearing >= 0 &&
  4096         extents.y_bearing >= -fontMetrics.maxAscent &&
  4097         extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
  4098         uint32_t appUnitsWidth =
  4099             uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
  4100         if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
  4101             aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
  4102             return;
  4105 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  4106     if (!aNeedTight) {
  4107         ++gGlyphExtentsSetupFallBackToTight;
  4109 #endif
  4111     gfxFloat d2a = appUnitsPerDevUnit;
  4112     gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
  4113                    extents.width*d2a, extents.height*d2a);
  4114     aExtents->SetTightGlyphExtents(aGlyphID, bounds);
  4117 // Try to initialize font metrics by reading sfnt tables directly;
  4118 // set mIsValid=TRUE and return TRUE on success.
  4119 // Return FALSE if the gfxFontEntry subclass does not
  4120 // implement GetFontTable(), or for non-sfnt fonts where tables are
  4121 // not available.
  4122 // If this returns TRUE without setting the mIsValid flag, then we -did-
  4123 // apparently find an sfnt, but it was too broken to be used.
  4124 bool
  4125 gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
  4127     mIsValid = false; // font is NOT valid in case of early return
  4129     const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
  4130     const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
  4131     const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
  4133     uint32_t len;
  4135     if (mFUnitsConvFactor == 0.0) {
  4136         // If the conversion factor from FUnits is not yet set,
  4137         // get the unitsPerEm from the 'head' table via the font entry
  4138         uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
  4139         if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
  4140             return false;
  4142         mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
  4145     // 'hhea' table is required to get vertical extents
  4146     gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
  4147     if (!hheaTable) {
  4148         return false; // no 'hhea' table -> not an sfnt
  4150     const HheaTable* hhea =
  4151         reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
  4152     if (len < sizeof(HheaTable)) {
  4153         return false;
  4156 #define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
  4157 #define SET_SIGNED(field,src)   aMetrics.field = int16_t(src) * mFUnitsConvFactor
  4159     SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
  4160     SET_SIGNED(maxAscent, hhea->ascender);
  4161     SET_SIGNED(maxDescent, -int16_t(hhea->descender));
  4162     SET_SIGNED(externalLeading, hhea->lineGap);
  4164     // 'post' table is required for underline metrics
  4165     gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
  4166     if (!postTable) {
  4167         return true; // no 'post' table -> sfnt is not valid
  4169     const PostTable *post =
  4170         reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
  4171     if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
  4172         return true; // bad post table -> sfnt is not valid
  4175     SET_SIGNED(underlineOffset, post->underlinePosition);
  4176     SET_UNSIGNED(underlineSize, post->underlineThickness);
  4178     // 'OS/2' table is optional, if not found we'll estimate xHeight
  4179     // and aveCharWidth by measuring glyphs
  4180     gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
  4181     if (os2Table) {
  4182         const OS2Table *os2 =
  4183             reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
  4184         if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
  4185             uint16_t(os2->version) >= 2) {
  4186             // version 2 and later includes the x-height field
  4187             SET_SIGNED(xHeight, os2->sxHeight);
  4188             // Abs because of negative xHeight seen in Kokonor (Tibetan) font
  4189             aMetrics.xHeight = Abs(aMetrics.xHeight);
  4191         // this should always be present in any valid OS/2 of any version
  4192         if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
  4193             SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
  4194             SET_SIGNED(subscriptOffset, os2->ySubscriptYOffset);
  4195             SET_SIGNED(superscriptOffset, os2->ySuperscriptYOffset);
  4196             SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
  4197             SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
  4199             // for fonts with USE_TYPO_METRICS set in the fsSelection field,
  4200             // and for all OpenType math fonts (having a 'MATH' table),
  4201             // let the OS/2 sTypo* metrics override those from the hhea table
  4202             // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
  4203             const uint16_t kUseTypoMetricsMask = 1 << 7;
  4204             if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
  4205                 mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
  4206                 SET_SIGNED(maxAscent, os2->sTypoAscender);
  4207                 SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
  4208                 SET_SIGNED(externalLeading, os2->sTypoLineGap);
  4213     mIsValid = true;
  4215     return true;
  4218 static double
  4219 RoundToNearestMultiple(double aValue, double aFraction)
  4221     return floor(aValue/aFraction + 0.5) * aFraction;
  4224 void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
  4226     aMetrics.maxAscent =
  4227         ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
  4228     aMetrics.maxDescent =
  4229         ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
  4231     if (aMetrics.xHeight <= 0) {
  4232         // only happens if we couldn't find either font metrics
  4233         // or a char to measure;
  4234         // pick an arbitrary value that's better than zero
  4235         aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
  4238     aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
  4240     if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
  4241         aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
  4242     } else {
  4243         aMetrics.internalLeading = 0.0;
  4246     aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
  4247                             / aMetrics.maxHeight;
  4248     aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
  4250     if (GetFontEntry()->IsFixedPitch()) {
  4251         // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
  4252         // advance than the average character width... this forces
  4253         // those fonts to be recognized like fixed pitch fonts by layout.
  4254         aMetrics.maxAdvance = aMetrics.aveCharWidth;
  4257     if (!aMetrics.subscriptOffset) {
  4258         aMetrics.subscriptOffset = aMetrics.xHeight;
  4260     if (!aMetrics.superscriptOffset) {
  4261         aMetrics.superscriptOffset = aMetrics.xHeight;
  4264     if (!aMetrics.strikeoutOffset) {
  4265         aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
  4267     if (!aMetrics.strikeoutSize) {
  4268         aMetrics.strikeoutSize = aMetrics.underlineSize;
  4272 void
  4273 gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
  4275     // Even if this font size is zero, this font is created with non-zero size.
  4276     // However, for layout and others, we should return the metrics of zero size font.
  4277     if (mStyle.size == 0.0) {
  4278         memset(aMetrics, 0, sizeof(gfxFont::Metrics));
  4279         return;
  4282     // MS (P)Gothic and MS (P)Mincho are not having suitable values in their super script offset.
  4283     // If the values are not suitable, we should use x-height instead of them.
  4284     // See https://bugzilla.mozilla.org/show_bug.cgi?id=353632
  4285     if (aMetrics->superscriptOffset <= 0 ||
  4286         aMetrics->superscriptOffset >= aMetrics->maxAscent) {
  4287         aMetrics->superscriptOffset = aMetrics->xHeight;
  4289     // And also checking the case of sub script offset. The old gfx for win has checked this too.
  4290     if (aMetrics->subscriptOffset <= 0 ||
  4291         aMetrics->subscriptOffset >= aMetrics->maxAscent) {
  4292         aMetrics->subscriptOffset = aMetrics->xHeight;
  4295     aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
  4296     aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
  4298     aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
  4300     if (aMetrics->maxAscent < 1.0) {
  4301         // We cannot draw strikeout line and overline in the ascent...
  4302         aMetrics->underlineSize = 0;
  4303         aMetrics->underlineOffset = 0;
  4304         aMetrics->strikeoutSize = 0;
  4305         aMetrics->strikeoutOffset = 0;
  4306         return;
  4309     /**
  4310      * Some CJK fonts have bad underline offset. Therefore, if this is such font,
  4311      * we need to lower the underline offset to bottom of *em* descent.
  4312      * However, if this is system font, we should not do this for the rendering compatibility with
  4313      * another application's UI on the platform.
  4314      * XXX Should not use this hack if the font size is too small?
  4315      *     Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
  4316      */
  4317     if (!mStyle.systemFont && aIsBadUnderlineFont) {
  4318         // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
  4319         // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
  4320         aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
  4322         // Next, we put the underline to bottom of below of the descent space.
  4323         if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
  4324             aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
  4325         } else {
  4326             aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
  4327                                                aMetrics->underlineSize - aMetrics->emDescent);
  4330     // If underline positioned is too far from the text, descent position is preferred so that underline
  4331     // will stay within the boundary.
  4332     else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
  4333         if (aMetrics->underlineSize > aMetrics->maxDescent)
  4334             aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
  4335         // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
  4336         aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
  4339     // If strikeout line is overflowed from the ascent, the line should be resized and moved for
  4340     // that being in the ascent space.
  4341     // Note that the strikeoutOffset is *middle* of the strikeout line position.
  4342     gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
  4343     if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
  4344         if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
  4345             aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
  4346             halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
  4348         gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
  4349         aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
  4352     // If overline is larger than the ascent, the line should be resized.
  4353     if (aMetrics->underlineSize > aMetrics->maxAscent) {
  4354         aMetrics->underlineSize = aMetrics->maxAscent;
  4358 gfxFloat
  4359 gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
  4361     // return an appropriate width for various Unicode space characters
  4362     // that we "fake" if they're not actually present in the font;
  4363     // returns negative value if the char is not a known space.
  4364     switch (aCh) {
  4365     case 0x2000:                                 // en quad
  4366     case 0x2002: return GetAdjustedSize() / 2;   // en space
  4367     case 0x2001:                                 // em quad
  4368     case 0x2003: return GetAdjustedSize();       // em space
  4369     case 0x2004: return GetAdjustedSize() / 3;   // three-per-em space
  4370     case 0x2005: return GetAdjustedSize() / 4;   // four-per-em space
  4371     case 0x2006: return GetAdjustedSize() / 6;   // six-per-em space
  4372     case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space
  4373     case 0x2008: return GetMetrics().spaceWidth; // punctuation space 
  4374     case 0x2009: return GetAdjustedSize() / 5;   // thin space
  4375     case 0x200a: return GetAdjustedSize() / 10;  // hair space
  4376     case 0x202f: return GetAdjustedSize() / 5;   // narrow no-break space
  4377     default: return -1.0;
  4381 /*static*/ size_t
  4382 gfxFont::WordCacheEntrySizeOfExcludingThis(CacheHashEntry*   aHashEntry,
  4383                                            MallocSizeOf aMallocSizeOf,
  4384                                            void*             aUserArg)
  4386     return aMallocSizeOf(aHashEntry->mShapedWord.get());
  4389 void
  4390 gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
  4391                                 FontCacheSizes* aSizes) const
  4393     for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
  4394         aSizes->mFontInstances +=
  4395             mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
  4397     if (mWordCache) {
  4398         aSizes->mShapedWords +=
  4399             mWordCache->SizeOfExcludingThis(WordCacheEntrySizeOfExcludingThis,
  4400                                             aMallocSizeOf);
  4404 void
  4405 gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
  4406                                 FontCacheSizes* aSizes) const
  4408     aSizes->mFontInstances += aMallocSizeOf(this);
  4409     AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
  4412 void
  4413 gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
  4415     if (!mGlyphChangeObservers) {
  4416         mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >;
  4418     mGlyphChangeObservers->PutEntry(aObserver);
  4421 void
  4422 gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
  4424     NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
  4425     NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
  4426     mGlyphChangeObservers->RemoveEntry(aObserver);
  4429 gfxGlyphExtents::~gfxGlyphExtents()
  4431 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  4432     gGlyphExtentsWidthsTotalSize +=
  4433         mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
  4434     gGlyphExtentsCount++;
  4435 #endif
  4436     MOZ_COUNT_DTOR(gfxGlyphExtents);
  4439 bool
  4440 gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
  4441     gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
  4443     HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
  4444     if (!entry) {
  4445         if (!aContext) {
  4446             NS_WARNING("Could not get glyph extents (no aContext)");
  4447             return false;
  4450         if (aFont->SetupCairoFont(aContext)) {
  4451 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  4452             ++gGlyphExtentsSetupLazyTight;
  4453 #endif
  4454             aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
  4455             entry = mTightGlyphExtents.GetEntry(aGlyphID);
  4457         if (!entry) {
  4458             NS_WARNING("Could not get glyph extents");
  4459             return false;
  4463     *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
  4464     return true;
  4467 gfxGlyphExtents::GlyphWidths::~GlyphWidths()
  4469     uint32_t i, count = mBlocks.Length();
  4470     for (i = 0; i < count; ++i) {
  4471         uintptr_t bits = mBlocks[i];
  4472         if (bits && !(bits & 0x1)) {
  4473             delete[] reinterpret_cast<uint16_t *>(bits);
  4478 uint32_t
  4479 gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  4481     uint32_t i;
  4482     uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
  4483     for (i = 0; i < mBlocks.Length(); ++i) {
  4484         uintptr_t bits = mBlocks[i];
  4485         if (bits && !(bits & 0x1)) {
  4486             size += aMallocSizeOf(reinterpret_cast<void*>(bits));
  4489     return size;
  4492 void
  4493 gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
  4495     uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
  4496     uint32_t len = mBlocks.Length();
  4497     if (block >= len) {
  4498         uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
  4499         if (!elems)
  4500             return;
  4501         memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
  4504     uintptr_t bits = mBlocks[block];
  4505     uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
  4506     if (!bits) {
  4507         mBlocks[block] = MakeSingle(glyphOffset, aWidth);
  4508         return;
  4511     uint16_t *newBlock;
  4512     if (bits & 0x1) {
  4513         // Expand the block to a real block. We could avoid this by checking
  4514         // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
  4515         newBlock = new uint16_t[BLOCK_SIZE];
  4516         if (!newBlock)
  4517             return;
  4518         uint32_t i;
  4519         for (i = 0; i < BLOCK_SIZE; ++i) {
  4520             newBlock[i] = INVALID_WIDTH;
  4522         newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
  4523         mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
  4524     } else {
  4525         newBlock = reinterpret_cast<uint16_t *>(bits);
  4527     newBlock[glyphOffset] = aWidth;
  4530 void
  4531 gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
  4533     HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
  4534     if (!entry)
  4535         return;
  4536     entry->x = aExtentsAppUnits.X();
  4537     entry->y = aExtentsAppUnits.Y();
  4538     entry->width = aExtentsAppUnits.Width();
  4539     entry->height = aExtentsAppUnits.Height();
  4542 size_t
  4543 gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
  4545     return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
  4546         mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf);
  4549 size_t
  4550 gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
  4552     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  4555 gfxFontGroup::gfxFontGroup(const nsAString& aFamilies,
  4556                            const gfxFontStyle *aStyle,
  4557                            gfxUserFontSet *aUserFontSet)
  4558     : mFamilies(aFamilies)
  4559     , mStyle(*aStyle)
  4560     , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
  4561     , mHyphenWidth(-1)
  4562     , mUserFontSet(aUserFontSet)
  4563     , mTextPerf(nullptr)
  4564     , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
  4565     , mSkipDrawing(false)
  4567     // We don't use SetUserFontSet() here, as we want to unconditionally call
  4568     // BuildFontList() rather than only do UpdateFontList() if it changed.
  4569     mCurrGeneration = GetGeneration();
  4570     BuildFontList();
  4573 void
  4574 gfxFontGroup::BuildFontList()
  4576 // "#if" to be removed once all platforms are moved to gfxPlatformFontList interface
  4577 // and subclasses of gfxFontGroup eliminated
  4578 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
  4579     ForEachFont(FindPlatformFont, this);
  4581     if (mFonts.Length() == 0) {
  4582         bool needsBold;
  4583         gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
  4584         gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
  4585         NS_ASSERTION(defaultFamily,
  4586                      "invalid default font returned by GetDefaultFont");
  4588         if (defaultFamily) {
  4589             gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
  4590                                                                needsBold);
  4591             if (fe) {
  4592                 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
  4593                                                             needsBold);
  4594                 if (font) {
  4595                     mFonts.AppendElement(FamilyFace(defaultFamily, font));
  4600         if (mFonts.Length() == 0) {
  4601             // Try for a "font of last resort...."
  4602             // Because an empty font list would be Really Bad for later code
  4603             // that assumes it will be able to get valid metrics for layout,
  4604             // just look for the first usable font and put in the list.
  4605             // (see bug 554544)
  4606             nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
  4607             pfl->GetFontFamilyList(families);
  4608             uint32_t count = families.Length();
  4609             for (uint32_t i = 0; i < count; ++i) {
  4610                 gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
  4611                                                                  needsBold);
  4612                 if (fe) {
  4613                     nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
  4614                                                                 needsBold);
  4615                     if (font) {
  4616                         mFonts.AppendElement(FamilyFace(families[i], font));
  4617                         break;
  4623         if (mFonts.Length() == 0) {
  4624             // an empty font list at this point is fatal; we're not going to
  4625             // be able to do even the most basic layout operations
  4626             char msg[256]; // CHECK buffer length if revising message below
  4627             sprintf(msg, "unable to find a usable font (%.220s)",
  4628                     NS_ConvertUTF16toUTF8(mFamilies).get());
  4629             NS_RUNTIMEABORT(msg);
  4633     if (!mStyle.systemFont) {
  4634         uint32_t count = mFonts.Length();
  4635         for (uint32_t i = 0; i < count; ++i) {
  4636             gfxFont* font = mFonts[i].Font();
  4637             if (font->GetFontEntry()->mIsBadUnderlineFont) {
  4638                 gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
  4639                 gfxFloat bad = font->GetMetrics().underlineOffset;
  4640                 mUnderlineOffset = std::min(first, bad);
  4641                 break;
  4645 #endif
  4648 bool
  4649 gfxFontGroup::FindPlatformFont(const nsAString& aName,
  4650                                const nsACString& aGenericName,
  4651                                bool aUseFontSet,
  4652                                void *aClosure)
  4654     gfxFontGroup *fontGroup = static_cast<gfxFontGroup*>(aClosure);
  4655     const gfxFontStyle *fontStyle = fontGroup->GetStyle();
  4657     bool needsBold;
  4658     gfxFontFamily *family = nullptr;
  4659     gfxFontEntry *fe = nullptr;
  4661     if (aUseFontSet) {
  4662         // First, look up in the user font set...
  4663         // If the fontSet matches the family, we must not look for a platform
  4664         // font of the same name, even if we fail to actually get a fontEntry
  4665         // here; we'll fall back to the next name in the CSS font-family list.
  4666         gfxUserFontSet *fs = fontGroup->GetUserFontSet();
  4667         if (fs) {
  4668             // If the fontSet matches the family, but the font has not yet finished
  4669             // loading (nor has its load timeout fired), the fontGroup should wait
  4670             // for the download, and not actually draw its text yet.
  4671             family = fs->GetFamily(aName);
  4672             if (family) {
  4673                 bool waitForUserFont = false;
  4674                 fe = fs->FindFontEntry(family, *fontStyle,
  4675                                        needsBold, waitForUserFont);
  4676                 if (!fe && waitForUserFont) {
  4677                     fontGroup->mSkipDrawing = true;
  4683     // Not known in the user font set ==> check system fonts
  4684     // XXX: Fallback is bad..
  4685     if (!family) {
  4686         gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
  4687         family = fontList->FindFamily(aName);
  4688         if (family) {
  4689             fe = family->FindFontForStyle(*fontStyle, needsBold);
  4693     // add to the font group, unless it's already there
  4694     if (fe && !fontGroup->HasFont(fe)) {
  4695         nsRefPtr<gfxFont> font = fe->FindOrMakeFont(fontStyle, needsBold);
  4696         if (font) {
  4697             fontGroup->mFonts.AppendElement(FamilyFace(family, font));
  4701     return true;
  4704 bool
  4705 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
  4707     uint32_t count = mFonts.Length();
  4708     for (uint32_t i = 0; i < count; ++i) {
  4709         if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
  4710             return true;
  4712     return false;
  4715 gfxFontGroup::~gfxFontGroup()
  4717     mFonts.Clear();
  4720 gfxFontGroup *
  4721 gfxFontGroup::Copy(const gfxFontStyle *aStyle)
  4723     gfxFontGroup *fg = new gfxFontGroup(mFamilies, aStyle, mUserFontSet);
  4724     fg->SetTextPerfMetrics(mTextPerf);
  4725     return fg;
  4728 bool 
  4729 gfxFontGroup::IsInvalidChar(uint8_t ch)
  4731     return ((ch & 0x7f) < 0x20 || ch == 0x7f);
  4734 bool 
  4735 gfxFontGroup::IsInvalidChar(char16_t ch)
  4737     // All printable 7-bit ASCII values are OK
  4738     if (ch >= ' ' && ch < 0x7f) {
  4739         return false;
  4741     // No point in sending non-printing control chars through font shaping
  4742     if (ch <= 0x9f) {
  4743         return true;
  4745     return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
  4746              (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
  4747             IsBidiControl(ch));
  4750 bool
  4751 gfxFontGroup::ForEachFont(FontCreationCallback fc,
  4752                           void *closure)
  4754     return ForEachFontInternal(mFamilies, mStyle.language,
  4755                                true, true, true, fc, closure);
  4758 bool
  4759 gfxFontGroup::ForEachFont(const nsAString& aFamilies,
  4760                           nsIAtom *aLanguage,
  4761                           FontCreationCallback fc,
  4762                           void *closure)
  4764     return ForEachFontInternal(aFamilies, aLanguage,
  4765                                false, true, true, fc, closure);
  4768 struct ResolveData {
  4769     ResolveData(gfxFontGroup::FontCreationCallback aCallback,
  4770                 nsACString& aGenericFamily,
  4771                 bool aUseFontSet,
  4772                 void *aClosure) :
  4773         mCallback(aCallback),
  4774         mGenericFamily(aGenericFamily),
  4775         mUseFontSet(aUseFontSet),
  4776         mClosure(aClosure) {
  4778     gfxFontGroup::FontCreationCallback mCallback;
  4779     nsCString mGenericFamily;
  4780     bool mUseFontSet;
  4781     void *mClosure;
  4782 };
  4784 bool
  4785 gfxFontGroup::ForEachFontInternal(const nsAString& aFamilies,
  4786                                   nsIAtom *aLanguage,
  4787                                   bool aResolveGeneric,
  4788                                   bool aResolveFontName,
  4789                                   bool aUseFontSet,
  4790                                   FontCreationCallback fc,
  4791                                   void *closure)
  4793     const char16_t kSingleQuote  = char16_t('\'');
  4794     const char16_t kDoubleQuote  = char16_t('\"');
  4795     const char16_t kComma        = char16_t(',');
  4797     nsIAtom *groupAtom = nullptr;
  4798     nsAutoCString groupString;
  4799     if (aLanguage) {
  4800         if (!gLangService) {
  4801             CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
  4803         if (gLangService) {
  4804             nsresult rv;
  4805             groupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
  4808     if (!groupAtom) {
  4809         groupAtom = nsGkAtoms::Unicode;
  4811     groupAtom->ToUTF8String(groupString);
  4813     nsPromiseFlatString families(aFamilies);
  4814     const char16_t *p, *p_end;
  4815     families.BeginReading(p);
  4816     families.EndReading(p_end);
  4817     nsAutoString family;
  4818     nsAutoCString lcFamily;
  4819     nsAutoString genericFamily;
  4821     while (p < p_end) {
  4822         while (nsCRT::IsAsciiSpace(*p) || *p == kComma)
  4823             if (++p == p_end)
  4824                 return true;
  4826         bool generic;
  4827         if (*p == kSingleQuote || *p == kDoubleQuote) {
  4828             // quoted font family
  4829             char16_t quoteMark = *p;
  4830             if (++p == p_end)
  4831                 return true;
  4832             const char16_t *nameStart = p;
  4834             // XXX What about CSS character escapes?
  4835             while (*p != quoteMark)
  4836                 if (++p == p_end)
  4837                     return true;
  4839             family = Substring(nameStart, p);
  4840             generic = false;
  4841             genericFamily.SetIsVoid(true);
  4843             while (++p != p_end && *p != kComma)
  4844                 /* nothing */ ;
  4846         } else {
  4847             // unquoted font family
  4848             const char16_t *nameStart = p;
  4849             while (++p != p_end && *p != kComma)
  4850                 /* nothing */ ;
  4852             family = Substring(nameStart, p);
  4853             family.CompressWhitespace(false, true);
  4855             if (aResolveGeneric &&
  4856                 (family.LowerCaseEqualsLiteral("serif") ||
  4857                  family.LowerCaseEqualsLiteral("sans-serif") ||
  4858                  family.LowerCaseEqualsLiteral("monospace") ||
  4859                  family.LowerCaseEqualsLiteral("cursive") ||
  4860                  family.LowerCaseEqualsLiteral("fantasy")))
  4862                 generic = true;
  4864                 ToLowerCase(NS_LossyConvertUTF16toASCII(family), lcFamily);
  4866                 nsAutoCString prefName("font.name.");
  4867                 prefName.Append(lcFamily);
  4868                 prefName.AppendLiteral(".");
  4869                 prefName.Append(groupString);
  4871                 nsAdoptingString value = Preferences::GetString(prefName.get());
  4872                 if (value) {
  4873                     CopyASCIItoUTF16(lcFamily, genericFamily);
  4874                     family = value;
  4876             } else {
  4877                 generic = false;
  4878                 genericFamily.SetIsVoid(true);
  4882         NS_LossyConvertUTF16toASCII gf(genericFamily);
  4883         if (generic) {
  4884             ForEachFontInternal(family, groupAtom, false,
  4885                                 aResolveFontName, false,
  4886                                 fc, closure);
  4887         } else if (!family.IsEmpty()) {
  4888             if (aResolveFontName) {
  4889                 ResolveData data(fc, gf, aUseFontSet, closure);
  4890                 bool aborted = false, needsBold;
  4891                 nsresult rv = NS_OK;
  4892                 bool foundFamily = false;
  4893                 bool waitForUserFont = false;
  4894                 gfxFontEntry *fe = nullptr;
  4895                 if (aUseFontSet && mUserFontSet) {
  4896                     gfxFontFamily *fam = mUserFontSet->GetFamily(family);
  4897                     if (fam) {
  4898                         fe = mUserFontSet->FindFontEntry(fam, mStyle,
  4899                                                          needsBold,
  4900                                                          waitForUserFont);
  4903                 if (fe) {
  4904                     gfxFontGroup::FontResolverProc(family, &data);
  4905                 } else {
  4906                     if (waitForUserFont) {
  4907                         mSkipDrawing = true;
  4909                     if (!foundFamily) {
  4910                         gfxPlatform *pf = gfxPlatform::GetPlatform();
  4911                         // XXX: Fallback is bad
  4912                         rv = pf->ResolveFontName(family,
  4913                                                  gfxFontGroup::FontResolverProc,
  4914                                                  &data, aborted);
  4917                 if (NS_FAILED(rv) || aborted)
  4918                     return false;
  4920             else {
  4921                 if (!fc(family, gf, aUseFontSet, closure))
  4922                     return false;
  4926         if (generic && aResolveGeneric) {
  4927             nsAutoCString prefName("font.name-list.");
  4928             prefName.Append(lcFamily);
  4929             prefName.AppendLiteral(".");
  4930             prefName.Append(groupString);
  4931             nsAdoptingString value = Preferences::GetString(prefName.get());
  4932             if (value) {
  4933                 ForEachFontInternal(value, groupAtom, false,
  4934                                     aResolveFontName, false,
  4935                                     fc, closure);
  4939         ++p; // may advance past p_end
  4942     return true;
  4945 bool
  4946 gfxFontGroup::FontResolverProc(const nsAString& aName, void *aClosure)
  4948     ResolveData *data = reinterpret_cast<ResolveData*>(aClosure);
  4949     return (data->mCallback)(aName, data->mGenericFamily, data->mUseFontSet,
  4950                              data->mClosure);
  4953 gfxTextRun *
  4954 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
  4956     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
  4957     return gfxTextRun::Create(aParams, 0, this, aFlags);
  4960 gfxTextRun *
  4961 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
  4963     aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
  4965     gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
  4966     if (!textRun) {
  4967         return nullptr;
  4970     gfxFont *font = GetFontAt(0);
  4971     if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
  4972         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
  4973         // them, and always create at least size 1 fonts, i.e. they still
  4974         // render something for size 0 fonts.
  4975         textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
  4977     else {
  4978         if (font->GetSpaceGlyph()) {
  4979             // Normally, the font has a cached space glyph, so we can avoid
  4980             // the cost of calling FindFontForChar.
  4981             textRun->SetSpaceGlyph(font, aParams->mContext, 0);
  4982         } else {
  4983             // In case the primary font doesn't have <space> (bug 970891),
  4984             // find one that does.
  4985             uint8_t matchType;
  4986             nsRefPtr<gfxFont> spaceFont =
  4987                 FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType);
  4988             if (spaceFont) {
  4989                 textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0);
  4994     // Note that the gfxGlyphExtents glyph bounds storage for the font will
  4995     // always contain an entry for the font's space glyph, so we don't have
  4996     // to call FetchGlyphExtents here.
  4997     return textRun;
  5000 gfxTextRun *
  5001 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
  5002                                const Parameters *aParams, uint32_t aFlags)
  5004     gfxTextRun *textRun =
  5005         gfxTextRun::Create(aParams, aLength, this, aFlags);
  5006     if (!textRun) {
  5007         return nullptr;
  5010     textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
  5011     return textRun;
  5014 gfxTextRun *
  5015 gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
  5017     // only use U+2010 if it is supported by the first font in the group;
  5018     // it's better to use ASCII '-' from the primary font than to fall back to
  5019     // U+2010 from some other, possibly poorly-matching face
  5020     static const char16_t hyphen = 0x2010;
  5021     gfxFont *font = GetFontAt(0);
  5022     if (font && font->HasCharacter(hyphen)) {
  5023         return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
  5024                            gfxFontGroup::TEXT_IS_PERSISTENT);
  5027     static const uint8_t dash = '-';
  5028     return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
  5029                        gfxFontGroup::TEXT_IS_PERSISTENT);
  5032 gfxFloat
  5033 gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
  5035     if (mHyphenWidth < 0) {
  5036         nsRefPtr<gfxContext> ctx(aProvider->GetContext());
  5037         if (ctx) {
  5038             nsAutoPtr<gfxTextRun>
  5039                 hyphRun(MakeHyphenTextRun(ctx,
  5040                                           aProvider->GetAppUnitsPerDevUnit()));
  5041             mHyphenWidth = hyphRun.get() ?
  5042                 hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
  5045     return mHyphenWidth;
  5048 gfxTextRun *
  5049 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
  5050                           const Parameters *aParams, uint32_t aFlags)
  5052     if (aLength == 0) {
  5053         return MakeEmptyTextRun(aParams, aFlags);
  5055     if (aLength == 1 && aString[0] == ' ') {
  5056         return MakeSpaceTextRun(aParams, aFlags);
  5059     aFlags |= TEXT_IS_8BIT;
  5061     if (GetStyle()->size == 0) {
  5062         // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
  5063         // them, and always create at least size 1 fonts, i.e. they still
  5064         // render something for size 0 fonts.
  5065         return MakeBlankTextRun(aLength, aParams, aFlags);
  5068     gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
  5069                                              this, aFlags);
  5070     if (!textRun) {
  5071         return nullptr;
  5074     InitTextRun(aParams->mContext, textRun, aString, aLength);
  5076     textRun->FetchGlyphExtents(aParams->mContext);
  5078     return textRun;
  5081 gfxTextRun *
  5082 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
  5083                           const Parameters *aParams, uint32_t aFlags)
  5085     if (aLength == 0) {
  5086         return MakeEmptyTextRun(aParams, aFlags);
  5088     if (aLength == 1 && aString[0] == ' ') {
  5089         return MakeSpaceTextRun(aParams, aFlags);
  5091     if (GetStyle()->size == 0) {
  5092         return MakeBlankTextRun(aLength, aParams, aFlags);
  5095     gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
  5096                                              this, aFlags);
  5097     if (!textRun) {
  5098         return nullptr;
  5101     InitTextRun(aParams->mContext, textRun, aString, aLength);
  5103     textRun->FetchGlyphExtents(aParams->mContext);
  5105     return textRun;
  5108 template<typename T>
  5109 void
  5110 gfxFontGroup::InitTextRun(gfxContext *aContext,
  5111                           gfxTextRun *aTextRun,
  5112                           const T *aString,
  5113                           uint32_t aLength)
  5115     NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
  5117     // we need to do numeral processing even on 8-bit text,
  5118     // in case we're converting Western to Hindi/Arabic digits
  5119     int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
  5120     nsAutoArrayPtr<char16_t> transformedString;
  5121     if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
  5122         // scan the string for numerals that may need to be transformed;
  5123         // if we find any, we'll make a local copy here and use that for
  5124         // font matching and glyph generation/shaping
  5125         bool prevIsArabic =
  5126             (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
  5127         for (uint32_t i = 0; i < aLength; ++i) {
  5128             char16_t origCh = aString[i];
  5129             char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
  5130             if (newCh != origCh) {
  5131                 if (!transformedString) {
  5132                     transformedString = new char16_t[aLength];
  5133                     if (sizeof(T) == sizeof(char16_t)) {
  5134                         memcpy(transformedString.get(), aString, i * sizeof(char16_t));
  5135                     } else {
  5136                         for (uint32_t j = 0; j < i; ++j) {
  5137                             transformedString[j] = aString[j];
  5142             if (transformedString) {
  5143                 transformedString[i] = newCh;
  5145             prevIsArabic = IS_ARABIC_CHAR(newCh);
  5149 #ifdef PR_LOGGING
  5150     PRLogModuleInfo *log = (mStyle.systemFont ?
  5151                             gfxPlatform::GetLog(eGfxLog_textrunui) :
  5152                             gfxPlatform::GetLog(eGfxLog_textrun));
  5153 #endif
  5155     if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
  5157 #ifdef PR_LOGGING
  5158         if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
  5159             nsAutoCString lang;
  5160             mStyle.language->ToUTF8String(lang);
  5161             nsAutoCString str((const char*)aString, aLength);
  5162             PR_LOG(log, PR_LOG_WARNING,\
  5163                    ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
  5164                     "weight: %d width: %d style: %s size: %6.2f %d-byte "
  5165                     "TEXTRUN [%s] ENDTEXTRUN\n",
  5166                     (mStyle.systemFont ? "textrunui" : "textrun"),
  5167                     NS_ConvertUTF16toUTF8(mFamilies).get(),
  5168                     lang.get(), MOZ_SCRIPT_LATIN, aLength,
  5169                     uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
  5170                     (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
  5171                     (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
  5172                                                             "normal")),
  5173                     mStyle.size,
  5174                     sizeof(T),
  5175                     str.get()));
  5177 #endif
  5179         // the text is still purely 8-bit; bypass the script-run itemizer
  5180         // and treat it as a single Latin run
  5181         InitScriptRun(aContext, aTextRun, aString,
  5182                       0, aLength, MOZ_SCRIPT_LATIN);
  5183     } else {
  5184         const char16_t *textPtr;
  5185         if (transformedString) {
  5186             textPtr = transformedString.get();
  5187         } else {
  5188             // typecast to avoid compilation error for the 8-bit version,
  5189             // even though this is dead code in that case
  5190             textPtr = reinterpret_cast<const char16_t*>(aString);
  5193         // split into script runs so that script can potentially influence
  5194         // the font matching process below
  5195         gfxScriptItemizer scriptRuns(textPtr, aLength);
  5197         uint32_t runStart = 0, runLimit = aLength;
  5198         int32_t runScript = MOZ_SCRIPT_LATIN;
  5199         while (scriptRuns.Next(runStart, runLimit, runScript)) {
  5201 #ifdef PR_LOGGING
  5202             if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
  5203                 nsAutoCString lang;
  5204                 mStyle.language->ToUTF8String(lang);
  5205                 uint32_t runLen = runLimit - runStart;
  5206                 PR_LOG(log, PR_LOG_WARNING,\
  5207                        ("(%s) fontgroup: [%s] lang: %s script: %d len %d "
  5208                         "weight: %d width: %d style: %s size: %6.2f %d-byte "
  5209                         "TEXTRUN [%s] ENDTEXTRUN\n",
  5210                         (mStyle.systemFont ? "textrunui" : "textrun"),
  5211                         NS_ConvertUTF16toUTF8(mFamilies).get(),
  5212                         lang.get(), runScript, runLen,
  5213                         uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
  5214                         (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
  5215                         (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
  5216                                                                 "normal")),
  5217                         mStyle.size,
  5218                         sizeof(T),
  5219                         NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
  5221 #endif
  5223             InitScriptRun(aContext, aTextRun, textPtr,
  5224                           runStart, runLimit, runScript);
  5228     if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
  5229         gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
  5230         if (!glyph->IsSimpleGlyph()) {
  5231             glyph->SetClusterStart(true);
  5235     // It's possible for CoreText to omit glyph runs if it decides they contain
  5236     // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
  5237     // need to eliminate them from the glyph run array to avoid drawing "partial
  5238     // ligatures" with the wrong font.
  5239     // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
  5240     // it will iterate back over all glyphruns in the textrun, which leads to
  5241     // pathologically-bad perf in the case where a textrun contains many script
  5242     // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
  5243     // every time a new script subrun is processed.
  5244     aTextRun->SanitizeGlyphRuns();
  5246     aTextRun->SortGlyphRuns();
  5249 template<typename T>
  5250 void
  5251 gfxFontGroup::InitScriptRun(gfxContext *aContext,
  5252                             gfxTextRun *aTextRun,
  5253                             const T *aString,
  5254                             uint32_t aScriptRunStart,
  5255                             uint32_t aScriptRunEnd,
  5256                             int32_t aRunScript)
  5258     NS_ASSERTION(aScriptRunEnd > aScriptRunStart,
  5259                  "don't call InitScriptRun for a zero-length run");
  5261     gfxFont *mainFont = GetFontAt(0);
  5263     uint32_t runStart = aScriptRunStart;
  5264     nsAutoTArray<gfxTextRange,3> fontRanges;
  5265     ComputeRanges(fontRanges, aString + aScriptRunStart,
  5266                   aScriptRunEnd - aScriptRunStart, aRunScript);
  5267     uint32_t numRanges = fontRanges.Length();
  5269     for (uint32_t r = 0; r < numRanges; r++) {
  5270         const gfxTextRange& range = fontRanges[r];
  5271         uint32_t matchedLength = range.Length();
  5272         gfxFont *matchedFont = range.font;
  5274         // create the glyph run for this range
  5275         if (matchedFont) {
  5276             aTextRun->AddGlyphRun(matchedFont, range.matchType,
  5277                                   runStart, (matchedLength > 0));
  5278             // do glyph layout and record the resulting positioned glyphs
  5279             if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun, aString,
  5280                                                   runStart, matchedLength,
  5281                                                   aRunScript)) {
  5282                 // glyph layout failed! treat as missing glyphs
  5283                 matchedFont = nullptr;
  5285         } else {
  5286             aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
  5287                                   runStart, (matchedLength > 0));
  5290         if (!matchedFont) {
  5291             // We need to set cluster boundaries (and mark spaces) so that
  5292             // surrogate pairs, combining characters, etc behave properly,
  5293             // even if we don't have glyphs for them
  5294             aTextRun->SetupClusterBoundaries(runStart, aString + runStart,
  5295                                              matchedLength);
  5297             // various "missing" characters may need special handling,
  5298             // so we check for them here
  5299             uint32_t runLimit = runStart + matchedLength;
  5300             for (uint32_t index = runStart; index < runLimit; index++) {
  5301                 T ch = aString[index];
  5303                 // tab and newline are not to be displayed as hexboxes,
  5304                 // but do need to be recorded in the textrun
  5305                 if (ch == '\n') {
  5306                     aTextRun->SetIsNewline(index);
  5307                     continue;
  5309                 if (ch == '\t') {
  5310                     aTextRun->SetIsTab(index);
  5311                     continue;
  5314                 // for 16-bit textruns only, check for surrogate pairs and
  5315                 // special Unicode spaces; omit these checks in 8-bit runs
  5316                 if (sizeof(T) == sizeof(char16_t)) {
  5317                     if (NS_IS_HIGH_SURROGATE(ch) &&
  5318                         index + 1 < aScriptRunEnd &&
  5319                         NS_IS_LOW_SURROGATE(aString[index + 1]))
  5321                         aTextRun->SetMissingGlyph(index,
  5322                                                   SURROGATE_TO_UCS4(ch,
  5323                                                                     aString[index + 1]),
  5324                                                   mainFont);
  5325                         index++;
  5326                         continue;
  5329                     // check if this is a known Unicode whitespace character that
  5330                     // we can render using the space glyph with a custom width
  5331                     gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
  5332                     if (wid >= 0.0) {
  5333                         nscoord advance =
  5334                             aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
  5335                         if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
  5336                             aTextRun->GetCharacterGlyphs()[index].
  5337                                 SetSimpleGlyph(advance,
  5338                                                mainFont->GetSpaceGlyph());
  5339                         } else {
  5340                             gfxTextRun::DetailedGlyph detailedGlyph;
  5341                             detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
  5342                             detailedGlyph.mAdvance = advance;
  5343                             detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
  5344                             gfxShapedText::CompressedGlyph g;
  5345                             g.SetComplex(true, true, 1);
  5346                             aTextRun->SetGlyphs(index,
  5347                                                 g, &detailedGlyph);
  5349                         continue;
  5353                 if (IsInvalidChar(ch)) {
  5354                     // invalid chars are left as zero-width/invisible
  5355                     continue;
  5358                 // record char code so we can draw a box with the Unicode value
  5359                 aTextRun->SetMissingGlyph(index, ch, mainFont);
  5363         runStart += matchedLength;
  5367 gfxTextRun *
  5368 gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
  5369                                  LazyReferenceContextGetter& aRefContextGetter)
  5371     if (mCachedEllipsisTextRun &&
  5372         mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
  5373         return mCachedEllipsisTextRun;
  5376     // Use a Unicode ellipsis if the font supports it,
  5377     // otherwise use three ASCII periods as fallback.
  5378     gfxFont* firstFont = GetFontAt(0);
  5379     nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
  5380         ? nsDependentString(kEllipsisChar,
  5381                             ArrayLength(kEllipsisChar) - 1)
  5382         : nsDependentString(kASCIIPeriodsChar,
  5383                             ArrayLength(kASCIIPeriodsChar) - 1);
  5385     nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
  5386     Parameters params = {
  5387         refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
  5388     };
  5389     gfxTextRun* textRun =
  5390         MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT);
  5391     if (!textRun) {
  5392         return nullptr;
  5394     mCachedEllipsisTextRun = textRun;
  5395     textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
  5396                                  // textrun prolong the fontgroup's life
  5397     return textRun;
  5400 already_AddRefed<gfxFont>
  5401 gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
  5403     if (!aFamily->TestCharacterMap(aCh)) {
  5404         return nullptr;
  5407     // Note that we don't need the actual runScript in matchData for
  5408     // gfxFontFamily::SearchAllFontsForChar, it's only used for the
  5409     // system-fallback case. So we can just set it to 0 here.
  5410     GlobalFontMatch matchData(aCh, 0, &mStyle);
  5411     aFamily->SearchAllFontsForChar(&matchData);
  5412     gfxFontEntry *fe = matchData.mBestMatch;
  5413     if (!fe) {
  5414         return nullptr;
  5417     bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
  5418     nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
  5419     return font.forget();
  5422 already_AddRefed<gfxFont>
  5423 gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
  5424                               int32_t aRunScript, gfxFont *aPrevMatchedFont,
  5425                               uint8_t *aMatchType)
  5427     // To optimize common cases, try the first font in the font-group
  5428     // before going into the more detailed checks below
  5429     uint32_t nextIndex = 0;
  5430     bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
  5431     bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
  5432     bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
  5434     if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
  5435         nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
  5436         if (firstFont->HasCharacter(aCh)) {
  5437             *aMatchType = gfxTextRange::kFontGroup;
  5438             return firstFont.forget();
  5440         // It's possible that another font in the family (e.g. regular face,
  5441         // where the requested style was italic) will support the character
  5442         nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
  5443         if (font) {
  5444             *aMatchType = gfxTextRange::kFontGroup;
  5445             return font.forget();
  5447         // we don't need to check the first font again below
  5448         ++nextIndex;
  5451     if (aPrevMatchedFont) {
  5452         // Don't switch fonts for control characters, regardless of
  5453         // whether they are present in the current font, as they won't
  5454         // actually be rendered (see bug 716229)
  5455         if (isJoinControl ||
  5456             GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
  5457             nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  5458             return ret.forget();
  5461         // if previous character was a join-causer (ZWJ),
  5462         // use the same font as the previous range if we can
  5463         if (wasJoinCauser) {
  5464             if (aPrevMatchedFont->HasCharacter(aCh)) {
  5465                 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  5466                 return ret.forget();
  5471     // if this character is a variation selector,
  5472     // use the previous font regardless of whether it supports VS or not.
  5473     // otherwise the text run will be divided.
  5474     if (isVarSelector) {
  5475         if (aPrevMatchedFont) {
  5476             nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  5477             return ret.forget();
  5479         // VS alone. it's meaningless to search different fonts
  5480         return nullptr;
  5483     // 1. check remaining fonts in the font group
  5484     uint32_t fontListLength = FontListLength();
  5485     for (uint32_t i = nextIndex; i < fontListLength; i++) {
  5486         nsRefPtr<gfxFont> font = mFonts[i].Font();
  5487         if (font->HasCharacter(aCh)) {
  5488             *aMatchType = gfxTextRange::kFontGroup;
  5489             return font.forget();
  5492         font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
  5493         if (font) {
  5494             *aMatchType = gfxTextRange::kFontGroup;
  5495             return font.forget();
  5499     // if character is in Private Use Area, don't do matching against pref or system fonts
  5500     if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
  5501         return nullptr;
  5503     // 2. search pref fonts
  5504     nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh);
  5505     if (font) {
  5506         *aMatchType = gfxTextRange::kPrefsFallback;
  5507         return font.forget();
  5510     // 3. use fallback fonts
  5511     // -- before searching for something else check the font used for the previous character
  5512     if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
  5513         *aMatchType = gfxTextRange::kSystemFallback;
  5514         nsRefPtr<gfxFont> ret = aPrevMatchedFont;
  5515         return ret.forget();
  5518     // never fall back for characters from unknown scripts
  5519     if (aRunScript == HB_SCRIPT_UNKNOWN) {
  5520         return nullptr;
  5523     // for known "space" characters, don't do a full system-fallback search;
  5524     // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
  5525     if (GetGeneralCategory(aCh) ==
  5526             HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
  5527         GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
  5529         return nullptr;
  5532     // -- otherwise look for other stuff
  5533     *aMatchType = gfxTextRange::kSystemFallback;
  5534     font = WhichSystemFontSupportsChar(aCh, aRunScript);
  5535     return font.forget();
  5538 template<typename T>
  5539 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
  5540                                  const T *aString, uint32_t aLength,
  5541                                  int32_t aRunScript)
  5543     NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
  5544     NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
  5546     uint32_t prevCh = 0;
  5547     int32_t lastRangeIndex = -1;
  5549     // initialize prevFont to the group's primary font, so that this will be
  5550     // used for string-initial control chars, etc rather than risk hitting font
  5551     // fallback for these (bug 716229)
  5552     gfxFont *prevFont = GetFontAt(0);
  5554     // if we use the initial value of prevFont, we treat this as a match from
  5555     // the font group; fixes bug 978313
  5556     uint8_t matchType = gfxTextRange::kFontGroup;
  5558     for (uint32_t i = 0; i < aLength; i++) {
  5560         const uint32_t origI = i; // save off in case we increase for surrogate
  5562         // set up current ch
  5563         uint32_t ch = aString[i];
  5565         // in 16-bit case only, check for surrogate pair
  5566         if (sizeof(T) == sizeof(char16_t)) {
  5567             if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
  5568                                  NS_IS_LOW_SURROGATE(aString[i + 1])) {
  5569                 i++;
  5570                 ch = SURROGATE_TO_UCS4(ch, aString[i]);
  5574         if (ch == 0xa0) {
  5575             ch = ' ';
  5578         // find the font for this char
  5579         nsRefPtr<gfxFont> font =
  5580             FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
  5582 #ifndef RELEASE_BUILD
  5583         if (MOZ_UNLIKELY(mTextPerf)) {
  5584             if (matchType == gfxTextRange::kPrefsFallback) {
  5585                 mTextPerf->current.fallbackPrefs++;
  5586             } else if (matchType == gfxTextRange::kSystemFallback) {
  5587                 mTextPerf->current.fallbackSystem++;
  5590 #endif
  5592         prevCh = ch;
  5594         if (lastRangeIndex == -1) {
  5595             // first char ==> make a new range
  5596             aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
  5597             lastRangeIndex++;
  5598             prevFont = font;
  5599         } else {
  5600             // if font has changed, make a new range
  5601             gfxTextRange& prevRange = aRanges[lastRangeIndex];
  5602             if (prevRange.font != font || prevRange.matchType != matchType) {
  5603                 // close out the previous range
  5604                 prevRange.end = origI;
  5605                 aRanges.AppendElement(gfxTextRange(origI, i + 1,
  5606                                                    font, matchType));
  5607                 lastRangeIndex++;
  5609                 // update prevFont for the next match, *unless* we switched
  5610                 // fonts on a ZWJ, in which case propagating the changed font
  5611                 // is probably not a good idea (see bug 619511)
  5612                 if (sizeof(T) == sizeof(uint8_t) ||
  5613                     !gfxFontUtils::IsJoinCauser(ch))
  5615                     prevFont = font;
  5621     aRanges[lastRangeIndex].end = aLength;
  5624 gfxUserFontSet* 
  5625 gfxFontGroup::GetUserFontSet()
  5627     return mUserFontSet;
  5630 void 
  5631 gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
  5633     if (aUserFontSet == mUserFontSet) {
  5634         return;
  5636     mUserFontSet = aUserFontSet;
  5637     mCurrGeneration = GetGeneration() - 1;
  5638     UpdateFontList();
  5641 uint64_t
  5642 gfxFontGroup::GetGeneration()
  5644     if (!mUserFontSet)
  5645         return 0;
  5646     return mUserFontSet->GetGeneration();
  5649 void
  5650 gfxFontGroup::UpdateFontList()
  5652     if (mCurrGeneration != GetGeneration()) {
  5653         // xxx - can probably improve this to detect when all fonts were found, so no need to update list
  5654         mFonts.Clear();
  5655         mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
  5656         mSkipDrawing = false;
  5658         // bug 548184 - need to clean up FT2, OS/2 platform code to use BuildFontList
  5659 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
  5660         BuildFontList();
  5661 #else
  5662         ForEachFont(FindPlatformFont, this);
  5663 #endif
  5664         mCurrGeneration = GetGeneration();
  5665         mCachedEllipsisTextRun = nullptr;
  5669 struct PrefFontCallbackData {
  5670     PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
  5671         : mPrefFamilies(aFamiliesArray)
  5672     {}
  5674     nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
  5676     static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
  5678         PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
  5680         gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
  5681         if (family) {
  5682             prefFontData->mPrefFamilies.AppendElement(family);
  5684         return true;
  5686 };
  5688 already_AddRefed<gfxFont>
  5689 gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
  5691     nsRefPtr<gfxFont> font;
  5693     // get the pref font list if it hasn't been set up already
  5694     uint32_t unicodeRange = FindCharUnicodeRange(aCh);
  5695     eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
  5697     // if the last pref font was the first family in the pref list, no need to recheck through a list of families
  5698     if (mLastPrefFont && charLang == mLastPrefLang &&
  5699         mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
  5700         font = mLastPrefFont;
  5701         return font.forget();
  5704     // based on char lang and page lang, set up list of pref lang fonts to check
  5705     eFontPrefLang prefLangs[kMaxLenPrefLangList];
  5706     uint32_t i, numLangs = 0;
  5708     gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
  5710     for (i = 0; i < numLangs; i++) {
  5711         nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
  5712         eFontPrefLang currentLang = prefLangs[i];
  5714         gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
  5716         // get the pref families for a single pref lang
  5717         if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
  5718             eFontPrefLang prefLangsToSearch[1] = { currentLang };
  5719             PrefFontCallbackData prefFontData(families);
  5720             gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
  5721                                            &prefFontData);
  5722             fontList->SetPrefFontFamilyEntries(currentLang, families);
  5725         // find the first pref font that includes the character
  5726         uint32_t  j, numPrefs;
  5727         numPrefs = families.Length();
  5728         for (j = 0; j < numPrefs; j++) {
  5729             // look up the appropriate face
  5730             gfxFontFamily *family = families[j];
  5731             if (!family) continue;
  5733             // if a pref font is used, it's likely to be used again in the same text run.
  5734             // the style doesn't change so the face lookup can be cached rather than calling
  5735             // FindOrMakeFont repeatedly.  speeds up FindFontForChar lookup times for subsequent
  5736             // pref font lookups
  5737             if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
  5738                 font = mLastPrefFont;
  5739                 return font.forget();
  5742             bool needsBold;
  5743             gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
  5744             // if ch in cmap, create and return a gfxFont
  5745             if (fe && fe->TestCharacterMap(aCh)) {
  5746                 nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
  5747                 if (!prefFont) continue;
  5748                 mLastPrefFamily = family;
  5749                 mLastPrefFont = prefFont;
  5750                 mLastPrefLang = charLang;
  5751                 mLastPrefFirstFont = (i == 0 && j == 0);
  5752                 return prefFont.forget();
  5758     return nullptr;
  5761 already_AddRefed<gfxFont>
  5762 gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
  5764     gfxFontEntry *fe = 
  5765         gfxPlatformFontList::PlatformFontList()->
  5766             SystemFindFontForChar(aCh, aRunScript, &mStyle);
  5767     if (fe) {
  5768         bool wantBold = mStyle.ComputeWeight() >= 6;
  5769         nsRefPtr<gfxFont> font =
  5770             fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
  5771         return font.forget();
  5774     return nullptr;
  5777 /*static*/ void
  5778 gfxFontGroup::Shutdown()
  5780     NS_IF_RELEASE(gLangService);
  5783 nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
  5786 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
  5788 /*static*/ uint32_t
  5789 gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
  5791   if (!aLangTag.Length() || aLangTag.Length() > 4) {
  5792     return NO_FONT_LANGUAGE_OVERRIDE;
  5794   uint32_t index, result = 0;
  5795   for (index = 0; index < aLangTag.Length(); ++index) {
  5796     char16_t ch = aLangTag[index];
  5797     if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
  5798       return NO_FONT_LANGUAGE_OVERRIDE;
  5800     result = (result << 8) + ch;
  5802   while (index++ < 4) {
  5803     result = (result << 8) + 0x20;
  5805   return result;
  5808 gfxFontStyle::gfxFontStyle() :
  5809     language(nsGkAtoms::x_western),
  5810     size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f),
  5811     languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
  5812     weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
  5813     systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
  5814     style(NS_FONT_STYLE_NORMAL)
  5818 gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
  5819                            gfxFloat aSize, nsIAtom *aLanguage,
  5820                            float aSizeAdjust, bool aSystemFont,
  5821                            bool aPrinterFont,
  5822                            const nsString& aLanguageOverride):
  5823     language(aLanguage),
  5824     size(aSize), sizeAdjust(aSizeAdjust),
  5825     languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
  5826     weight(aWeight), stretch(aStretch),
  5827     systemFont(aSystemFont), printerFont(aPrinterFont),
  5828     useGrayscaleAntialiasing(false), style(aStyle)
  5830     MOZ_ASSERT(!mozilla::IsNaN(size));
  5831     MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
  5833     if (weight > 900)
  5834         weight = 900;
  5835     if (weight < 100)
  5836         weight = 100;
  5838     if (size >= FONT_MAX_SIZE) {
  5839         size = FONT_MAX_SIZE;
  5840         sizeAdjust = 0.0;
  5841     } else if (size < 0.0) {
  5842         NS_WARNING("negative font size");
  5843         size = 0.0;
  5846     if (!language) {
  5847         NS_WARNING("null language");
  5848         language = nsGkAtoms::x_western;
  5852 gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
  5853     language(aStyle.language),
  5854     featureValueLookup(aStyle.featureValueLookup),
  5855     size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
  5856     languageOverride(aStyle.languageOverride),
  5857     weight(aStyle.weight), stretch(aStyle.stretch),
  5858     systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
  5859     useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
  5860     style(aStyle.style)
  5862     featureSettings.AppendElements(aStyle.featureSettings);
  5863     alternateValues.AppendElements(aStyle.alternateValues);
  5866 int8_t
  5867 gfxFontStyle::ComputeWeight() const
  5869     int8_t baseWeight = (weight + 50) / 100;
  5871     if (baseWeight < 0)
  5872         baseWeight = 0;
  5873     if (baseWeight > 9)
  5874         baseWeight = 9;
  5876     return baseWeight;
  5879 void
  5880 gfxShapedText::SetupClusterBoundaries(uint32_t         aOffset,
  5881                                       const char16_t *aString,
  5882                                       uint32_t         aLength)
  5884     CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
  5886     gfxTextRun::CompressedGlyph extendCluster;
  5887     extendCluster.SetComplex(false, true, 0);
  5889     ClusterIterator iter(aString, aLength);
  5891     // the ClusterIterator won't be able to tell us if the string
  5892     // _begins_ with a cluster-extender, so we handle that here
  5893     if (aLength && IsClusterExtender(*aString)) {
  5894         *glyphs = extendCluster;
  5897     while (!iter.AtEnd()) {
  5898         if (*iter == char16_t(' ')) {
  5899             glyphs->SetIsSpace();
  5901         // advance iter to the next cluster-start (or end of text)
  5902         iter.Next();
  5903         // step past the first char of the cluster
  5904         aString++;
  5905         glyphs++;
  5906         // mark all the rest as cluster-continuations
  5907         while (aString < iter) {
  5908             *glyphs = extendCluster;
  5909             if (NS_IS_LOW_SURROGATE(*aString)) {
  5910                 glyphs->SetIsLowSurrogate();
  5912             glyphs++;
  5913             aString++;
  5918 void
  5919 gfxShapedText::SetupClusterBoundaries(uint32_t       aOffset,
  5920                                       const uint8_t *aString,
  5921                                       uint32_t       aLength)
  5923     CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
  5924     const uint8_t *limit = aString + aLength;
  5926     while (aString < limit) {
  5927         if (*aString == uint8_t(' ')) {
  5928             glyphs->SetIsSpace();
  5930         aString++;
  5931         glyphs++;
  5935 gfxShapedText::DetailedGlyph *
  5936 gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
  5938     NS_ASSERTION(aIndex < GetLength(), "Index out of range");
  5940     if (!mDetailedGlyphs) {
  5941         mDetailedGlyphs = new DetailedGlyphStore();
  5944     DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
  5945     if (!details) {
  5946         GetCharacterGlyphs()[aIndex].SetMissing(0);
  5947         return nullptr;
  5950     return details;
  5953 void
  5954 gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
  5955                          const DetailedGlyph *aGlyphs)
  5957     NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
  5958     NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
  5959                  "First character can't be a ligature continuation!");
  5961     uint32_t glyphCount = aGlyph.GetGlyphCount();
  5962     if (glyphCount > 0) {
  5963         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
  5964         if (!details) {
  5965             return;
  5967         memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
  5969     GetCharacterGlyphs()[aIndex] = aGlyph;
  5972 #define ZWNJ 0x200C
  5973 #define ZWJ  0x200D
  5974 // U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
  5975 // in a future Unicode update. Add it manually for now
  5976 #define ALM  0x061C
  5977 static inline bool
  5978 IsDefaultIgnorable(uint32_t aChar)
  5980     return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
  5981            aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
  5984 void
  5985 gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
  5987     uint8_t category = GetGeneralCategory(aChar);
  5988     if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
  5989         category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
  5991         GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
  5994     DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
  5995     if (!details) {
  5996         return;
  5999     details->mGlyphID = aChar;
  6000     if (IsDefaultIgnorable(aChar)) {
  6001         // Setting advance width to zero will prevent drawing the hexbox
  6002         details->mAdvance = 0;
  6003     } else {
  6004         gfxFloat width =
  6005             std::max(aFont->GetMetrics().aveCharWidth,
  6006                      gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
  6007                          mAppUnitsPerDevUnit));
  6008         details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
  6010     details->mXOffset = 0;
  6011     details->mYOffset = 0;
  6012     GetCharacterGlyphs()[aIndex].SetMissing(1);
  6015 bool
  6016 gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
  6018     if (IsDefaultIgnorable(aCh)) {
  6019         DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
  6020         if (details) {
  6021             details->mGlyphID = aCh;
  6022             details->mAdvance = 0;
  6023             details->mXOffset = 0;
  6024             details->mYOffset = 0;
  6025             GetCharacterGlyphs()[aIndex].SetMissing(1);
  6026             return true;
  6029     return false;
  6032 void
  6033 gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
  6034                                               uint32_t aOffset,
  6035                                               uint32_t aLength)
  6037     uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
  6038     CompressedGlyph *charGlyphs = GetCharacterGlyphs();
  6039     for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
  6040          CompressedGlyph *glyphData = charGlyphs + i;
  6041          if (glyphData->IsSimpleGlyph()) {
  6042              // simple glyphs ==> just add the advance
  6043              int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
  6044              if (CompressedGlyph::IsSimpleAdvance(advance)) {
  6045                  glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
  6046              } else {
  6047                  // rare case, tested by making this the default
  6048                  uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  6049                  glyphData->SetComplex(true, true, 1);
  6050                  DetailedGlyph detail = {glyphIndex, advance, 0, 0};
  6051                  SetGlyphs(i, *glyphData, &detail);
  6053          } else {
  6054              // complex glyphs ==> add offset at cluster/ligature boundaries
  6055              uint32_t detailedLength = glyphData->GetGlyphCount();
  6056              if (detailedLength) {
  6057                  DetailedGlyph *details = GetDetailedGlyphs(i);
  6058                  if (!details) {
  6059                      continue;
  6061                  if (IsRightToLeft()) {
  6062                      details[0].mAdvance += synAppUnitOffset;
  6063                  } else {
  6064                      details[detailedLength - 1].mAdvance += synAppUnitOffset;
  6071 bool
  6072 gfxTextRun::GlyphRunIterator::NextRun()  {
  6073     if (mNextIndex >= mTextRun->mGlyphRuns.Length())
  6074         return false;
  6075     mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
  6076     if (mGlyphRun->mCharacterOffset >= mEndOffset)
  6077         return false;
  6079     mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
  6080     uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
  6081         ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
  6082     mStringEnd = std::min(mEndOffset, last);
  6084     ++mNextIndex;
  6085     return true;
  6088 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  6089 static void
  6090 AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
  6092     // Ignores detailed glyphs... we don't know when those have been constructed
  6093     // Also ignores gfxSkipChars dynamic storage (which won't be anything
  6094     // for preformatted text)
  6095     // Also ignores GlyphRun array, again because it hasn't been constructed
  6096     // by the time this gets called. If there's only one glyphrun that's stored
  6097     // directly in the textrun anyway so no additional overhead.
  6098     uint32_t length = aTextRun->GetLength();
  6099     int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
  6100     bytes += sizeof(gfxTextRun);
  6101     gTextRunStorage += bytes*aSign;
  6102     gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
  6104 #endif
  6106 // Helper for textRun creation to preallocate storage for glyph records;
  6107 // this function returns a pointer to the newly-allocated glyph storage.
  6108 // Returns nullptr if allocation fails.
  6109 void *
  6110 gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
  6112     // Allocate the storage we need, returning nullptr on failure rather than
  6113     // throwing an exception (because web content can create huge runs).
  6114     void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
  6115     if (!storage) {
  6116         NS_WARNING("failed to allocate storage for text run!");
  6117         return nullptr;
  6120     // Initialize the glyph storage (beyond aSize) to zero
  6121     memset(reinterpret_cast<char*>(storage) + aSize, 0,
  6122            aLength * sizeof(CompressedGlyph));
  6124     return storage;
  6127 gfxTextRun *
  6128 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
  6129                    uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
  6131     void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
  6132     if (!storage) {
  6133         return nullptr;
  6136     return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
  6139 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
  6140                        uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
  6141     : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
  6142     , mUserData(aParams->mUserData)
  6143     , mFontGroup(aFontGroup)
  6144     , mReleasedFontGroup(false)
  6146     NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
  6147     MOZ_COUNT_CTOR(gfxTextRun);
  6148     NS_ADDREF(mFontGroup);
  6150 #ifndef RELEASE_BUILD
  6151     gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
  6152     if (tp) {
  6153         tp->current.textrunConst++;
  6155 #endif
  6157     mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
  6159     if (aParams->mSkipChars) {
  6160         mSkipChars.TakeFrom(aParams->mSkipChars);
  6163 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  6164     AccountStorageForTextRun(this, 1);
  6165 #endif
  6167     mSkipDrawing = mFontGroup->ShouldSkipDrawing();
  6170 gfxTextRun::~gfxTextRun()
  6172 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  6173     AccountStorageForTextRun(this, -1);
  6174 #endif
  6175 #ifdef DEBUG
  6176     // Make it easy to detect a dead text run
  6177     mFlags = 0xFFFFFFFF;
  6178 #endif
  6180     // The cached ellipsis textrun (if any) in a fontgroup will have already
  6181     // been told to release its reference to the group, so we mustn't do that
  6182     // again here.
  6183     if (!mReleasedFontGroup) {
  6184 #ifndef RELEASE_BUILD
  6185         gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
  6186         if (tp) {
  6187             tp->current.textrunDestr++;
  6189 #endif
  6190         NS_RELEASE(mFontGroup);
  6193     MOZ_COUNT_DTOR(gfxTextRun);
  6196 void
  6197 gfxTextRun::ReleaseFontGroup()
  6199     NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
  6200     NS_RELEASE(mFontGroup);
  6201     mReleasedFontGroup = true;
  6204 bool
  6205 gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
  6206                                    uint8_t *aBreakBefore,
  6207                                    gfxContext *aRefContext)
  6209     NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
  6211     uint32_t changed = 0;
  6212     uint32_t i;
  6213     CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
  6214     for (i = 0; i < aLength; ++i) {
  6215         uint8_t canBreak = aBreakBefore[i];
  6216         if (canBreak && !charGlyphs[i].IsClusterStart()) {
  6217             // This can happen ... there is no guarantee that our linebreaking rules
  6218             // align with the platform's idea of what constitutes a cluster.
  6219             NS_WARNING("Break suggested inside cluster!");
  6220             canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
  6222         changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
  6224     return changed != 0;
  6227 gfxTextRun::LigatureData
  6228 gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
  6229                                 PropertyProvider *aProvider)
  6231     NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
  6232     NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
  6234     LigatureData result;
  6235     CompressedGlyph *charGlyphs = mCharacterGlyphs;
  6237     uint32_t i;
  6238     for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
  6239         NS_ASSERTION(i > 0, "Ligature at the start of the run??");
  6241     result.mLigatureStart = i;
  6242     for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
  6244     result.mLigatureEnd = i;
  6246     int32_t ligatureWidth =
  6247         GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
  6248     // Count the number of started clusters we have seen
  6249     uint32_t totalClusterCount = 0;
  6250     uint32_t partClusterIndex = 0;
  6251     uint32_t partClusterCount = 0;
  6252     for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
  6253         // Treat the first character of the ligature as the start of a
  6254         // cluster for our purposes of allocating ligature width to its
  6255         // characters.
  6256         if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
  6257             ++totalClusterCount;
  6258             if (i < aPartStart) {
  6259                 ++partClusterIndex;
  6260             } else if (i < aPartEnd) {
  6261                 ++partClusterCount;
  6265     NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
  6266     result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
  6267     result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
  6269     // Any rounding errors are apportioned to the final part of the ligature,
  6270     // so that measuring all parts of a ligature and summing them is equal to
  6271     // the ligature width.
  6272     if (aPartEnd == result.mLigatureEnd) {
  6273         gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
  6274         result.mPartWidth += ligatureWidth - allParts;
  6277     if (partClusterCount == 0) {
  6278         // nothing to draw
  6279         result.mClipBeforePart = result.mClipAfterPart = true;
  6280     } else {
  6281         // Determine whether we should clip before or after this part when
  6282         // drawing its slice of the ligature.
  6283         // We need to clip before the part if any cluster is drawn before
  6284         // this part.
  6285         result.mClipBeforePart = partClusterIndex > 0;
  6286         // We need to clip after the part if any cluster is drawn after
  6287         // this part.
  6288         result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
  6291     if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
  6292         gfxFont::Spacing spacing;
  6293         if (aPartStart == result.mLigatureStart) {
  6294             aProvider->GetSpacing(aPartStart, 1, &spacing);
  6295             result.mPartWidth += spacing.mBefore;
  6297         if (aPartEnd == result.mLigatureEnd) {
  6298             aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
  6299             result.mPartWidth += spacing.mAfter;
  6303     return result;
  6306 gfxFloat
  6307 gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
  6308                                         PropertyProvider *aProvider)
  6310     if (aPartStart >= aPartEnd)
  6311         return 0;
  6312     LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
  6313     return data.mPartWidth;
  6316 int32_t
  6317 gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
  6319     const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
  6320     int32_t advance = 0;
  6321     uint32_t i;
  6322     for (i = aStart; i < aEnd; ++i, ++glyphData) {
  6323         if (glyphData->IsSimpleGlyph()) {
  6324             advance += glyphData->GetSimpleAdvance();   
  6325         } else {
  6326             uint32_t glyphCount = glyphData->GetGlyphCount();
  6327             if (glyphCount == 0) {
  6328                 continue;
  6330             const DetailedGlyph *details = GetDetailedGlyphs(i);
  6331             if (details) {
  6332                 uint32_t j;
  6333                 for (j = 0; j < glyphCount; ++j, ++details) {
  6334                     advance += details->mAdvance;
  6339     return advance;
  6342 static void
  6343 GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
  6344                    gfxTextRun::PropertyProvider *aProvider,
  6345                    gfxTextRun::PropertyProvider::Spacing *aSpacing)
  6347     if (aStart >= aEnd)
  6348         return;
  6350     aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
  6352 #ifdef DEBUG
  6353     // Check to see if we have spacing inside ligatures
  6355     const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
  6356     uint32_t i;
  6358     for (i = aStart; i < aEnd; ++i) {
  6359         if (!charGlyphs[i].IsLigatureGroupStart()) {
  6360             NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
  6361                          "Before-spacing inside a ligature!");
  6362             NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
  6363                          "After-spacing inside a ligature!");
  6366 #endif
  6369 bool
  6370 gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
  6371                                     PropertyProvider *aProvider,
  6372                                     uint32_t aSpacingStart, uint32_t aSpacingEnd,
  6373                                     nsTArray<PropertyProvider::Spacing> *aSpacing)
  6375     if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
  6376         return false;
  6377     if (!aSpacing->AppendElements(aEnd - aStart))
  6378         return false;
  6379     memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
  6380     GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
  6381                        aSpacing->Elements() + aSpacingStart - aStart);
  6382     memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
  6383     return true;
  6386 void
  6387 gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
  6389     if (*aStart >= *aEnd)
  6390         return;
  6392     CompressedGlyph *charGlyphs = mCharacterGlyphs;
  6394     while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
  6395         ++(*aStart);
  6397     if (*aEnd < GetLength()) {
  6398         while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
  6399             --(*aEnd);
  6404 void
  6405 gfxTextRun::DrawGlyphs(gfxFont *aFont, gfxContext *aContext,
  6406                        DrawMode aDrawMode, gfxPoint *aPt,
  6407                        gfxTextContextPaint *aContextPaint,
  6408                        uint32_t aStart, uint32_t aEnd,
  6409                        PropertyProvider *aProvider,
  6410                        uint32_t aSpacingStart, uint32_t aSpacingEnd,
  6411                        gfxTextRunDrawCallbacks *aCallbacks)
  6413     nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  6414     bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
  6415         aSpacingStart, aSpacingEnd, &spacingBuffer);
  6416     aFont->Draw(this, aStart, aEnd, aContext, aDrawMode, aPt,
  6417                 haveSpacing ? spacingBuffer.Elements() : nullptr, aContextPaint,
  6418                 aCallbacks);
  6421 static void
  6422 ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
  6423                     gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
  6425     if (aLigature->mClipBeforePart) {
  6426         if (aTextRun->IsRightToLeft()) {
  6427             *aRight = std::min(*aRight, aXOrigin);
  6428         } else {
  6429             *aLeft = std::max(*aLeft, aXOrigin);
  6432     if (aLigature->mClipAfterPart) {
  6433         gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
  6434         if (aTextRun->IsRightToLeft()) {
  6435             *aLeft = std::max(*aLeft, endEdge);
  6436         } else {
  6437             *aRight = std::min(*aRight, endEdge);
  6442 void
  6443 gfxTextRun::DrawPartialLigature(gfxFont *aFont, gfxContext *aCtx,
  6444                                 uint32_t aStart, uint32_t aEnd,
  6445                                 gfxPoint *aPt,
  6446                                 PropertyProvider *aProvider,
  6447                                 gfxTextRunDrawCallbacks *aCallbacks)
  6449     if (aStart >= aEnd)
  6450         return;
  6452     // Draw partial ligature. We hack this by clipping the ligature.
  6453     LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
  6454     gfxRect clipExtents = aCtx->GetClipExtents();
  6455     gfxFloat left = clipExtents.X()*mAppUnitsPerDevUnit;
  6456     gfxFloat right = clipExtents.XMost()*mAppUnitsPerDevUnit;
  6457     ClipPartialLigature(this, &left, &right, aPt->x, &data);
  6460       // Need to preserve the path, otherwise this can break canvas text-on-path;
  6461       // in general it seems like a good thing, as naive callers probably won't
  6462       // expect gfxTextRun::Draw to implicitly destroy the current path.
  6463       gfxContextPathAutoSaveRestore savePath(aCtx);
  6465       // use division here to ensure that when the rect is aligned on multiples
  6466       // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
  6467       // Also, make sure we snap the rectangle to device pixels.
  6468       aCtx->Save();
  6469       aCtx->NewPath();
  6470       aCtx->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
  6471                               clipExtents.Y(),
  6472                               (right - left) / mAppUnitsPerDevUnit,
  6473                               clipExtents.Height()), true);
  6474       aCtx->Clip();
  6477     gfxFloat direction = GetDirection();
  6478     gfxPoint pt(aPt->x - direction*data.mPartAdvance, aPt->y);
  6479     DrawGlyphs(aFont, aCtx,
  6480                aCallbacks ? DrawMode::GLYPH_PATH : DrawMode::GLYPH_FILL, &pt,
  6481                nullptr, data.mLigatureStart, data.mLigatureEnd, aProvider,
  6482                aStart, aEnd, aCallbacks);
  6483     aCtx->Restore();
  6485     aPt->x += direction*data.mPartWidth;
  6488 // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
  6489 static bool
  6490 HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
  6492     gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
  6493     while (iter.NextRun()) {
  6494         gfxFont *font = iter.GetGlyphRun()->mFont;
  6495         if (font && font->IsSyntheticBold()) {
  6496             return true;
  6500     return false;
  6503 // returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
  6504 // if true, color is set on output
  6505 static bool
  6506 HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
  6508     if (aContext->GetDeviceColor(aCurrentColor)) {
  6509         if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
  6510             return true;
  6514     return false;
  6517 // helper class for double-buffering drawing with non-opaque color
  6518 struct BufferAlphaColor {
  6519     BufferAlphaColor(gfxContext *aContext)
  6520         : mContext(aContext)
  6525     ~BufferAlphaColor() {}
  6527     void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
  6529         mContext->Save();
  6530         mContext->NewPath();
  6531         mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
  6532                     aBounds.Y() / appsPerDevUnit,
  6533                     aBounds.Width() / appsPerDevUnit,
  6534                     aBounds.Height() / appsPerDevUnit), true);
  6535         mContext->Clip();
  6536         mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
  6537         mContext->PushGroup(gfxContentType::COLOR_ALPHA);
  6538         mAlpha = aAlphaColor.a;
  6541     void PopAlpha()
  6543         // pop the text, using the color alpha as the opacity
  6544         mContext->PopGroupToSource();
  6545         mContext->SetOperator(gfxContext::OPERATOR_OVER);
  6546         mContext->Paint(mAlpha);
  6547         mContext->Restore();
  6550     gfxContext *mContext;
  6551     gfxFloat mAlpha;
  6552 };
  6554 void
  6555 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
  6556                  uint32_t aStart, uint32_t aLength,
  6557                  PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
  6558                  gfxTextContextPaint *aContextPaint,
  6559                  gfxTextRunDrawCallbacks *aCallbacks)
  6561     NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  6562     NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
  6563                  "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
  6564     NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks, "callback must not be specified unless using GLYPH_PATH");
  6566     bool skipDrawing = mSkipDrawing;
  6567     if (aDrawMode == DrawMode::GLYPH_FILL) {
  6568         gfxRGBA currentColor;
  6569         if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
  6570             skipDrawing = true;
  6574     gfxFloat direction = GetDirection();
  6576     if (skipDrawing) {
  6577         // We don't need to draw anything;
  6578         // but if the caller wants advance width, we need to compute it here
  6579         if (aAdvanceWidth) {
  6580             gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
  6581                                                       gfxFont::LOOSE_INK_EXTENTS,
  6582                                                       aContext, aProvider);
  6583             *aAdvanceWidth = metrics.mAdvanceWidth * direction;
  6586         // return without drawing
  6587         return;
  6590     gfxPoint pt = aPt;
  6592     // synthetic bolding draws glyphs twice ==> colors with opacity won't draw correctly unless first drawn without alpha
  6593     BufferAlphaColor syntheticBoldBuffer(aContext);
  6594     gfxRGBA currentColor;
  6595     bool needToRestore = false;
  6597     if (aDrawMode == DrawMode::GLYPH_FILL && HasNonOpaqueColor(aContext, currentColor)
  6598                                           && HasSyntheticBold(this, aStart, aLength)) {
  6599         needToRestore = true;
  6600         // measure text, use the bounding box
  6601         gfxTextRun::Metrics metrics = MeasureText(aStart, aLength, gfxFont::LOOSE_INK_EXTENTS,
  6602                                                   aContext, aProvider);
  6603         metrics.mBoundingBox.MoveBy(aPt);
  6604         syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor, GetAppUnitsPerDevUnit());
  6607     GlyphRunIterator iter(this, aStart, aLength);
  6608     while (iter.NextRun()) {
  6609         gfxFont *font = iter.GetGlyphRun()->mFont;
  6610         uint32_t start = iter.GetStringStart();
  6611         uint32_t end = iter.GetStringEnd();
  6612         uint32_t ligatureRunStart = start;
  6613         uint32_t ligatureRunEnd = end;
  6614         ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  6616         bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
  6617                            (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
  6619         if (drawPartial) {
  6620             DrawPartialLigature(font, aContext, start, ligatureRunStart, &pt,
  6621                                 aProvider, aCallbacks);
  6624         DrawGlyphs(font, aContext, aDrawMode, &pt, aContextPaint, ligatureRunStart,
  6625                    ligatureRunEnd, aProvider, ligatureRunStart, ligatureRunEnd,
  6626                    aCallbacks);
  6628         if (drawPartial) {
  6629             DrawPartialLigature(font, aContext, ligatureRunEnd, end, &pt,
  6630                                 aProvider, aCallbacks);
  6634     // composite result when synthetic bolding used
  6635     if (needToRestore) {
  6636         syntheticBoldBuffer.PopAlpha();
  6639     if (aAdvanceWidth) {
  6640         *aAdvanceWidth = (pt.x - aPt.x)*direction;
  6644 void
  6645 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
  6646                                     uint32_t aStart, uint32_t aEnd,
  6647                                     gfxFont::BoundingBoxType aBoundingBoxType,
  6648                                     gfxContext *aRefContext,
  6649                                     PropertyProvider *aProvider,
  6650                                     uint32_t aSpacingStart, uint32_t aSpacingEnd,
  6651                                     Metrics *aMetrics)
  6653     nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  6654     bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
  6655         aSpacingStart, aSpacingEnd, &spacingBuffer);
  6656     Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
  6657                                      haveSpacing ? spacingBuffer.Elements() : nullptr);
  6658     aMetrics->CombineWith(metrics, IsRightToLeft());
  6661 void
  6662 gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
  6663     uint32_t aStart, uint32_t aEnd,
  6664     gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
  6665     PropertyProvider *aProvider, Metrics *aMetrics)
  6667     if (aStart >= aEnd)
  6668         return;
  6670     // Measure partial ligature. We hack this by clipping the metrics in the
  6671     // same way we clip the drawing.
  6672     LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
  6674     // First measure the complete ligature
  6675     Metrics metrics;
  6676     AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
  6677                             aBoundingBoxType, aRefContext,
  6678                             aProvider, aStart, aEnd, &metrics);
  6680     // Clip the bounding box to the ligature part
  6681     gfxFloat bboxLeft = metrics.mBoundingBox.X();
  6682     gfxFloat bboxRight = metrics.mBoundingBox.XMost();
  6683     // Where we are going to start "drawing" relative to our left baseline origin
  6684     gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
  6685     ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
  6686     metrics.mBoundingBox.x = bboxLeft;
  6687     metrics.mBoundingBox.width = bboxRight - bboxLeft;
  6689     // mBoundingBox is now relative to the left baseline origin for the entire
  6690     // ligature. Shift it left.
  6691     metrics.mBoundingBox.x -=
  6692         IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
  6693             : data.mPartAdvance;    
  6694     metrics.mAdvanceWidth = data.mPartWidth;
  6696     aMetrics->CombineWith(metrics, IsRightToLeft());
  6699 gfxTextRun::Metrics
  6700 gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
  6701                         gfxFont::BoundingBoxType aBoundingBoxType,
  6702                         gfxContext *aRefContext,
  6703                         PropertyProvider *aProvider)
  6705     NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  6707     Metrics accumulatedMetrics;
  6708     GlyphRunIterator iter(this, aStart, aLength);
  6709     while (iter.NextRun()) {
  6710         gfxFont *font = iter.GetGlyphRun()->mFont;
  6711         uint32_t start = iter.GetStringStart();
  6712         uint32_t end = iter.GetStringEnd();
  6713         uint32_t ligatureRunStart = start;
  6714         uint32_t ligatureRunEnd = end;
  6715         ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  6717         AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
  6718             aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
  6720         // XXX This sucks. We have to get glyph extents just so we can detect
  6721         // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
  6722         // even though in almost all cases we could get correct results just
  6723         // by getting some ascent/descent from the font and using our stored
  6724         // advance widths.
  6725         AccumulateMetricsForRun(font,
  6726             ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
  6727             aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
  6728             &accumulatedMetrics);
  6730         AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
  6731             aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
  6734     return accumulatedMetrics;
  6737 #define MEASUREMENT_BUFFER_SIZE 100
  6739 uint32_t
  6740 gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
  6741                                 bool aLineBreakBefore, gfxFloat aWidth,
  6742                                 PropertyProvider *aProvider,
  6743                                 bool aSuppressInitialBreak,
  6744                                 gfxFloat *aTrimWhitespace,
  6745                                 Metrics *aMetrics,
  6746                                 gfxFont::BoundingBoxType aBoundingBoxType,
  6747                                 gfxContext *aRefContext,
  6748                                 bool *aUsedHyphenation,
  6749                                 uint32_t *aLastBreak,
  6750                                 bool aCanWordWrap,
  6751                                 gfxBreakPriority *aBreakPriority)
  6753     aMaxLength = std::min(aMaxLength, GetLength() - aStart);
  6755     NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
  6757     uint32_t bufferStart = aStart;
  6758     uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
  6759     PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
  6760     bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
  6761     if (haveSpacing) {
  6762         GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
  6763                            spacingBuffer);
  6765     bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
  6766     bool haveHyphenation = aProvider &&
  6767         (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
  6768          (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
  6769           (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
  6770     if (haveHyphenation) {
  6771         aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
  6772                                         hyphenBuffer);
  6775     gfxFloat width = 0;
  6776     gfxFloat advance = 0;
  6777     // The number of space characters that can be trimmed
  6778     uint32_t trimmableChars = 0;
  6779     // The amount of space removed by ignoring trimmableChars
  6780     gfxFloat trimmableAdvance = 0;
  6781     int32_t lastBreak = -1;
  6782     int32_t lastBreakTrimmableChars = -1;
  6783     gfxFloat lastBreakTrimmableAdvance = -1;
  6784     bool aborted = false;
  6785     uint32_t end = aStart + aMaxLength;
  6786     bool lastBreakUsedHyphenation = false;
  6788     uint32_t ligatureRunStart = aStart;
  6789     uint32_t ligatureRunEnd = end;
  6790     ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  6792     uint32_t i;
  6793     for (i = aStart; i < end; ++i) {
  6794         if (i >= bufferStart + bufferLength) {
  6795             // Fetch more spacing and hyphenation data
  6796             bufferStart = i;
  6797             bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
  6798             if (haveSpacing) {
  6799                 GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
  6800                                    spacingBuffer);
  6802             if (haveHyphenation) {
  6803                 aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
  6804                                                 hyphenBuffer);
  6808         // There can't be a word-wrap break opportunity at the beginning of the
  6809         // line: if the width is too small for even one character to fit, it 
  6810         // could be the first and last break opportunity on the line, and that
  6811         // would trigger an infinite loop.
  6812         if (!aSuppressInitialBreak || i > aStart) {
  6813             bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
  6814             bool atHyphenationBreak =
  6815                 !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart];
  6816             bool atBreak = atNaturalBreak || atHyphenationBreak;
  6817             bool wordWrapping =
  6818                 aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
  6819                 *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
  6821             if (atBreak || wordWrapping) {
  6822                 gfxFloat hyphenatedAdvance = advance;
  6823                 if (atHyphenationBreak) {
  6824                     hyphenatedAdvance += aProvider->GetHyphenWidth();
  6827                 if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
  6828                     // We can break here.
  6829                     lastBreak = i;
  6830                     lastBreakTrimmableChars = trimmableChars;
  6831                     lastBreakTrimmableAdvance = trimmableAdvance;
  6832                     lastBreakUsedHyphenation = atHyphenationBreak;
  6833                     *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
  6834                                               : gfxBreakPriority::eWordWrapBreak;
  6837                 width += advance;
  6838                 advance = 0;
  6839                 if (width - trimmableAdvance > aWidth) {
  6840                     // No more text fits. Abort
  6841                     aborted = true;
  6842                     break;
  6847         gfxFloat charAdvance;
  6848         if (i >= ligatureRunStart && i < ligatureRunEnd) {
  6849             charAdvance = GetAdvanceForGlyphs(i, i + 1);
  6850             if (haveSpacing) {
  6851                 PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
  6852                 charAdvance += space->mBefore + space->mAfter;
  6854         } else {
  6855             charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
  6858         advance += charAdvance;
  6859         if (aTrimWhitespace) {
  6860             if (mCharacterGlyphs[i].CharIsSpace()) {
  6861                 ++trimmableChars;
  6862                 trimmableAdvance += charAdvance;
  6863             } else {
  6864                 trimmableAdvance = 0;
  6865                 trimmableChars = 0;
  6870     if (!aborted) {
  6871         width += advance;
  6874     // There are three possibilities:
  6875     // 1) all the text fit (width <= aWidth)
  6876     // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
  6877     // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
  6878     uint32_t charsFit;
  6879     bool usedHyphenation = false;
  6880     if (width - trimmableAdvance <= aWidth) {
  6881         charsFit = aMaxLength;
  6882     } else if (lastBreak >= 0) {
  6883         charsFit = lastBreak - aStart;
  6884         trimmableChars = lastBreakTrimmableChars;
  6885         trimmableAdvance = lastBreakTrimmableAdvance;
  6886         usedHyphenation = lastBreakUsedHyphenation;
  6887     } else {
  6888         charsFit = aMaxLength;
  6891     if (aMetrics) {
  6892         *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
  6893             aBoundingBoxType, aRefContext, aProvider);
  6895     if (aTrimWhitespace) {
  6896         *aTrimWhitespace = trimmableAdvance;
  6898     if (aUsedHyphenation) {
  6899         *aUsedHyphenation = usedHyphenation;
  6901     if (aLastBreak && charsFit == aMaxLength) {
  6902         if (lastBreak < 0) {
  6903             *aLastBreak = UINT32_MAX;
  6904         } else {
  6905             *aLastBreak = lastBreak - aStart;
  6909     return charsFit;
  6912 gfxFloat
  6913 gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
  6914                             PropertyProvider *aProvider)
  6916     NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
  6918     uint32_t ligatureRunStart = aStart;
  6919     uint32_t ligatureRunEnd = aStart + aLength;
  6920     ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
  6922     gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
  6923                       ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
  6925     // Account for all remaining spacing here. This is more efficient than
  6926     // processing it along with the glyphs.
  6927     if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
  6928         uint32_t i;
  6929         nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
  6930         if (spacingBuffer.AppendElements(aLength)) {
  6931             GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
  6932                                spacingBuffer.Elements());
  6933             for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
  6934                 PropertyProvider::Spacing *space = &spacingBuffer[i];
  6935                 result += space->mBefore + space->mAfter;
  6940     return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
  6943 bool
  6944 gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
  6945                           bool aLineBreakBefore, bool aLineBreakAfter,
  6946                           gfxFloat *aAdvanceWidthDelta,
  6947                           gfxContext *aRefContext)
  6949     // Do nothing because our shaping does not currently take linebreaks into
  6950     // account. There is no change in advance width.
  6951     if (aAdvanceWidthDelta) {
  6952         *aAdvanceWidthDelta = 0;
  6954     return false;
  6957 uint32_t
  6958 gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
  6960     NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
  6961     NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
  6962                  "non-empty text but no glyph runs present!");
  6963     if (aOffset == GetLength())
  6964         return mGlyphRuns.Length();
  6965     uint32_t start = 0;
  6966     uint32_t end = mGlyphRuns.Length();
  6967     while (end - start > 1) {
  6968         uint32_t mid = (start + end)/2;
  6969         if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
  6970             start = mid;
  6971         } else {
  6972             end = mid;
  6975     NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
  6976                  "Hmm, something went wrong, aOffset should have been found");
  6977     return start;
  6980 nsresult
  6981 gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
  6982                         uint32_t aUTF16Offset, bool aForceNewRun)
  6984     NS_ASSERTION(aFont, "adding glyph run for null font!");
  6985     if (!aFont) {
  6986         return NS_OK;
  6988     uint32_t numGlyphRuns = mGlyphRuns.Length();
  6989     if (!aForceNewRun && numGlyphRuns > 0) {
  6990         GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
  6992         NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
  6993                      "Glyph runs out of order (and run not forced)");
  6995         // Don't append a run if the font is already the one we want
  6996         if (lastGlyphRun->mFont == aFont &&
  6997             lastGlyphRun->mMatchType == aMatchType)
  6999             return NS_OK;
  7002         // If the offset has not changed, avoid leaving a zero-length run
  7003         // by overwriting the last entry instead of appending...
  7004         if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
  7006             // ...except that if the run before the last entry had the same
  7007             // font as the new one wants, merge with it instead of creating
  7008             // adjacent runs with the same font
  7009             if (numGlyphRuns > 1 &&
  7010                 mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
  7011                 mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
  7013                 mGlyphRuns.TruncateLength(numGlyphRuns - 1);
  7014                 return NS_OK;
  7017             lastGlyphRun->mFont = aFont;
  7018             lastGlyphRun->mMatchType = aMatchType;
  7019             return NS_OK;
  7023     NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
  7024                  "First run doesn't cover the first character (and run not forced)?");
  7026     GlyphRun *glyphRun = mGlyphRuns.AppendElement();
  7027     if (!glyphRun)
  7028         return NS_ERROR_OUT_OF_MEMORY;
  7029     glyphRun->mFont = aFont;
  7030     glyphRun->mCharacterOffset = aUTF16Offset;
  7031     glyphRun->mMatchType = aMatchType;
  7032     return NS_OK;
  7035 void
  7036 gfxTextRun::SortGlyphRuns()
  7038     if (mGlyphRuns.Length() <= 1)
  7039         return;
  7041     nsTArray<GlyphRun> runs(mGlyphRuns);
  7042     GlyphRunOffsetComparator comp;
  7043     runs.Sort(comp);
  7045     // Now copy back, coalescing adjacent glyph runs that have the same font
  7046     mGlyphRuns.Clear();
  7047     uint32_t i, count = runs.Length();
  7048     for (i = 0; i < count; ++i) {
  7049         // a GlyphRun with the same font as the previous GlyphRun can just
  7050         // be skipped; the last GlyphRun will cover its character range.
  7051         if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
  7052             mGlyphRuns.AppendElement(runs[i]);
  7053             // If two fonts have the same character offset, Sort() will have
  7054             // randomized the order.
  7055             NS_ASSERTION(i == 0 ||
  7056                          runs[i].mCharacterOffset !=
  7057                          runs[i - 1].mCharacterOffset,
  7058                          "Two fonts for the same run, glyph indices may not match the font");
  7063 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
  7064 // therefore we only call it once, at the end of textrun construction,
  7065 // NOT incrementally as each glyph run is added (bug 680402).
  7066 void
  7067 gfxTextRun::SanitizeGlyphRuns()
  7069     if (mGlyphRuns.Length() <= 1)
  7070         return;
  7072     // If any glyph run starts with ligature-continuation characters, we need to advance it
  7073     // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
  7074     // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
  7075     // it appear as if a ligature has been formed)
  7076     int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
  7077     const CompressedGlyph *charGlyphs = mCharacterGlyphs;
  7078     for (i = lastRunIndex; i >= 0; --i) {
  7079         GlyphRun& run = mGlyphRuns[i];
  7080         while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
  7081                run.mCharacterOffset < GetLength()) {
  7082             run.mCharacterOffset++;
  7084         // if the run has become empty, eliminate it
  7085         if ((i < lastRunIndex &&
  7086              run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
  7087             (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
  7088             mGlyphRuns.RemoveElementAt(i);
  7089             --lastRunIndex;
  7094 uint32_t
  7095 gfxTextRun::CountMissingGlyphs()
  7097     uint32_t i;
  7098     uint32_t count = 0;
  7099     for (i = 0; i < GetLength(); ++i) {
  7100         if (mCharacterGlyphs[i].IsMissing()) {
  7101             ++count;
  7104     return count;
  7107 gfxTextRun::DetailedGlyph *
  7108 gfxTextRun::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
  7110     NS_ASSERTION(aIndex < GetLength(), "Index out of range");
  7112     if (!mDetailedGlyphs) {
  7113         mDetailedGlyphs = new DetailedGlyphStore();
  7116     DetailedGlyph *details = mDetailedGlyphs->Allocate(aIndex, aCount);
  7117     if (!details) {
  7118         mCharacterGlyphs[aIndex].SetMissing(0);
  7119         return nullptr;
  7122     return details;
  7125 void
  7126 gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
  7128     uint32_t wordLen = aShapedWord->GetLength();
  7129     NS_ASSERTION(aOffset + wordLen <= GetLength(),
  7130                  "word overruns end of textrun!");
  7132     CompressedGlyph *charGlyphs = GetCharacterGlyphs();
  7133     const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
  7134     if (aShapedWord->HasDetailedGlyphs()) {
  7135         for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
  7136             const CompressedGlyph& g = wordGlyphs[i];
  7137             if (g.IsSimpleGlyph()) {
  7138                 charGlyphs[aOffset] = g;
  7139             } else {
  7140                 const DetailedGlyph *details =
  7141                     g.GetGlyphCount() > 0 ?
  7142                         aShapedWord->GetDetailedGlyphs(i) : nullptr;
  7143                 SetGlyphs(aOffset, g, details);
  7146     } else {
  7147         memcpy(charGlyphs + aOffset, wordGlyphs,
  7148                wordLen * sizeof(CompressedGlyph));
  7152 void
  7153 gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
  7154                               uint32_t aLength, uint32_t aDest)
  7156     NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
  7157                  "Source substring out of range");
  7158     NS_ASSERTION(aDest + aLength <= GetLength(),
  7159                  "Destination substring out of range");
  7161     if (aSource->mSkipDrawing) {
  7162         mSkipDrawing = true;
  7165     // Copy base glyph data, and DetailedGlyph data where present
  7166     const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
  7167     CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
  7168     for (uint32_t i = 0; i < aLength; ++i) {
  7169         CompressedGlyph g = srcGlyphs[i];
  7170         g.SetCanBreakBefore(!g.IsClusterStart() ?
  7171             CompressedGlyph::FLAG_BREAK_TYPE_NONE :
  7172             dstGlyphs[i].CanBreakBefore());
  7173         if (!g.IsSimpleGlyph()) {
  7174             uint32_t count = g.GetGlyphCount();
  7175             if (count > 0) {
  7176                 DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
  7177                 if (dst) {
  7178                     DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
  7179                     if (src) {
  7180                         ::memcpy(dst, src, count * sizeof(DetailedGlyph));
  7181                     } else {
  7182                         g.SetMissing(0);
  7184                 } else {
  7185                     g.SetMissing(0);
  7189         dstGlyphs[i] = g;
  7192     // Copy glyph runs
  7193     GlyphRunIterator iter(aSource, aStart, aLength);
  7194 #ifdef DEBUG
  7195     gfxFont *lastFont = nullptr;
  7196 #endif
  7197     while (iter.NextRun()) {
  7198         gfxFont *font = iter.GetGlyphRun()->mFont;
  7199         NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
  7200 #ifdef DEBUG
  7201         lastFont = font;
  7202         uint32_t end = iter.GetStringEnd();
  7203 #endif
  7204         uint32_t start = iter.GetStringStart();
  7206         // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
  7207         // Although it's unusual (and not desirable), it's possible for us to assign
  7208         // different fonts to a base character and a following diacritic.
  7209         // Example on OSX 10.5/10.6 with default fonts installed:
  7210         //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
  7211         //                    &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
  7212         // This means the rendering of the cluster will probably not be very good,
  7213         // but it's the best we can do for now if the specified font only covered the
  7214         // initial base character and not its applied marks.
  7215         NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
  7216                          "Started font run in the middle of a cluster");
  7217         NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
  7218                          "Ended font run in the middle of a cluster");
  7220         nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
  7221                                   start - aStart + aDest, false);
  7222         if (NS_FAILED(rv))
  7223             return;
  7227 void
  7228 gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
  7229                           uint32_t aCharIndex)
  7231     if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
  7232         return;
  7235     aFont->InitWordCache();
  7236     static const uint8_t space = ' ';
  7237     gfxShapedWord *sw = aFont->GetShapedWord(aContext,
  7238                                              &space, 1,
  7239                                              HashMix(0, ' '), 
  7240                                              MOZ_SCRIPT_LATIN,
  7241                                              mAppUnitsPerDevUnit,
  7242                                              gfxTextRunFactory::TEXT_IS_8BIT |
  7243                                              gfxTextRunFactory::TEXT_IS_ASCII |
  7244                                              gfxTextRunFactory::TEXT_IS_PERSISTENT,
  7245                                              nullptr);
  7246     if (sw) {
  7247         AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
  7248         CopyGlyphDataFrom(sw, aCharIndex);
  7252 bool
  7253 gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
  7254                                   uint32_t aCharIndex, char16_t aSpaceChar)
  7256     uint32_t spaceGlyph = aFont->GetSpaceGlyph();
  7257     if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
  7258         return false;
  7261     uint32_t spaceWidthAppUnits =
  7262         NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
  7263     if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
  7264         return false;
  7267     AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
  7268     CompressedGlyph g;
  7269     g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
  7270     if (aSpaceChar == ' ') {
  7271         g.SetIsSpace();
  7273     GetCharacterGlyphs()[aCharIndex] = g;
  7274     return true;
  7277 void
  7278 gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
  7280     bool needsGlyphExtents = NeedsGlyphExtents(this);
  7281     if (!needsGlyphExtents && !mDetailedGlyphs)
  7282         return;
  7284     uint32_t i, runCount = mGlyphRuns.Length();
  7285     CompressedGlyph *charGlyphs = mCharacterGlyphs;
  7286     for (i = 0; i < runCount; ++i) {
  7287         const GlyphRun& run = mGlyphRuns[i];
  7288         gfxFont *font = run.mFont;
  7289         uint32_t start = run.mCharacterOffset;
  7290         uint32_t end = i + 1 < runCount ?
  7291             mGlyphRuns[i + 1].mCharacterOffset : GetLength();
  7292         bool fontIsSetup = false;
  7293         uint32_t j;
  7294         gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
  7296         for (j = start; j < end; ++j) {
  7297             const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
  7298             if (glyphData->IsSimpleGlyph()) {
  7299                 // If we're in speed mode, don't set up glyph extents here; we'll
  7300                 // just return "optimistic" glyph bounds later
  7301                 if (needsGlyphExtents) {
  7302                     uint32_t glyphIndex = glyphData->GetSimpleGlyph();
  7303                     if (!extents->IsGlyphKnown(glyphIndex)) {
  7304                         if (!fontIsSetup) {
  7305                             if (!font->SetupCairoFont(aRefContext)) {
  7306                                 NS_WARNING("failed to set up font for glyph extents");
  7307                                 break;
  7309                             fontIsSetup = true;
  7311 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  7312                         ++gGlyphExtentsSetupEagerSimple;
  7313 #endif
  7314                         font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
  7317             } else if (!glyphData->IsMissing()) {
  7318                 uint32_t glyphCount = glyphData->GetGlyphCount();
  7319                 if (glyphCount == 0) {
  7320                     continue;
  7322                 const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
  7323                 if (!details) {
  7324                     continue;
  7326                 for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
  7327                     uint32_t glyphIndex = details->mGlyphID;
  7328                     if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
  7329                         if (!fontIsSetup) {
  7330                             if (!font->SetupCairoFont(aRefContext)) {
  7331                                 NS_WARNING("failed to set up font for glyph extents");
  7332                                 break;
  7334                             fontIsSetup = true;
  7336 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
  7337                         ++gGlyphExtentsSetupEagerTight;
  7338 #endif
  7339                         font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
  7348 gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
  7349     : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
  7353 void
  7354 gfxTextRun::ClusterIterator::Reset()
  7356     mCurrentChar = uint32_t(-1);
  7359 bool
  7360 gfxTextRun::ClusterIterator::NextCluster()
  7362     uint32_t len = mTextRun->GetLength();
  7363     while (++mCurrentChar < len) {
  7364         if (mTextRun->IsClusterStart(mCurrentChar)) {
  7365             return true;
  7369     mCurrentChar = uint32_t(-1);
  7370     return false;
  7373 uint32_t
  7374 gfxTextRun::ClusterIterator::ClusterLength() const
  7376     if (mCurrentChar == uint32_t(-1)) {
  7377         return 0;
  7380     uint32_t i = mCurrentChar,
  7381              len = mTextRun->GetLength();
  7382     while (++i < len) {
  7383         if (mTextRun->IsClusterStart(i)) {
  7384             break;
  7388     return i - mCurrentChar;
  7391 gfxFloat
  7392 gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
  7394     if (mCurrentChar == uint32_t(-1)) {
  7395         return 0;
  7398     return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
  7401 size_t
  7402 gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
  7404     // The second arg is how much gfxTextRun::AllocateStorage would have
  7405     // allocated.
  7406     size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
  7408     if (mDetailedGlyphs) {
  7409         total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
  7412     return total;
  7415 size_t
  7416 gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
  7418     return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
  7422 #ifdef DEBUG
  7423 void
  7424 gfxTextRun::Dump(FILE* aOutput) {
  7425     if (!aOutput) {
  7426         aOutput = stdout;
  7429     uint32_t i;
  7430     fputc('[', aOutput);
  7431     for (i = 0; i < mGlyphRuns.Length(); ++i) {
  7432         if (i > 0) {
  7433             fputc(',', aOutput);
  7435         gfxFont* font = mGlyphRuns[i].mFont;
  7436         const gfxFontStyle* style = font->GetStyle();
  7437         NS_ConvertUTF16toUTF8 fontName(font->GetName());
  7438         nsAutoCString lang;
  7439         style->language->ToUTF8String(lang);
  7440         fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
  7441                 fontName.get(), style->size,
  7442                 style->weight, style->style, lang.get());
  7444     fputc(']', aOutput);
  7446 #endif

mercurial