Bumping manifests a=b2g-bump
[gecko.git] / gfx / thebes / gfxFont.cpp
blobbca507a01058016eabfb24646e5f6e36435baa09
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 "gfxGraphiteShaper.h"
27 #include "gfxHarfBuzzShaper.h"
28 #include "gfxUserFontSet.h"
29 #include "gfxPlatformFontList.h"
30 #include "gfxScriptItemizer.h"
31 #include "nsSpecialCasingData.h"
32 #include "nsTextRunTransformations.h"
33 #include "nsUnicodeProperties.h"
34 #include "nsMathUtils.h"
35 #include "nsBidiUtils.h"
36 #include "nsUnicodeRange.h"
37 #include "nsStyleConsts.h"
38 #include "mozilla/AppUnits.h"
39 #include "mozilla/FloatingPoint.h"
40 #include "mozilla/Likely.h"
41 #include "mozilla/MemoryReporting.h"
42 #include "mozilla/Preferences.h"
43 #include "mozilla/Services.h"
44 #include "mozilla/Telemetry.h"
45 #include "gfxSVGGlyphs.h"
46 #include "gfxMathTable.h"
47 #include "gfx2DGlue.h"
49 #include "GreekCasing.h"
51 #if defined(XP_MACOSX)
52 #include "nsCocoaFeatures.h"
53 #endif
55 #include "cairo.h"
56 #include "gfxFontTest.h"
58 #include "harfbuzz/hb.h"
59 #include "harfbuzz/hb-ot.h"
60 #include "graphite2/Font.h"
62 #include "nsCRT.h"
63 #include "GeckoProfiler.h"
64 #include "gfxFontConstants.h"
66 #include <algorithm>
68 using namespace mozilla;
69 using namespace mozilla::gfx;
70 using namespace mozilla::unicode;
71 using mozilla::services::GetObserverService;
73 gfxFontCache *gfxFontCache::gGlobalCache = nullptr;
75 static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
76 static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
78 #ifdef DEBUG_roc
79 #define DEBUG_TEXT_RUN_STORAGE_METRICS
80 #endif
82 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
83 static uint32_t gTextRunStorageHighWaterMark = 0;
84 static uint32_t gTextRunStorage = 0;
85 static uint32_t gFontCount = 0;
86 static uint32_t gGlyphExtentsCount = 0;
87 static uint32_t gGlyphExtentsWidthsTotalSize = 0;
88 static uint32_t gGlyphExtentsSetupEagerSimple = 0;
89 static uint32_t gGlyphExtentsSetupEagerTight = 0;
90 static uint32_t gGlyphExtentsSetupLazyTight = 0;
91 static uint32_t gGlyphExtentsSetupFallBackToTight = 0;
92 #endif
94 #ifdef PR_LOGGING
95 #define LOG_FONTINIT(args) PR_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), \
96 PR_LOG_DEBUG, args)
97 #define LOG_FONTINIT_ENABLED() PR_LOG_TEST( \
98 gfxPlatform::GetLog(eGfxLog_fontinit), \
99 PR_LOG_DEBUG)
100 #endif // PR_LOGGING
102 void
103 gfxCharacterMap::NotifyReleased()
105 gfxPlatformFontList *fontlist = gfxPlatformFontList::PlatformFontList();
106 if (mShared) {
107 fontlist->RemoveCmap(this);
109 delete this;
112 gfxFontEntry::gfxFontEntry() :
113 mItalic(false), mFixedPitch(false),
114 mIsProxy(false), mIsValid(true),
115 mIsBadUnderlineFont(false),
116 mIsUserFont(false),
117 mIsLocalUserFont(false),
118 mStandardFace(false),
119 mSymbolFont(false),
120 mIgnoreGDEF(false),
121 mIgnoreGSUB(false),
122 mSVGInitialized(false),
123 mMathInitialized(false),
124 mHasSpaceFeaturesInitialized(false),
125 mHasSpaceFeatures(false),
126 mHasSpaceFeaturesKerning(false),
127 mHasSpaceFeaturesNonKerning(false),
128 mSkipDefaultFeatureSpaceCheck(false),
129 mCheckedForGraphiteTables(false),
130 mHasCmapTable(false),
131 mGrFaceInitialized(false),
132 mCheckedForColorGlyph(false),
133 mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
134 mUVSOffset(0), mUVSData(nullptr),
135 mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
136 mCOLR(nullptr),
137 mCPAL(nullptr),
138 mUnitsPerEm(0),
139 mHBFace(nullptr),
140 mGrFace(nullptr),
141 mGrFaceRefCnt(0)
143 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
144 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
147 gfxFontEntry::gfxFontEntry(const nsAString& aName, bool aIsStandardFace) :
148 mName(aName), mItalic(false), mFixedPitch(false),
149 mIsProxy(false), mIsValid(true),
150 mIsBadUnderlineFont(false), mIsUserFont(false),
151 mIsLocalUserFont(false), mStandardFace(aIsStandardFace),
152 mSymbolFont(false),
153 mIgnoreGDEF(false),
154 mIgnoreGSUB(false),
155 mSVGInitialized(false),
156 mMathInitialized(false),
157 mHasSpaceFeaturesInitialized(false),
158 mHasSpaceFeatures(false),
159 mHasSpaceFeaturesKerning(false),
160 mHasSpaceFeaturesNonKerning(false),
161 mSkipDefaultFeatureSpaceCheck(false),
162 mCheckedForGraphiteTables(false),
163 mHasCmapTable(false),
164 mGrFaceInitialized(false),
165 mCheckedForColorGlyph(false),
166 mWeight(500), mStretch(NS_FONT_STRETCH_NORMAL),
167 mUVSOffset(0), mUVSData(nullptr),
168 mLanguageOverride(NO_FONT_LANGUAGE_OVERRIDE),
169 mCOLR(nullptr),
170 mCPAL(nullptr),
171 mUnitsPerEm(0),
172 mHBFace(nullptr),
173 mGrFace(nullptr),
174 mGrFaceRefCnt(0)
176 memset(&mDefaultSubSpaceFeatures, 0, sizeof(mDefaultSubSpaceFeatures));
177 memset(&mNonDefaultSubSpaceFeatures, 0, sizeof(mNonDefaultSubSpaceFeatures));
180 static PLDHashOperator
181 DestroyHBSet(const uint32_t& aTag, hb_set_t*& aSet, void *aUserArg)
183 hb_set_destroy(aSet);
184 return PL_DHASH_NEXT;
187 gfxFontEntry::~gfxFontEntry()
189 if (mCOLR) {
190 hb_blob_destroy(mCOLR);
193 if (mCPAL) {
194 hb_blob_destroy(mCPAL);
197 // For downloaded fonts, we need to tell the user font cache that this
198 // entry is being deleted.
199 if (!mIsProxy && IsUserFont() && !IsLocalUserFont()) {
200 gfxUserFontSet::UserFontCache::ForgetFont(this);
203 if (mFeatureInputs) {
204 mFeatureInputs->Enumerate(DestroyHBSet, nullptr);
207 // By the time the entry is destroyed, all font instances that were
208 // using it should already have been deleted, and so the HB and/or Gr
209 // face objects should have been released.
210 MOZ_ASSERT(!mHBFace);
211 MOZ_ASSERT(!mGrFaceInitialized);
214 bool gfxFontEntry::IsSymbolFont()
216 return mSymbolFont;
219 bool gfxFontEntry::TestCharacterMap(uint32_t aCh)
221 if (!mCharacterMap) {
222 ReadCMAP();
223 NS_ASSERTION(mCharacterMap, "failed to initialize character map");
225 return mCharacterMap->test(aCh);
228 nsresult gfxFontEntry::InitializeUVSMap()
230 // mUVSOffset will not be initialized
231 // until cmap is initialized.
232 if (!mCharacterMap) {
233 ReadCMAP();
234 NS_ASSERTION(mCharacterMap, "failed to initialize character map");
237 if (!mUVSOffset) {
238 return NS_ERROR_FAILURE;
241 if (!mUVSData) {
242 const uint32_t kCmapTag = TRUETYPE_TAG('c','m','a','p');
243 AutoTable cmapTable(this, kCmapTag);
244 if (!cmapTable) {
245 mUVSOffset = 0; // don't bother to read the table again
246 return NS_ERROR_FAILURE;
249 uint8_t* uvsData;
250 unsigned int cmapLen;
251 const char* cmapData = hb_blob_get_data(cmapTable, &cmapLen);
252 nsresult rv = gfxFontUtils::ReadCMAPTableFormat14(
253 (const uint8_t*)cmapData + mUVSOffset,
254 cmapLen - mUVSOffset, uvsData);
256 if (NS_FAILED(rv)) {
257 mUVSOffset = 0; // don't bother to read the table again
258 return rv;
261 mUVSData = uvsData;
264 return NS_OK;
267 uint16_t gfxFontEntry::GetUVSGlyph(uint32_t aCh, uint32_t aVS)
269 InitializeUVSMap();
271 if (mUVSData) {
272 return gfxFontUtils::MapUVSToGlyphFormat14(mUVSData, aCh, aVS);
275 return 0;
278 bool gfxFontEntry::SupportsScriptInGSUB(const hb_tag_t* aScriptTags)
280 hb_face_t *face = GetHBFace();
281 if (!face) {
282 return false;
285 unsigned int index;
286 hb_tag_t chosenScript;
287 bool found =
288 hb_ot_layout_table_choose_script(face, TRUETYPE_TAG('G','S','U','B'),
289 aScriptTags, &index, &chosenScript);
290 hb_face_destroy(face);
292 return found && chosenScript != TRUETYPE_TAG('D','F','L','T');
295 nsresult gfxFontEntry::ReadCMAP(FontInfoData *aFontInfoData)
297 NS_ASSERTION(false, "using default no-op implementation of ReadCMAP");
298 mCharacterMap = new gfxCharacterMap();
299 return NS_OK;
302 nsString
303 gfxFontEntry::RealFaceName()
305 AutoTable nameTable(this, TRUETYPE_TAG('n','a','m','e'));
306 if (nameTable) {
307 nsAutoString name;
308 nsresult rv = gfxFontUtils::GetFullNameFromTable(nameTable, name);
309 if (NS_SUCCEEDED(rv)) {
310 return name;
313 return Name();
316 already_AddRefed<gfxFont>
317 gfxFontEntry::FindOrMakeFont(const gfxFontStyle *aStyle, bool aNeedsBold)
319 // the font entry name is the psname, not the family name
320 nsRefPtr<gfxFont> font = gfxFontCache::GetCache()->Lookup(this, aStyle);
322 if (!font) {
323 gfxFont *newFont = CreateFontInstance(aStyle, aNeedsBold);
324 if (!newFont)
325 return nullptr;
326 if (!newFont->Valid()) {
327 delete newFont;
328 return nullptr;
330 font = newFont;
331 gfxFontCache::GetCache()->AddNew(font);
333 return font.forget();
336 uint16_t
337 gfxFontEntry::UnitsPerEm()
339 if (!mUnitsPerEm) {
340 AutoTable headTable(this, TRUETYPE_TAG('h','e','a','d'));
341 if (headTable) {
342 uint32_t len;
343 const HeadTable* head =
344 reinterpret_cast<const HeadTable*>(hb_blob_get_data(headTable,
345 &len));
346 if (len >= sizeof(HeadTable)) {
347 mUnitsPerEm = head->unitsPerEm;
351 // if we didn't find a usable 'head' table, or if the value was
352 // outside the valid range, record it as invalid
353 if (mUnitsPerEm < kMinUPEM || mUnitsPerEm > kMaxUPEM) {
354 mUnitsPerEm = kInvalidUPEM;
357 return mUnitsPerEm;
360 bool
361 gfxFontEntry::HasSVGGlyph(uint32_t aGlyphId)
363 NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
364 return mSVGGlyphs->HasSVGGlyph(aGlyphId);
367 bool
368 gfxFontEntry::GetSVGGlyphExtents(gfxContext *aContext, uint32_t aGlyphId,
369 gfxRect *aResult)
371 NS_ABORT_IF_FALSE(mSVGInitialized,
372 "SVG data has not yet been loaded. TryGetSVGData() first.");
373 NS_ABORT_IF_FALSE(mUnitsPerEm >= kMinUPEM && mUnitsPerEm <= kMaxUPEM,
374 "font has invalid unitsPerEm");
376 gfxContextAutoSaveRestore matrixRestore(aContext);
377 cairo_matrix_t fontMatrix;
378 cairo_get_font_matrix(aContext->GetCairo(), &fontMatrix);
380 gfxMatrix svgToAppSpace = *reinterpret_cast<gfxMatrix*>(&fontMatrix);
381 svgToAppSpace.Scale(1.0f / mUnitsPerEm, 1.0f / mUnitsPerEm);
383 return mSVGGlyphs->GetGlyphExtents(aGlyphId, svgToAppSpace, aResult);
386 bool
387 gfxFontEntry::RenderSVGGlyph(gfxContext *aContext, uint32_t aGlyphId,
388 int aDrawMode, gfxTextContextPaint *aContextPaint)
390 NS_ASSERTION(mSVGInitialized, "SVG data has not yet been loaded. TryGetSVGData() first.");
391 return mSVGGlyphs->RenderGlyph(aContext, aGlyphId, DrawMode(aDrawMode),
392 aContextPaint);
395 bool
396 gfxFontEntry::TryGetSVGData(gfxFont* aFont)
398 if (!gfxPlatform::GetPlatform()->OpenTypeSVGEnabled()) {
399 return false;
402 if (!mSVGInitialized) {
403 mSVGInitialized = true;
405 // If UnitsPerEm is not known/valid, we can't use SVG glyphs
406 if (UnitsPerEm() == kInvalidUPEM) {
407 return false;
410 // We don't use AutoTable here because we'll pass ownership of this
411 // blob to the gfxSVGGlyphs, once we've confirmed the table exists
412 hb_blob_t *svgTable = GetFontTable(TRUETYPE_TAG('S','V','G',' '));
413 if (!svgTable) {
414 return false;
417 // gfxSVGGlyphs will hb_blob_destroy() the table when it is finished
418 // with it.
419 mSVGGlyphs = new gfxSVGGlyphs(svgTable, this);
422 if (!mFontsUsingSVGGlyphs.Contains(aFont)) {
423 mFontsUsingSVGGlyphs.AppendElement(aFont);
426 return !!mSVGGlyphs;
429 void
430 gfxFontEntry::NotifyFontDestroyed(gfxFont* aFont)
432 mFontsUsingSVGGlyphs.RemoveElement(aFont);
435 void
436 gfxFontEntry::NotifyGlyphsChanged()
438 for (uint32_t i = 0, count = mFontsUsingSVGGlyphs.Length(); i < count; ++i) {
439 gfxFont* font = mFontsUsingSVGGlyphs[i];
440 font->NotifyGlyphsChanged();
444 bool
445 gfxFontEntry::TryGetMathTable()
447 if (!mMathInitialized) {
448 mMathInitialized = true;
450 // If UnitsPerEm is not known/valid, we can't use MATH table
451 if (UnitsPerEm() == kInvalidUPEM) {
452 return false;
455 // We don't use AutoTable here because we'll pass ownership of this
456 // blob to the gfxMathTable, once we've confirmed the table exists
457 hb_blob_t *mathTable = GetFontTable(TRUETYPE_TAG('M','A','T','H'));
458 if (!mathTable) {
459 return false;
462 // gfxMathTable will hb_blob_destroy() the table when it is finished
463 // with it.
464 mMathTable = new gfxMathTable(mathTable);
465 if (!mMathTable->HasValidHeaders()) {
466 mMathTable = nullptr;
467 return false;
471 return !!mMathTable;
474 gfxFloat
475 gfxFontEntry::GetMathConstant(gfxFontEntry::MathConstant aConstant)
477 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
478 gfxFloat value = mMathTable->GetMathConstant(aConstant);
479 if (aConstant == gfxFontEntry::ScriptPercentScaleDown ||
480 aConstant == gfxFontEntry::ScriptScriptPercentScaleDown ||
481 aConstant == gfxFontEntry::RadicalDegreeBottomRaisePercent) {
482 return value / 100.0;
484 return value / mUnitsPerEm;
487 bool
488 gfxFontEntry::GetMathItalicsCorrection(uint32_t aGlyphID,
489 gfxFloat* aItalicCorrection)
491 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
492 int16_t italicCorrection;
493 if (!mMathTable->GetMathItalicsCorrection(aGlyphID, &italicCorrection)) {
494 return false;
496 *aItalicCorrection = gfxFloat(italicCorrection) / mUnitsPerEm;
497 return true;
500 uint32_t
501 gfxFontEntry::GetMathVariantsSize(uint32_t aGlyphID, bool aVertical,
502 uint16_t aSize)
504 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
505 return mMathTable->GetMathVariantsSize(aGlyphID, aVertical, aSize);
508 bool
509 gfxFontEntry::GetMathVariantsParts(uint32_t aGlyphID, bool aVertical,
510 uint32_t aGlyphs[4])
512 NS_ASSERTION(mMathTable, "Math data has not yet been loaded. TryGetMathData() first.");
513 return mMathTable->GetMathVariantsParts(aGlyphID, aVertical, aGlyphs);
516 bool
517 gfxFontEntry::TryGetColorGlyphs()
519 if (mCheckedForColorGlyph) {
520 return (mCOLR && mCPAL);
523 mCheckedForColorGlyph = true;
525 mCOLR = GetFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'));
526 if (!mCOLR) {
527 return false;
530 mCPAL = GetFontTable(TRUETYPE_TAG('C', 'P', 'A', 'L'));
531 if (!mCPAL) {
532 hb_blob_destroy(mCOLR);
533 mCOLR = nullptr;
534 return false;
537 // validation COLR and CPAL table
538 if (gfxFontUtils::ValidateColorGlyphs(mCOLR, mCPAL)) {
539 return true;
542 hb_blob_destroy(mCOLR);
543 hb_blob_destroy(mCPAL);
544 mCOLR = nullptr;
545 mCPAL = nullptr;
546 return false;
550 * FontTableBlobData
552 * See FontTableHashEntry for the general strategy.
555 class gfxFontEntry::FontTableBlobData {
556 public:
557 // Adopts the content of aBuffer.
558 explicit FontTableBlobData(FallibleTArray<uint8_t>& aBuffer)
559 : mHashtable(nullptr), mHashKey(0)
561 MOZ_COUNT_CTOR(FontTableBlobData);
562 mTableData.SwapElements(aBuffer);
565 ~FontTableBlobData() {
566 MOZ_COUNT_DTOR(FontTableBlobData);
567 if (mHashtable && mHashKey) {
568 mHashtable->RemoveEntry(mHashKey);
572 // Useful for creating blobs
573 const char *GetTable() const
575 return reinterpret_cast<const char*>(mTableData.Elements());
577 uint32_t GetTableLength() const { return mTableData.Length(); }
579 // Tell this FontTableBlobData to remove the HashEntry when this is
580 // destroyed.
581 void ManageHashEntry(nsTHashtable<FontTableHashEntry> *aHashtable,
582 uint32_t aHashKey)
584 mHashtable = aHashtable;
585 mHashKey = aHashKey;
588 // Disconnect from the HashEntry (because the blob has already been
589 // removed from the hashtable).
590 void ForgetHashEntry()
592 mHashtable = nullptr;
593 mHashKey = 0;
596 size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
597 return mTableData.SizeOfExcludingThis(aMallocSizeOf);
599 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
600 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
603 private:
604 // The font table data block, owned (via adoption)
605 FallibleTArray<uint8_t> mTableData;
607 // The blob destroy function needs to know the owning hashtable
608 // and the hashtable key, so that it can remove the entry.
609 nsTHashtable<FontTableHashEntry> *mHashtable;
610 uint32_t mHashKey;
612 // not implemented
613 FontTableBlobData(const FontTableBlobData&);
616 hb_blob_t *
617 gfxFontEntry::FontTableHashEntry::
618 ShareTableAndGetBlob(FallibleTArray<uint8_t>& aTable,
619 nsTHashtable<FontTableHashEntry> *aHashtable)
621 Clear();
622 // adopts elements of aTable
623 mSharedBlobData = new FontTableBlobData(aTable);
624 mBlob = hb_blob_create(mSharedBlobData->GetTable(),
625 mSharedBlobData->GetTableLength(),
626 HB_MEMORY_MODE_READONLY,
627 mSharedBlobData, DeleteFontTableBlobData);
628 if (!mSharedBlobData) {
629 // The FontTableBlobData was destroyed during hb_blob_create().
630 // The (empty) blob is still be held in the hashtable with a strong
631 // reference.
632 return hb_blob_reference(mBlob);
635 // Tell the FontTableBlobData to remove this hash entry when destroyed.
636 // The hashtable does not keep a strong reference.
637 mSharedBlobData->ManageHashEntry(aHashtable, GetKey());
638 return mBlob;
641 void
642 gfxFontEntry::FontTableHashEntry::Clear()
644 // If the FontTableBlobData is managing the hash entry, then the blob is
645 // not owned by this HashEntry; otherwise there is strong reference to the
646 // blob that must be removed.
647 if (mSharedBlobData) {
648 mSharedBlobData->ForgetHashEntry();
649 mSharedBlobData = nullptr;
650 } else if (mBlob) {
651 hb_blob_destroy(mBlob);
653 mBlob = nullptr;
656 // a hb_destroy_func for hb_blob_create
658 /* static */ void
659 gfxFontEntry::FontTableHashEntry::DeleteFontTableBlobData(void *aBlobData)
661 delete static_cast<FontTableBlobData*>(aBlobData);
664 hb_blob_t *
665 gfxFontEntry::FontTableHashEntry::GetBlob() const
667 return hb_blob_reference(mBlob);
670 bool
671 gfxFontEntry::GetExistingFontTable(uint32_t aTag, hb_blob_t **aBlob)
673 if (!mFontTableCache) {
674 // we do this here rather than on fontEntry construction
675 // because not all shapers will access the table cache at all
676 mFontTableCache = new nsTHashtable<FontTableHashEntry>(8);
679 FontTableHashEntry *entry = mFontTableCache->GetEntry(aTag);
680 if (!entry) {
681 return false;
684 *aBlob = entry->GetBlob();
685 return true;
688 hb_blob_t *
689 gfxFontEntry::ShareFontTableAndGetBlob(uint32_t aTag,
690 FallibleTArray<uint8_t>* aBuffer)
692 if (MOZ_UNLIKELY(!mFontTableCache)) {
693 // we do this here rather than on fontEntry construction
694 // because not all shapers will access the table cache at all
695 mFontTableCache = new nsTHashtable<FontTableHashEntry>(8);
698 FontTableHashEntry *entry = mFontTableCache->PutEntry(aTag);
699 if (MOZ_UNLIKELY(!entry)) { // OOM
700 return nullptr;
703 if (!aBuffer) {
704 // ensure the entry is null
705 entry->Clear();
706 return nullptr;
709 return entry->ShareTableAndGetBlob(*aBuffer, mFontTableCache);
712 static int
713 DirEntryCmp(const void* aKey, const void* aItem)
715 int32_t tag = *static_cast<const int32_t*>(aKey);
716 const TableDirEntry* entry = static_cast<const TableDirEntry*>(aItem);
717 return tag - int32_t(entry->tag);
720 hb_blob_t*
721 gfxFontEntry::GetTableFromFontData(const void* aFontData, uint32_t aTableTag)
723 const SFNTHeader* header =
724 reinterpret_cast<const SFNTHeader*>(aFontData);
725 const TableDirEntry* dir =
726 reinterpret_cast<const TableDirEntry*>(header + 1);
727 dir = static_cast<const TableDirEntry*>
728 (bsearch(&aTableTag, dir, uint16_t(header->numTables),
729 sizeof(TableDirEntry), DirEntryCmp));
730 if (dir) {
731 return hb_blob_create(reinterpret_cast<const char*>(aFontData) +
732 dir->offset, dir->length,
733 HB_MEMORY_MODE_READONLY, nullptr, nullptr);
736 return nullptr;
739 already_AddRefed<gfxCharacterMap>
740 gfxFontEntry::GetCMAPFromFontInfo(FontInfoData *aFontInfoData,
741 uint32_t& aUVSOffset,
742 bool& aSymbolFont)
744 if (!aFontInfoData || !aFontInfoData->mLoadCmaps) {
745 return nullptr;
748 return aFontInfoData->GetCMAP(mName, aUVSOffset, aSymbolFont);
751 hb_blob_t *
752 gfxFontEntry::GetFontTable(uint32_t aTag)
754 hb_blob_t *blob;
755 if (GetExistingFontTable(aTag, &blob)) {
756 return blob;
759 FallibleTArray<uint8_t> buffer;
760 bool haveTable = NS_SUCCEEDED(CopyFontTable(aTag, buffer));
762 return ShareFontTableAndGetBlob(aTag, haveTable ? &buffer : nullptr);
765 // callback for HarfBuzz to get a font table (in hb_blob_t form)
766 // from the font entry (passed as aUserData)
767 /*static*/ hb_blob_t *
768 gfxFontEntry::HBGetTable(hb_face_t *face, uint32_t aTag, void *aUserData)
770 gfxFontEntry *fontEntry = static_cast<gfxFontEntry*>(aUserData);
772 // bug 589682 - ignore the GDEF table in buggy fonts (applies to
773 // Italic and BoldItalic faces of Times New Roman)
774 if (aTag == TRUETYPE_TAG('G','D','E','F') &&
775 fontEntry->IgnoreGDEF()) {
776 return nullptr;
779 // bug 721719 - ignore the GSUB table in buggy fonts (applies to Roboto,
780 // at least on some Android ICS devices; set in gfxFT2FontList.cpp)
781 if (aTag == TRUETYPE_TAG('G','S','U','B') &&
782 fontEntry->IgnoreGSUB()) {
783 return nullptr;
786 return fontEntry->GetFontTable(aTag);
789 /*static*/ void
790 gfxFontEntry::HBFaceDeletedCallback(void *aUserData)
792 gfxFontEntry *fe = static_cast<gfxFontEntry*>(aUserData);
793 fe->ForgetHBFace();
796 void
797 gfxFontEntry::ForgetHBFace()
799 mHBFace = nullptr;
802 hb_face_t*
803 gfxFontEntry::GetHBFace()
805 if (!mHBFace) {
806 mHBFace = hb_face_create_for_tables(HBGetTable, this,
807 HBFaceDeletedCallback);
808 return mHBFace;
810 return hb_face_reference(mHBFace);
813 /*static*/ const void*
814 gfxFontEntry::GrGetTable(const void *aAppFaceHandle, unsigned int aName,
815 size_t *aLen)
817 gfxFontEntry *fontEntry =
818 static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
819 hb_blob_t *blob = fontEntry->GetFontTable(aName);
820 if (blob) {
821 unsigned int blobLength;
822 const void *tableData = hb_blob_get_data(blob, &blobLength);
823 fontEntry->mGrTableMap->Put(tableData, blob);
824 *aLen = blobLength;
825 return tableData;
827 *aLen = 0;
828 return nullptr;
831 /*static*/ void
832 gfxFontEntry::GrReleaseTable(const void *aAppFaceHandle,
833 const void *aTableBuffer)
835 gfxFontEntry *fontEntry =
836 static_cast<gfxFontEntry*>(const_cast<void*>(aAppFaceHandle));
837 void *data;
838 if (fontEntry->mGrTableMap->Get(aTableBuffer, &data)) {
839 fontEntry->mGrTableMap->Remove(aTableBuffer);
840 hb_blob_destroy(static_cast<hb_blob_t*>(data));
844 gr_face*
845 gfxFontEntry::GetGrFace()
847 if (!mGrFaceInitialized) {
848 gr_face_ops faceOps = {
849 sizeof(gr_face_ops),
850 GrGetTable,
851 GrReleaseTable
853 mGrTableMap = new nsDataHashtable<nsPtrHashKey<const void>,void*>;
854 mGrFace = gr_make_face_with_ops(this, &faceOps, gr_face_default);
855 mGrFaceInitialized = true;
857 ++mGrFaceRefCnt;
858 return mGrFace;
861 void
862 gfxFontEntry::ReleaseGrFace(gr_face *aFace)
864 MOZ_ASSERT(aFace == mGrFace); // sanity-check
865 MOZ_ASSERT(mGrFaceRefCnt > 0);
866 if (--mGrFaceRefCnt == 0) {
867 gr_face_destroy(mGrFace);
868 mGrFace = nullptr;
869 mGrFaceInitialized = false;
870 delete mGrTableMap;
871 mGrTableMap = nullptr;
875 void
876 gfxFontEntry::DisconnectSVG()
878 if (mSVGInitialized && mSVGGlyphs) {
879 mSVGGlyphs = nullptr;
880 mSVGInitialized = false;
884 bool
885 gfxFontEntry::HasFontTable(uint32_t aTableTag)
887 AutoTable table(this, aTableTag);
888 return table && hb_blob_get_length(table) > 0;
891 void
892 gfxFontEntry::CheckForGraphiteTables()
894 mHasGraphiteTables = HasFontTable(TRUETYPE_TAG('S','i','l','f'));
898 #define FEATURE_SCRIPT_MASK 0x000000ff // script index replaces low byte of tag
900 // check for too many script codes
901 PR_STATIC_ASSERT(MOZ_NUM_SCRIPT_CODES <= FEATURE_SCRIPT_MASK);
903 // high-order three bytes of tag with script in low-order byte
904 #define SCRIPT_FEATURE(s,tag) (((~FEATURE_SCRIPT_MASK) & (tag)) | \
905 ((FEATURE_SCRIPT_MASK) & (s)))
907 bool
908 gfxFontEntry::SupportsOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
910 if (!mSupportedFeatures) {
911 mSupportedFeatures = new nsDataHashtable<nsUint32HashKey,bool>();
914 // note: high-order three bytes *must* be unique for each feature
915 // listed below (see SCRIPT_FEATURE macro def'n)
916 NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
917 aFeatureTag == HB_TAG('c','2','s','c') ||
918 aFeatureTag == HB_TAG('p','c','a','p') ||
919 aFeatureTag == HB_TAG('c','2','p','c') ||
920 aFeatureTag == HB_TAG('s','u','p','s') ||
921 aFeatureTag == HB_TAG('s','u','b','s'),
922 "use of unknown feature tag");
924 // note: graphite feature support uses the last script index
925 NS_ASSERTION(aScript < FEATURE_SCRIPT_MASK - 1,
926 "need to bump the size of the feature shift");
928 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
929 bool result;
930 if (mSupportedFeatures->Get(scriptFeature, &result)) {
931 return result;
934 result = false;
936 hb_face_t *face = GetHBFace();
938 if (hb_ot_layout_has_substitution(face)) {
939 hb_script_t hbScript =
940 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
942 // Get the OpenType tag(s) that match this script code
943 hb_tag_t scriptTags[4] = {
944 HB_TAG_NONE,
945 HB_TAG_NONE,
946 HB_TAG_NONE,
947 HB_TAG_NONE
949 hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
951 // Replace the first remaining NONE with DEFAULT
952 hb_tag_t* scriptTag = &scriptTags[0];
953 while (*scriptTag != HB_TAG_NONE) {
954 ++scriptTag;
956 *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
958 // Now check for 'smcp' under the first of those scripts that is present
959 const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
960 scriptTag = &scriptTags[0];
961 while (*scriptTag != HB_TAG_NONE) {
962 unsigned int scriptIndex;
963 if (hb_ot_layout_table_find_script(face, kGSUB, *scriptTag,
964 &scriptIndex)) {
965 if (hb_ot_layout_language_find_feature(face, kGSUB,
966 scriptIndex,
967 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX,
968 aFeatureTag, nullptr)) {
969 result = true;
971 break;
973 ++scriptTag;
977 hb_face_destroy(face);
979 mSupportedFeatures->Put(scriptFeature, result);
981 return result;
984 const hb_set_t*
985 gfxFontEntry::InputsForOpenTypeFeature(int32_t aScript, uint32_t aFeatureTag)
987 if (!mFeatureInputs) {
988 mFeatureInputs = new nsDataHashtable<nsUint32HashKey,hb_set_t*>();
991 NS_ASSERTION(aFeatureTag == HB_TAG('s','u','p','s') ||
992 aFeatureTag == HB_TAG('s','u','b','s'),
993 "use of unknown feature tag");
995 uint32_t scriptFeature = SCRIPT_FEATURE(aScript, aFeatureTag);
996 hb_set_t *inputGlyphs;
997 if (mFeatureInputs->Get(scriptFeature, &inputGlyphs)) {
998 return inputGlyphs;
1001 inputGlyphs = hb_set_create();
1003 hb_face_t *face = GetHBFace();
1005 if (hb_ot_layout_has_substitution(face)) {
1006 hb_script_t hbScript =
1007 gfxHarfBuzzShaper::GetHBScriptUsedForShaping(aScript);
1009 // Get the OpenType tag(s) that match this script code
1010 hb_tag_t scriptTags[4] = {
1011 HB_TAG_NONE,
1012 HB_TAG_NONE,
1013 HB_TAG_NONE,
1014 HB_TAG_NONE
1016 hb_ot_tags_from_script(hbScript, &scriptTags[0], &scriptTags[1]);
1018 // Replace the first remaining NONE with DEFAULT
1019 hb_tag_t* scriptTag = &scriptTags[0];
1020 while (*scriptTag != HB_TAG_NONE) {
1021 ++scriptTag;
1023 *scriptTag = HB_OT_TAG_DEFAULT_SCRIPT;
1025 const hb_tag_t kGSUB = HB_TAG('G','S','U','B');
1026 hb_tag_t features[2] = { aFeatureTag, HB_TAG_NONE };
1027 hb_set_t *featurelookups = hb_set_create();
1028 hb_ot_layout_collect_lookups(face, kGSUB, scriptTags, nullptr,
1029 features, featurelookups);
1030 hb_codepoint_t index = -1;
1031 while (hb_set_next(featurelookups, &index)) {
1032 hb_ot_layout_lookup_collect_glyphs(face, kGSUB, index,
1033 nullptr, inputGlyphs,
1034 nullptr, nullptr);
1038 hb_face_destroy(face);
1040 mFeatureInputs->Put(scriptFeature, inputGlyphs);
1041 return inputGlyphs;
1044 bool
1045 gfxFontEntry::SupportsGraphiteFeature(uint32_t aFeatureTag)
1047 if (!mSupportedFeatures) {
1048 mSupportedFeatures = new nsDataHashtable<nsUint32HashKey,bool>();
1051 // note: high-order three bytes *must* be unique for each feature
1052 // listed below (see SCRIPT_FEATURE macro def'n)
1053 NS_ASSERTION(aFeatureTag == HB_TAG('s','m','c','p') ||
1054 aFeatureTag == HB_TAG('c','2','s','c') ||
1055 aFeatureTag == HB_TAG('p','c','a','p') ||
1056 aFeatureTag == HB_TAG('c','2','p','c') ||
1057 aFeatureTag == HB_TAG('s','u','p','s') ||
1058 aFeatureTag == HB_TAG('s','u','b','s'),
1059 "use of unknown feature tag");
1061 // graphite feature check uses the last script slot
1062 uint32_t scriptFeature = SCRIPT_FEATURE(FEATURE_SCRIPT_MASK, aFeatureTag);
1063 bool result;
1064 if (mSupportedFeatures->Get(scriptFeature, &result)) {
1065 return result;
1068 gr_face* face = GetGrFace();
1069 result = gr_face_find_fref(face, aFeatureTag) != nullptr;
1070 ReleaseGrFace(face);
1072 mSupportedFeatures->Put(scriptFeature, result);
1074 return result;
1077 bool
1078 gfxFontEntry::GetColorLayersInfo(uint32_t aGlyphId,
1079 nsTArray<uint16_t>& aLayerGlyphs,
1080 nsTArray<mozilla::gfx::Color>& aLayerColors)
1082 return gfxFontUtils::GetColorGlyphLayers(mCOLR,
1083 mCPAL,
1084 aGlyphId,
1085 aLayerGlyphs,
1086 aLayerColors);
1089 size_t
1090 gfxFontEntry::FontTableHashEntry::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
1092 size_t n = 0;
1093 if (mBlob) {
1094 n += aMallocSizeOf(mBlob);
1096 if (mSharedBlobData) {
1097 n += mSharedBlobData->SizeOfIncludingThis(aMallocSizeOf);
1099 return n;
1102 void
1103 gfxFontEntry::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1104 FontListSizes* aSizes) const
1106 aSizes->mFontListSize += mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1108 // cmaps are shared so only non-shared cmaps are included here
1109 if (mCharacterMap && mCharacterMap->mBuildOnTheFly) {
1110 aSizes->mCharMapsSize +=
1111 mCharacterMap->SizeOfIncludingThis(aMallocSizeOf);
1113 if (mFontTableCache) {
1114 aSizes->mFontTableCacheSize +=
1115 mFontTableCache->SizeOfIncludingThis(aMallocSizeOf);
1119 void
1120 gfxFontEntry::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1121 FontListSizes* aSizes) const
1123 aSizes->mFontListSize += aMallocSizeOf(this);
1124 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1127 //////////////////////////////////////////////////////////////////////////////
1129 // class gfxFontFamily
1131 //////////////////////////////////////////////////////////////////////////////
1133 // we consider faces with mStandardFace == true to be "greater than" those with false,
1134 // because during style matching, later entries will replace earlier ones
1135 class FontEntryStandardFaceComparator {
1136 public:
1137 bool Equals(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
1138 return a->mStandardFace == b->mStandardFace;
1140 bool LessThan(const nsRefPtr<gfxFontEntry>& a, const nsRefPtr<gfxFontEntry>& b) const {
1141 return (a->mStandardFace == false && b->mStandardFace == true);
1145 void
1146 gfxFontFamily::SortAvailableFonts()
1148 mAvailableFonts.Sort(FontEntryStandardFaceComparator());
1151 bool
1152 gfxFontFamily::HasOtherFamilyNames()
1154 // need to read in other family names to determine this
1155 if (!mOtherFamilyNamesInitialized) {
1156 ReadOtherFamilyNames(gfxPlatformFontList::PlatformFontList()); // sets mHasOtherFamilyNames
1158 return mHasOtherFamilyNames;
1161 gfxFontEntry*
1162 gfxFontFamily::FindFontForStyle(const gfxFontStyle& aFontStyle,
1163 bool& aNeedsSyntheticBold)
1165 if (!mHasStyles)
1166 FindStyleVariations(); // collect faces for the family, if not already done
1168 NS_ASSERTION(mAvailableFonts.Length() > 0, "font family with no faces!");
1170 aNeedsSyntheticBold = false;
1172 int8_t baseWeight = aFontStyle.ComputeWeight();
1173 bool wantBold = baseWeight >= 6;
1175 // If the family has only one face, we simply return it; no further checking needed
1176 if (mAvailableFonts.Length() == 1) {
1177 gfxFontEntry *fe = mAvailableFonts[0];
1178 aNeedsSyntheticBold =
1179 wantBold && !fe->IsBold() && aFontStyle.allowSyntheticWeight;
1180 return fe;
1183 bool wantItalic = (aFontStyle.style &
1184 (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
1186 // Most families are "simple", having just Regular/Bold/Italic/BoldItalic,
1187 // or some subset of these. In this case, we have exactly 4 entries in mAvailableFonts,
1188 // stored in the above order; note that some of the entries may be nullptr.
1189 // We can then pick the required entry based on whether the request is for
1190 // bold or non-bold, italic or non-italic, without running the more complex
1191 // matching algorithm used for larger families with many weights and/or widths.
1193 if (mIsSimpleFamily) {
1194 // Family has no more than the "standard" 4 faces, at fixed indexes;
1195 // calculate which one we want.
1196 // Note that we cannot simply return it as not all 4 faces are necessarily present.
1197 uint8_t faceIndex = (wantItalic ? kItalicMask : 0) |
1198 (wantBold ? kBoldMask : 0);
1200 // if the desired style is available, return it directly
1201 gfxFontEntry *fe = mAvailableFonts[faceIndex];
1202 if (fe) {
1203 // no need to set aNeedsSyntheticBold here as we matched the boldness request
1204 return fe;
1207 // order to check fallback faces in a simple family, depending on requested style
1208 static const uint8_t simpleFallbacks[4][3] = {
1209 { kBoldFaceIndex, kItalicFaceIndex, kBoldItalicFaceIndex }, // fallbacks for Regular
1210 { kRegularFaceIndex, kBoldItalicFaceIndex, kItalicFaceIndex },// Bold
1211 { kBoldItalicFaceIndex, kRegularFaceIndex, kBoldFaceIndex }, // Italic
1212 { kItalicFaceIndex, kBoldFaceIndex, kRegularFaceIndex } // BoldItalic
1214 const uint8_t *order = simpleFallbacks[faceIndex];
1216 for (uint8_t trial = 0; trial < 3; ++trial) {
1217 // check remaining faces in order of preference to find the first that actually exists
1218 fe = mAvailableFonts[order[trial]];
1219 if (fe) {
1220 aNeedsSyntheticBold =
1221 wantBold && !fe->IsBold() &&
1222 aFontStyle.allowSyntheticWeight;
1223 return fe;
1227 // this can't happen unless we have totally broken the font-list manager!
1228 NS_NOTREACHED("no face found in simple font family!");
1229 return nullptr;
1232 // This is a large/rich font family, so we do full style- and weight-matching:
1233 // first collect a list of weights that are the best match for the requested
1234 // font-stretch and font-style, then pick the best weight match among those
1235 // available.
1237 gfxFontEntry *weightList[10] = { 0 };
1238 bool foundWeights = FindWeightsForStyle(weightList, wantItalic, aFontStyle.stretch);
1239 if (!foundWeights) {
1240 return nullptr;
1243 // First find a match for the best weight
1244 int8_t matchBaseWeight = 0;
1245 int8_t i = baseWeight;
1247 // Need to special case when normal face doesn't exist but medium does.
1248 // In that case, use medium otherwise weights < 400
1249 if (baseWeight == 4 && !weightList[4]) {
1250 i = 5; // medium
1253 // Loop through weights, since one exists loop will terminate
1254 int8_t direction = (baseWeight > 5) ? 1 : -1;
1255 for (; ; i += direction) {
1256 if (weightList[i]) {
1257 matchBaseWeight = i;
1258 break;
1261 // If we've reached one side without finding a font,
1262 // start over and go the other direction until we find a match
1263 if (i == 1 || i == 9) {
1264 i = baseWeight;
1265 direction = -direction;
1269 NS_ASSERTION(matchBaseWeight != 0,
1270 "weight mapping should always find at least one font in a family");
1272 gfxFontEntry *matchFE = weightList[matchBaseWeight];
1274 NS_ASSERTION(matchFE,
1275 "weight mapping should always find at least one font in a family");
1277 if (!matchFE->IsBold() && baseWeight >= 6 &&
1278 aFontStyle.allowSyntheticWeight)
1280 aNeedsSyntheticBold = true;
1283 return matchFE;
1286 void
1287 gfxFontFamily::CheckForSimpleFamily()
1289 // already checked this family
1290 if (mIsSimpleFamily) {
1291 return;
1294 uint32_t count = mAvailableFonts.Length();
1295 if (count > 4 || count == 0) {
1296 return; // can't be "simple" if there are >4 faces;
1297 // if none then the family is unusable anyway
1300 if (count == 1) {
1301 mIsSimpleFamily = true;
1302 return;
1305 int16_t firstStretch = mAvailableFonts[0]->Stretch();
1307 gfxFontEntry *faces[4] = { 0 };
1308 for (uint8_t i = 0; i < count; ++i) {
1309 gfxFontEntry *fe = mAvailableFonts[i];
1310 if (fe->Stretch() != firstStretch) {
1311 return; // font-stretch doesn't match, don't treat as simple family
1313 uint8_t faceIndex = (fe->IsItalic() ? kItalicMask : 0) |
1314 (fe->Weight() >= 600 ? kBoldMask : 0);
1315 if (faces[faceIndex]) {
1316 return; // two faces resolve to the same slot; family isn't "simple"
1318 faces[faceIndex] = fe;
1321 // we have successfully slotted the available faces into the standard
1322 // 4-face framework
1323 mAvailableFonts.SetLength(4);
1324 for (uint8_t i = 0; i < 4; ++i) {
1325 if (mAvailableFonts[i].get() != faces[i]) {
1326 mAvailableFonts[i].swap(faces[i]);
1330 mIsSimpleFamily = true;
1333 static inline uint32_t
1334 StyleDistance(gfxFontEntry *aFontEntry,
1335 bool anItalic, int16_t aStretch)
1337 // Compute a measure of the "distance" between the requested style
1338 // and the given fontEntry,
1339 // considering italicness and font-stretch but not weight.
1341 int32_t distance = 0;
1342 if (aStretch != aFontEntry->mStretch) {
1343 // stretch values are in the range -4 .. +4
1344 // if aStretch is positive, we prefer more-positive values;
1345 // if zero or negative, prefer more-negative
1346 if (aStretch > 0) {
1347 distance = (aFontEntry->mStretch - aStretch) * 2;
1348 } else {
1349 distance = (aStretch - aFontEntry->mStretch) * 2;
1351 // if the computed "distance" here is negative, it means that
1352 // aFontEntry lies in the "non-preferred" direction from aStretch,
1353 // so we treat that as larger than any preferred-direction distance
1354 // (max possible is 8) by adding an extra 10 to the absolute value
1355 if (distance < 0) {
1356 distance = -distance + 10;
1359 if (aFontEntry->IsItalic() != anItalic) {
1360 distance += 1;
1362 return uint32_t(distance);
1365 bool
1366 gfxFontFamily::FindWeightsForStyle(gfxFontEntry* aFontsForWeights[],
1367 bool anItalic, int16_t aStretch)
1369 uint32_t foundWeights = 0;
1370 uint32_t bestMatchDistance = 0xffffffff;
1372 uint32_t count = mAvailableFonts.Length();
1373 for (uint32_t i = 0; i < count; i++) {
1374 // this is not called for "simple" families, and therefore it does not
1375 // need to check the mAvailableFonts entries for nullptr.
1376 gfxFontEntry *fe = mAvailableFonts[i];
1377 uint32_t distance = StyleDistance(fe, anItalic, aStretch);
1378 if (distance <= bestMatchDistance) {
1379 int8_t wt = fe->mWeight / 100;
1380 NS_ASSERTION(wt >= 1 && wt < 10, "invalid weight in fontEntry");
1381 if (!aFontsForWeights[wt]) {
1382 // record this as a possible candidate for weight matching
1383 aFontsForWeights[wt] = fe;
1384 ++foundWeights;
1385 } else {
1386 uint32_t prevDistance =
1387 StyleDistance(aFontsForWeights[wt], anItalic, aStretch);
1388 if (prevDistance >= distance) {
1389 // replacing a weight we already found,
1390 // so don't increment foundWeights
1391 aFontsForWeights[wt] = fe;
1394 bestMatchDistance = distance;
1398 NS_ASSERTION(foundWeights > 0, "Font family containing no faces?");
1400 if (foundWeights == 1) {
1401 // no need to cull entries if we only found one weight
1402 return true;
1405 // we might have recorded some faces that were a partial style match, but later found
1406 // others that were closer; in this case, we need to cull the poorer matches from the
1407 // weight list we'll return
1408 for (uint32_t i = 0; i < 10; ++i) {
1409 if (aFontsForWeights[i] &&
1410 StyleDistance(aFontsForWeights[i], anItalic, aStretch) > bestMatchDistance)
1412 aFontsForWeights[i] = 0;
1416 return (foundWeights > 0);
1420 void gfxFontFamily::LocalizedName(nsAString& aLocalizedName)
1422 // just return the primary name; subclasses should override
1423 aLocalizedName = mName;
1426 // metric for how close a given font matches a style
1427 static int32_t
1428 CalcStyleMatch(gfxFontEntry *aFontEntry, const gfxFontStyle *aStyle)
1430 int32_t rank = 0;
1431 if (aStyle) {
1432 // italics
1433 bool wantItalic =
1434 (aStyle->style & (NS_FONT_STYLE_ITALIC | NS_FONT_STYLE_OBLIQUE)) != 0;
1435 if (aFontEntry->IsItalic() == wantItalic) {
1436 rank += 10;
1439 // measure of closeness of weight to the desired value
1440 rank += 9 - DeprecatedAbs(aFontEntry->Weight() / 100 - aStyle->ComputeWeight());
1441 } else {
1442 // if no font to match, prefer non-bold, non-italic fonts
1443 if (!aFontEntry->IsItalic()) {
1444 rank += 3;
1446 if (!aFontEntry->IsBold()) {
1447 rank += 2;
1451 return rank;
1454 #define RANK_MATCHED_CMAP 20
1456 void
1457 gfxFontFamily::FindFontForChar(GlobalFontMatch *aMatchData)
1459 if (mFamilyCharacterMapInitialized && !TestCharacterMap(aMatchData->mCh)) {
1460 // none of the faces in the family support the required char,
1461 // so bail out immediately
1462 return;
1465 bool needsBold;
1466 gfxFontStyle normal;
1467 gfxFontEntry *fe = FindFontForStyle(
1468 (aMatchData->mStyle == nullptr) ? *aMatchData->mStyle : normal,
1469 needsBold);
1471 if (fe && !fe->SkipDuringSystemFallback()) {
1472 int32_t rank = 0;
1474 if (fe->TestCharacterMap(aMatchData->mCh)) {
1475 rank += RANK_MATCHED_CMAP;
1476 aMatchData->mCount++;
1477 #ifdef PR_LOGGING
1478 PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textrun);
1480 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_DEBUG))) {
1481 uint32_t unicodeRange = FindCharUnicodeRange(aMatchData->mCh);
1482 uint32_t script = GetScriptCode(aMatchData->mCh);
1483 PR_LOG(log, PR_LOG_DEBUG,\
1484 ("(textrun-systemfallback-fonts) char: u+%6.6x "
1485 "unicode-range: %d script: %d match: [%s]\n",
1486 aMatchData->mCh,
1487 unicodeRange, script,
1488 NS_ConvertUTF16toUTF8(fe->Name()).get()));
1490 #endif
1493 aMatchData->mCmapsTested++;
1494 if (rank == 0) {
1495 return;
1498 // omitting from original windows code -- family name, lang group, pitch
1499 // not available in current FontEntry implementation
1500 rank += CalcStyleMatch(fe, aMatchData->mStyle);
1502 // xxx - add whether AAT font with morphing info for specific lang groups
1504 if (rank > aMatchData->mMatchRank
1505 || (rank == aMatchData->mMatchRank &&
1506 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
1508 aMatchData->mBestMatch = fe;
1509 aMatchData->mMatchedFamily = this;
1510 aMatchData->mMatchRank = rank;
1515 void
1516 gfxFontFamily::SearchAllFontsForChar(GlobalFontMatch *aMatchData)
1518 uint32_t i, numFonts = mAvailableFonts.Length();
1519 for (i = 0; i < numFonts; i++) {
1520 gfxFontEntry *fe = mAvailableFonts[i];
1521 if (fe && fe->TestCharacterMap(aMatchData->mCh)) {
1522 int32_t rank = RANK_MATCHED_CMAP;
1523 rank += CalcStyleMatch(fe, aMatchData->mStyle);
1524 if (rank > aMatchData->mMatchRank
1525 || (rank == aMatchData->mMatchRank &&
1526 Compare(fe->Name(), aMatchData->mBestMatch->Name()) > 0))
1528 aMatchData->mBestMatch = fe;
1529 aMatchData->mMatchedFamily = this;
1530 aMatchData->mMatchRank = rank;
1536 /*static*/ void
1537 gfxFontFamily::ReadOtherFamilyNamesForFace(const nsAString& aFamilyName,
1538 const char *aNameData,
1539 uint32_t aDataLength,
1540 nsTArray<nsString>& aOtherFamilyNames,
1541 bool useFullName)
1543 const gfxFontUtils::NameHeader *nameHeader =
1544 reinterpret_cast<const gfxFontUtils::NameHeader*>(aNameData);
1546 uint32_t nameCount = nameHeader->count;
1547 if (nameCount * sizeof(gfxFontUtils::NameRecord) > aDataLength) {
1548 NS_WARNING("invalid font (name records)");
1549 return;
1552 const gfxFontUtils::NameRecord *nameRecord =
1553 reinterpret_cast<const gfxFontUtils::NameRecord*>(aNameData + sizeof(gfxFontUtils::NameHeader));
1554 uint32_t stringsBase = uint32_t(nameHeader->stringOffset);
1556 for (uint32_t i = 0; i < nameCount; i++, nameRecord++) {
1557 uint32_t nameLen = nameRecord->length;
1558 uint32_t nameOff = nameRecord->offset; // offset from base of string storage
1560 if (stringsBase + nameOff + nameLen > aDataLength) {
1561 NS_WARNING("invalid font (name table strings)");
1562 return;
1565 uint16_t nameID = nameRecord->nameID;
1566 if ((useFullName && nameID == gfxFontUtils::NAME_ID_FULL) ||
1567 (!useFullName && (nameID == gfxFontUtils::NAME_ID_FAMILY ||
1568 nameID == gfxFontUtils::NAME_ID_PREFERRED_FAMILY))) {
1569 nsAutoString otherFamilyName;
1570 bool ok = gfxFontUtils::DecodeFontName(aNameData + stringsBase + nameOff,
1571 nameLen,
1572 uint32_t(nameRecord->platformID),
1573 uint32_t(nameRecord->encodingID),
1574 uint32_t(nameRecord->languageID),
1575 otherFamilyName);
1576 // add if not same as canonical family name
1577 if (ok && otherFamilyName != aFamilyName) {
1578 aOtherFamilyNames.AppendElement(otherFamilyName);
1584 // returns true if other names were found, false otherwise
1585 bool
1586 gfxFontFamily::ReadOtherFamilyNamesForFace(gfxPlatformFontList *aPlatformFontList,
1587 hb_blob_t *aNameTable,
1588 bool useFullName)
1590 uint32_t dataLength;
1591 const char *nameData = hb_blob_get_data(aNameTable, &dataLength);
1592 nsAutoTArray<nsString,4> otherFamilyNames;
1594 ReadOtherFamilyNamesForFace(mName, nameData, dataLength,
1595 otherFamilyNames, useFullName);
1597 uint32_t n = otherFamilyNames.Length();
1598 for (uint32_t i = 0; i < n; i++) {
1599 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
1602 return n != 0;
1605 void
1606 gfxFontFamily::ReadOtherFamilyNames(gfxPlatformFontList *aPlatformFontList)
1608 if (mOtherFamilyNamesInitialized)
1609 return;
1610 mOtherFamilyNamesInitialized = true;
1612 FindStyleVariations();
1614 // read in other family names for the first face in the list
1615 uint32_t i, numFonts = mAvailableFonts.Length();
1616 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
1618 for (i = 0; i < numFonts; ++i) {
1619 gfxFontEntry *fe = mAvailableFonts[i];
1620 if (!fe) {
1621 continue;
1623 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1624 if (!nameTable) {
1625 continue;
1627 mHasOtherFamilyNames = ReadOtherFamilyNamesForFace(aPlatformFontList,
1628 nameTable);
1629 break;
1632 // read in other names for the first face in the list with the assumption
1633 // that if extra names don't exist in that face then they don't exist in
1634 // other faces for the same font
1635 if (!mHasOtherFamilyNames)
1636 return;
1638 // read in names for all faces, needed to catch cases where fonts have
1639 // family names for individual weights (e.g. Hiragino Kaku Gothic Pro W6)
1640 for ( ; i < numFonts; i++) {
1641 gfxFontEntry *fe = mAvailableFonts[i];
1642 if (!fe) {
1643 continue;
1645 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1646 if (!nameTable) {
1647 continue;
1649 ReadOtherFamilyNamesForFace(aPlatformFontList, nameTable);
1653 void
1654 gfxFontFamily::ReadFaceNames(gfxPlatformFontList *aPlatformFontList,
1655 bool aNeedFullnamePostscriptNames,
1656 FontInfoData *aFontInfoData)
1658 // if all needed names have already been read, skip
1659 if (mOtherFamilyNamesInitialized &&
1660 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames))
1661 return;
1663 bool asyncFontLoaderDisabled = false;
1665 #if defined(XP_MACOSX)
1666 // bug 975460 - async font loader crashes sometimes under 10.6, disable
1667 if (!nsCocoaFeatures::OnLionOrLater()) {
1668 asyncFontLoaderDisabled = true;
1670 #endif
1672 if (!mOtherFamilyNamesInitialized &&
1673 aFontInfoData &&
1674 aFontInfoData->mLoadOtherNames &&
1675 !asyncFontLoaderDisabled)
1677 nsAutoTArray<nsString,4> otherFamilyNames;
1678 bool foundOtherNames =
1679 aFontInfoData->GetOtherFamilyNames(mName, otherFamilyNames);
1680 if (foundOtherNames) {
1681 uint32_t i, n = otherFamilyNames.Length();
1682 for (i = 0; i < n; i++) {
1683 aPlatformFontList->AddOtherFamilyName(this, otherFamilyNames[i]);
1686 mOtherFamilyNamesInitialized = true;
1689 // if all needed data has been initialized, return
1690 if (mOtherFamilyNamesInitialized &&
1691 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
1692 return;
1695 FindStyleVariations(aFontInfoData);
1697 // check again, as style enumeration code may have loaded names
1698 if (mOtherFamilyNamesInitialized &&
1699 (mFaceNamesInitialized || !aNeedFullnamePostscriptNames)) {
1700 return;
1703 uint32_t i, numFonts = mAvailableFonts.Length();
1704 const uint32_t kNAME = TRUETYPE_TAG('n','a','m','e');
1706 bool firstTime = true, readAllFaces = false;
1707 for (i = 0; i < numFonts; ++i) {
1708 gfxFontEntry *fe = mAvailableFonts[i];
1709 if (!fe) {
1710 continue;
1713 nsAutoString fullname, psname;
1714 bool foundFaceNames = false;
1715 if (!mFaceNamesInitialized &&
1716 aNeedFullnamePostscriptNames &&
1717 aFontInfoData &&
1718 aFontInfoData->mLoadFaceNames) {
1719 aFontInfoData->GetFaceNames(fe->Name(), fullname, psname);
1720 if (!fullname.IsEmpty()) {
1721 aPlatformFontList->AddFullname(fe, fullname);
1723 if (!psname.IsEmpty()) {
1724 aPlatformFontList->AddPostscriptName(fe, psname);
1726 foundFaceNames = true;
1728 // found everything needed? skip to next font
1729 if (mOtherFamilyNamesInitialized) {
1730 continue;
1734 // load directly from the name table
1735 gfxFontEntry::AutoTable nameTable(fe, kNAME);
1736 if (!nameTable) {
1737 continue;
1740 if (aNeedFullnamePostscriptNames && !foundFaceNames) {
1741 if (gfxFontUtils::ReadCanonicalName(
1742 nameTable, gfxFontUtils::NAME_ID_FULL, fullname) == NS_OK)
1744 aPlatformFontList->AddFullname(fe, fullname);
1747 if (gfxFontUtils::ReadCanonicalName(
1748 nameTable, gfxFontUtils::NAME_ID_POSTSCRIPT, psname) == NS_OK)
1750 aPlatformFontList->AddPostscriptName(fe, psname);
1754 if (!mOtherFamilyNamesInitialized && (firstTime || readAllFaces)) {
1755 bool foundOtherName = ReadOtherFamilyNamesForFace(aPlatformFontList,
1756 nameTable);
1758 // if the first face has a different name, scan all faces, otherwise
1759 // assume the family doesn't have other names
1760 if (firstTime && foundOtherName) {
1761 mHasOtherFamilyNames = true;
1762 readAllFaces = true;
1764 firstTime = false;
1767 // if not reading in any more names, skip other faces
1768 if (!readAllFaces && !aNeedFullnamePostscriptNames) {
1769 break;
1773 mFaceNamesInitialized = true;
1774 mOtherFamilyNamesInitialized = true;
1778 gfxFontEntry*
1779 gfxFontFamily::FindFont(const nsAString& aPostscriptName)
1781 // find the font using a simple linear search
1782 uint32_t numFonts = mAvailableFonts.Length();
1783 for (uint32_t i = 0; i < numFonts; i++) {
1784 gfxFontEntry *fe = mAvailableFonts[i].get();
1785 if (fe && fe->Name() == aPostscriptName)
1786 return fe;
1788 return nullptr;
1791 void
1792 gfxFontFamily::ReadAllCMAPs(FontInfoData *aFontInfoData)
1794 FindStyleVariations(aFontInfoData);
1796 uint32_t i, numFonts = mAvailableFonts.Length();
1797 for (i = 0; i < numFonts; i++) {
1798 gfxFontEntry *fe = mAvailableFonts[i];
1799 // don't try to load cmaps for downloadable fonts not yet loaded
1800 if (!fe || fe->mIsProxy) {
1801 continue;
1803 fe->ReadCMAP(aFontInfoData);
1804 mFamilyCharacterMap.Union(*(fe->mCharacterMap));
1806 mFamilyCharacterMap.Compact();
1807 mFamilyCharacterMapInitialized = true;
1810 void
1811 gfxFontFamily::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
1812 FontListSizes* aSizes) const
1814 aSizes->mFontListSize +=
1815 mName.SizeOfExcludingThisIfUnshared(aMallocSizeOf);
1816 aSizes->mCharMapsSize +=
1817 mFamilyCharacterMap.SizeOfExcludingThis(aMallocSizeOf);
1819 aSizes->mFontListSize +=
1820 mAvailableFonts.SizeOfExcludingThis(aMallocSizeOf);
1821 for (uint32_t i = 0; i < mAvailableFonts.Length(); ++i) {
1822 gfxFontEntry *fe = mAvailableFonts[i];
1823 if (fe) {
1824 fe->AddSizeOfIncludingThis(aMallocSizeOf, aSizes);
1829 void
1830 gfxFontFamily::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
1831 FontListSizes* aSizes) const
1833 aSizes->mFontListSize += aMallocSizeOf(this);
1834 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
1838 * gfxFontCache - global cache of gfxFont instances.
1839 * Expires unused fonts after a short interval;
1840 * notifies fonts to age their cached shaped-word records;
1841 * observes memory-pressure notification and tells fonts to clear their
1842 * shaped-word caches to free up memory.
1845 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
1847 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
1849 NS_IMETHODIMP
1850 gfxFontCache::MemoryReporter::CollectReports(
1851 nsIMemoryReporterCallback* aCb, nsISupports* aClosure, bool aAnonymize)
1853 FontCacheSizes sizes;
1855 gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
1856 &sizes);
1858 aCb->Callback(EmptyCString(),
1859 NS_LITERAL_CSTRING("explicit/gfx/font-cache"),
1860 KIND_HEAP, UNITS_BYTES, sizes.mFontInstances,
1861 NS_LITERAL_CSTRING("Memory used for active font instances."),
1862 aClosure);
1864 aCb->Callback(EmptyCString(),
1865 NS_LITERAL_CSTRING("explicit/gfx/font-shaped-words"),
1866 KIND_HEAP, UNITS_BYTES, sizes.mShapedWords,
1867 NS_LITERAL_CSTRING("Memory used to cache shaped glyph data."),
1868 aClosure);
1870 return NS_OK;
1873 NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
1875 NS_IMETHODIMP
1876 gfxFontCache::Observer::Observe(nsISupports *aSubject,
1877 const char *aTopic,
1878 const char16_t *someData)
1880 if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
1881 gfxFontCache *fontCache = gfxFontCache::GetCache();
1882 if (fontCache) {
1883 fontCache->FlushShapedWordCaches();
1885 } else {
1886 NS_NOTREACHED("unexpected notification topic");
1888 return NS_OK;
1891 nsresult
1892 gfxFontCache::Init()
1894 NS_ASSERTION(!gGlobalCache, "Where did this come from?");
1895 gGlobalCache = new gfxFontCache();
1896 if (!gGlobalCache) {
1897 return NS_ERROR_OUT_OF_MEMORY;
1899 RegisterStrongMemoryReporter(new MemoryReporter());
1900 return NS_OK;
1903 void
1904 gfxFontCache::Shutdown()
1906 delete gGlobalCache;
1907 gGlobalCache = nullptr;
1909 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1910 printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
1911 printf("Total number of fonts=%d\n", gFontCount);
1912 printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
1913 int(gGlyphExtentsCount*sizeof(gfxGlyphExtents)));
1914 printf("Total glyph extents width-storage size allocated=%d\n", gGlyphExtentsWidthsTotalSize);
1915 printf("Number of simple glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerSimple);
1916 printf("Number of tight glyph extents eagerly requested=%d\n", gGlyphExtentsSetupEagerTight);
1917 printf("Number of tight glyph extents lazily requested=%d\n", gGlyphExtentsSetupLazyTight);
1918 printf("Number of simple glyph extent setups that fell back to tight=%d\n", gGlyphExtentsSetupFallBackToTight);
1919 #endif
1922 gfxFontCache::gfxFontCache()
1923 : nsExpirationTracker<gfxFont,3>(FONT_TIMEOUT_SECONDS * 1000)
1925 nsCOMPtr<nsIObserverService> obs = GetObserverService();
1926 if (obs) {
1927 obs->AddObserver(new Observer, "memory-pressure", false);
1930 #ifndef RELEASE_BUILD
1931 // Currently disabled for release builds, due to unexplained crashes
1932 // during expiration; see bug 717175 & 894798.
1933 mWordCacheExpirationTimer = do_CreateInstance("@mozilla.org/timer;1");
1934 if (mWordCacheExpirationTimer) {
1935 mWordCacheExpirationTimer->
1936 InitWithFuncCallback(WordCacheExpirationTimerCallback, this,
1937 SHAPED_WORD_TIMEOUT_SECONDS * 1000,
1938 nsITimer::TYPE_REPEATING_SLACK);
1940 #endif
1943 gfxFontCache::~gfxFontCache()
1945 // Ensure the user font cache releases its references to font entries,
1946 // so they aren't kept alive after the font instances and font-list
1947 // have been shut down.
1948 gfxUserFontSet::UserFontCache::Shutdown();
1950 if (mWordCacheExpirationTimer) {
1951 mWordCacheExpirationTimer->Cancel();
1952 mWordCacheExpirationTimer = nullptr;
1955 // Expire everything that has a zero refcount, so we don't leak them.
1956 AgeAllGenerations();
1957 // All fonts should be gone.
1958 NS_WARN_IF_FALSE(mFonts.Count() == 0,
1959 "Fonts still alive while shutting down gfxFontCache");
1960 // Note that we have to delete everything through the expiration
1961 // tracker, since there might be fonts not in the hashtable but in
1962 // the tracker.
1965 bool
1966 gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const
1968 return aKey->mFontEntry == mFont->GetFontEntry() &&
1969 aKey->mStyle->Equals(*mFont->GetStyle());
1972 already_AddRefed<gfxFont>
1973 gfxFontCache::Lookup(const gfxFontEntry *aFontEntry,
1974 const gfxFontStyle *aStyle)
1976 Key key(aFontEntry, aStyle);
1977 HashEntry *entry = mFonts.GetEntry(key);
1979 Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
1980 if (!entry)
1981 return nullptr;
1983 nsRefPtr<gfxFont> font = entry->mFont;
1984 return font.forget();
1987 void
1988 gfxFontCache::AddNew(gfxFont *aFont)
1990 Key key(aFont->GetFontEntry(), aFont->GetStyle());
1991 HashEntry *entry = mFonts.PutEntry(key);
1992 if (!entry)
1993 return;
1994 gfxFont *oldFont = entry->mFont;
1995 entry->mFont = aFont;
1996 // Assert that we can find the entry we just put in (this fails if the key
1997 // has a NaN float value in it, e.g. 'sizeAdjust').
1998 MOZ_ASSERT(entry == mFonts.GetEntry(key));
1999 // If someone's asked us to replace an existing font entry, then that's a
2000 // bit weird, but let it happen, and expire the old font if it's not used.
2001 if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
2002 // if oldFont == aFont, recount should be > 0,
2003 // so we shouldn't be here.
2004 NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
2005 NotifyExpired(oldFont);
2009 void
2010 gfxFontCache::NotifyReleased(gfxFont *aFont)
2012 nsresult rv = AddObject(aFont);
2013 if (NS_FAILED(rv)) {
2014 // We couldn't track it for some reason. Kill it now.
2015 DestroyFont(aFont);
2017 // Note that we might have fonts that aren't in the hashtable, perhaps because
2018 // of OOM adding to the hashtable or because someone did an AddNew where
2019 // we already had a font. These fonts are added to the expiration tracker
2020 // anyway, even though Lookup can't resurrect them. Eventually they will
2021 // expire and be deleted.
2024 void
2025 gfxFontCache::NotifyExpired(gfxFont *aFont)
2027 aFont->ClearCachedWords();
2028 RemoveObject(aFont);
2029 DestroyFont(aFont);
2032 void
2033 gfxFontCache::DestroyFont(gfxFont *aFont)
2035 Key key(aFont->GetFontEntry(), aFont->GetStyle());
2036 HashEntry *entry = mFonts.GetEntry(key);
2037 if (entry && entry->mFont == aFont) {
2038 mFonts.RemoveEntry(key);
2040 NS_ASSERTION(aFont->GetRefCount() == 0,
2041 "Destroying with non-zero ref count!");
2042 delete aFont;
2045 /*static*/
2046 PLDHashOperator
2047 gfxFontCache::AgeCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
2049 aHashEntry->mFont->AgeCachedWords();
2050 return PL_DHASH_NEXT;
2053 /*static*/
2054 void
2055 gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer, void* aCache)
2057 gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
2058 cache->mFonts.EnumerateEntries(AgeCachedWordsForFont, nullptr);
2061 /*static*/
2062 PLDHashOperator
2063 gfxFontCache::ClearCachedWordsForFont(HashEntry* aHashEntry, void* aUserData)
2065 aHashEntry->mFont->ClearCachedWords();
2066 return PL_DHASH_NEXT;
2069 /*static*/
2070 size_t
2071 gfxFontCache::AddSizeOfFontEntryExcludingThis(HashEntry* aHashEntry,
2072 MallocSizeOf aMallocSizeOf,
2073 void* aUserArg)
2075 HashEntry *entry = static_cast<HashEntry*>(aHashEntry);
2076 FontCacheSizes *sizes = static_cast<FontCacheSizes*>(aUserArg);
2077 entry->mFont->AddSizeOfExcludingThis(aMallocSizeOf, sizes);
2079 // The entry's size is recorded in the |sizes| parameter, so we return zero
2080 // here to the hashtable enumerator.
2081 return 0;
2084 void
2085 gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
2086 FontCacheSizes* aSizes) const
2088 // TODO: add the overhead of the expiration tracker (generation arrays)
2090 aSizes->mFontInstances +=
2091 mFonts.SizeOfExcludingThis(AddSizeOfFontEntryExcludingThis,
2092 aMallocSizeOf, aSizes);
2095 void
2096 gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
2097 FontCacheSizes* aSizes) const
2099 aSizes->mFontInstances += aMallocSizeOf(this);
2100 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
2103 #define MAX_SSXX_VALUE 99
2104 #define MAX_CVXX_VALUE 99
2106 static void
2107 LookupAlternateValues(gfxFontFeatureValueSet *featureLookup,
2108 const nsAString& aFamily,
2109 const nsTArray<gfxAlternateValue>& altValue,
2110 nsTArray<gfxFontFeature>& aFontFeatures)
2112 uint32_t numAlternates = altValue.Length();
2113 for (uint32_t i = 0; i < numAlternates; i++) {
2114 const gfxAlternateValue& av = altValue.ElementAt(i);
2115 nsAutoTArray<uint32_t,4> values;
2117 // map <family, name, feature> ==> <values>
2118 bool found =
2119 featureLookup->GetFontFeatureValuesFor(aFamily, av.alternate,
2120 av.value, values);
2121 uint32_t numValues = values.Length();
2123 // nothing defined, skip
2124 if (!found || numValues == 0) {
2125 continue;
2128 gfxFontFeature feature;
2129 if (av.alternate == NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT) {
2130 NS_ASSERTION(numValues <= 2,
2131 "too many values allowed for character-variant");
2132 // character-variant(12 3) ==> 'cv12' = 3
2133 uint32_t nn = values.ElementAt(0);
2134 // ignore values greater than 99
2135 if (nn == 0 || nn > MAX_CVXX_VALUE) {
2136 continue;
2138 feature.mValue = 1;
2139 if (numValues > 1) {
2140 feature.mValue = values.ElementAt(1);
2142 feature.mTag = HB_TAG('c','v',('0' + nn / 10), ('0' + nn % 10));
2143 aFontFeatures.AppendElement(feature);
2145 } else if (av.alternate == NS_FONT_VARIANT_ALTERNATES_STYLESET) {
2146 // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
2147 feature.mValue = 1;
2148 for (uint32_t v = 0; v < numValues; v++) {
2149 uint32_t nn = values.ElementAt(v);
2150 if (nn == 0 || nn > MAX_SSXX_VALUE) {
2151 continue;
2153 feature.mTag = HB_TAG('s','s',('0' + nn / 10), ('0' + nn % 10));
2154 aFontFeatures.AppendElement(feature);
2157 } else {
2158 NS_ASSERTION(numValues == 1,
2159 "too many values for font-specific font-variant-alternates");
2160 feature.mValue = values.ElementAt(0);
2162 switch (av.alternate) {
2163 case NS_FONT_VARIANT_ALTERNATES_STYLISTIC: // salt
2164 feature.mTag = HB_TAG('s','a','l','t');
2165 break;
2166 case NS_FONT_VARIANT_ALTERNATES_SWASH: // swsh, cswh
2167 feature.mTag = HB_TAG('s','w','s','h');
2168 aFontFeatures.AppendElement(feature);
2169 feature.mTag = HB_TAG('c','s','w','h');
2170 break;
2171 case NS_FONT_VARIANT_ALTERNATES_ORNAMENTS: // ornm
2172 feature.mTag = HB_TAG('o','r','n','m');
2173 break;
2174 case NS_FONT_VARIANT_ALTERNATES_ANNOTATION: // nalt
2175 feature.mTag = HB_TAG('n','a','l','t');
2176 break;
2177 default:
2178 feature.mTag = 0;
2179 break;
2182 NS_ASSERTION(feature.mTag, "unsupported alternate type");
2183 if (!feature.mTag) {
2184 continue;
2186 aFontFeatures.AppendElement(feature);
2191 /* static */ bool
2192 gfxFontShaper::MergeFontFeatures(
2193 const gfxFontStyle *aStyle,
2194 const nsTArray<gfxFontFeature>& aFontFeatures,
2195 bool aDisableLigatures,
2196 const nsAString& aFamilyName,
2197 bool aAddSmallCaps,
2198 nsDataHashtable<nsUint32HashKey,uint32_t>& aMergedFeatures)
2200 uint32_t numAlts = aStyle->alternateValues.Length();
2201 const nsTArray<gfxFontFeature>& styleRuleFeatures =
2202 aStyle->featureSettings;
2204 // bail immediately if nothing to do
2205 if (styleRuleFeatures.IsEmpty() &&
2206 aFontFeatures.IsEmpty() &&
2207 !aDisableLigatures &&
2208 aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
2209 aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
2210 numAlts == 0) {
2211 return false;
2214 // Ligature features are enabled by default in the generic shaper,
2215 // so we explicitly turn them off if necessary (for letter-spacing)
2216 if (aDisableLigatures) {
2217 aMergedFeatures.Put(HB_TAG('l','i','g','a'), 0);
2218 aMergedFeatures.Put(HB_TAG('c','l','i','g'), 0);
2221 // add feature values from font
2222 uint32_t i, count;
2224 count = aFontFeatures.Length();
2225 for (i = 0; i < count; i++) {
2226 const gfxFontFeature& feature = aFontFeatures.ElementAt(i);
2227 aMergedFeatures.Put(feature.mTag, feature.mValue);
2230 // font-variant-caps - handled here due to the need for fallback handling
2231 // petite caps cases can fallback to appropriate smallcaps
2232 uint32_t variantCaps = aStyle->variantCaps;
2233 switch (variantCaps) {
2234 case NS_FONT_VARIANT_CAPS_ALLSMALL:
2235 aMergedFeatures.Put(HB_TAG('c','2','s','c'), 1);
2236 // fall through to the small-caps case
2237 case NS_FONT_VARIANT_CAPS_SMALLCAPS:
2238 aMergedFeatures.Put(HB_TAG('s','m','c','p'), 1);
2239 break;
2241 case NS_FONT_VARIANT_CAPS_ALLPETITE:
2242 aMergedFeatures.Put(aAddSmallCaps ? HB_TAG('c','2','s','c') :
2243 HB_TAG('c','2','p','c'), 1);
2244 // fall through to the petite-caps case
2245 case NS_FONT_VARIANT_CAPS_PETITECAPS:
2246 aMergedFeatures.Put(aAddSmallCaps ? HB_TAG('s','m','c','p') :
2247 HB_TAG('p','c','a','p'), 1);
2248 break;
2250 case NS_FONT_VARIANT_CAPS_TITLING:
2251 aMergedFeatures.Put(HB_TAG('t','i','t','l'), 1);
2252 break;
2254 case NS_FONT_VARIANT_CAPS_UNICASE:
2255 aMergedFeatures.Put(HB_TAG('u','n','i','c'), 1);
2256 break;
2258 default:
2259 break;
2262 // font-variant-position - handled here due to the need for fallback
2263 switch (aStyle->variantSubSuper) {
2264 case NS_FONT_VARIANT_POSITION_SUPER:
2265 aMergedFeatures.Put(HB_TAG('s','u','p','s'), 1);
2266 break;
2267 case NS_FONT_VARIANT_POSITION_SUB:
2268 aMergedFeatures.Put(HB_TAG('s','u','b','s'), 1);
2269 break;
2270 default:
2271 break;
2274 // add font-specific feature values from style rules
2275 if (aStyle->featureValueLookup && numAlts > 0) {
2276 nsAutoTArray<gfxFontFeature,4> featureList;
2278 // insert list of alternate feature settings
2279 LookupAlternateValues(aStyle->featureValueLookup, aFamilyName,
2280 aStyle->alternateValues, featureList);
2282 count = featureList.Length();
2283 for (i = 0; i < count; i++) {
2284 const gfxFontFeature& feature = featureList.ElementAt(i);
2285 aMergedFeatures.Put(feature.mTag, feature.mValue);
2289 // add feature values from style rules
2290 count = styleRuleFeatures.Length();
2291 for (i = 0; i < count; i++) {
2292 const gfxFontFeature& feature = styleRuleFeatures.ElementAt(i);
2293 aMergedFeatures.Put(feature.mTag, feature.mValue);
2296 return aMergedFeatures.Count() != 0;
2299 void
2300 gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther, bool aOtherIsOnLeft)
2302 mAscent = std::max(mAscent, aOther.mAscent);
2303 mDescent = std::max(mDescent, aOther.mDescent);
2304 if (aOtherIsOnLeft) {
2305 mBoundingBox =
2306 (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0)).Union(aOther.mBoundingBox);
2307 } else {
2308 mBoundingBox =
2309 mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
2311 mAdvanceWidth += aOther.mAdvanceWidth;
2314 gfxFont::gfxFont(gfxFontEntry *aFontEntry, const gfxFontStyle *aFontStyle,
2315 AntialiasOption anAAOption, cairo_scaled_font_t *aScaledFont) :
2316 mScaledFont(aScaledFont),
2317 mFontEntry(aFontEntry), mIsValid(true),
2318 mApplySyntheticBold(false),
2319 mStyle(*aFontStyle),
2320 mAdjustedSize(0.0),
2321 mFUnitsConvFactor(0.0f),
2322 mAntialiasOption(anAAOption)
2324 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
2325 ++gFontCount;
2326 #endif
2327 mKerningSet = HasFeatureSet(HB_TAG('k','e','r','n'), mKerningEnabled);
2330 static PLDHashOperator
2331 NotifyFontDestroyed(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
2332 void* aClosure)
2334 aKey->GetKey()->ForgetFont();
2335 return PL_DHASH_NEXT;
2338 gfxFont::~gfxFont()
2340 uint32_t i, count = mGlyphExtentsArray.Length();
2341 // We destroy the contents of mGlyphExtentsArray explicitly instead of
2342 // using nsAutoPtr because VC++ can't deal with nsTArrays of nsAutoPtrs
2343 // of classes that lack a proper copy constructor
2344 for (i = 0; i < count; ++i) {
2345 delete mGlyphExtentsArray[i];
2348 mFontEntry->NotifyFontDestroyed(this);
2350 if (mGlyphChangeObservers) {
2351 mGlyphChangeObservers->EnumerateEntries(NotifyFontDestroyed, nullptr);
2355 gfxFloat
2356 gfxFont::GetGlyphHAdvance(gfxContext *aCtx, uint16_t aGID)
2358 if (!SetupCairoFont(aCtx)) {
2359 return 0;
2361 if (ProvidesGlyphWidths()) {
2362 return GetGlyphWidth(aCtx, aGID) / 65536.0;
2364 if (mFUnitsConvFactor == 0.0f) {
2365 GetMetrics();
2367 NS_ASSERTION(mFUnitsConvFactor > 0.0f,
2368 "missing font unit conversion factor");
2369 if (!mHarfBuzzShaper) {
2370 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
2372 gfxHarfBuzzShaper* shaper =
2373 static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
2374 if (!shaper->Initialize()) {
2375 return 0;
2377 return shaper->GetGlyphHAdvance(aCtx, aGID) / 65536.0;
2380 /*static*/
2381 PLDHashOperator
2382 gfxFont::AgeCacheEntry(CacheHashEntry *aEntry, void *aUserData)
2384 if (!aEntry->mShapedWord) {
2385 NS_ASSERTION(aEntry->mShapedWord, "cache entry has no gfxShapedWord!");
2386 return PL_DHASH_REMOVE;
2388 if (aEntry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
2389 return PL_DHASH_REMOVE;
2391 return PL_DHASH_NEXT;
2394 static void
2395 CollectLookupsByFeature(hb_face_t *aFace, hb_tag_t aTableTag,
2396 uint32_t aFeatureIndex, hb_set_t *aLookups)
2398 uint32_t lookups[32];
2399 uint32_t i, len, offset;
2401 offset = 0;
2402 do {
2403 len = ArrayLength(lookups);
2404 hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex,
2405 offset, &len, lookups);
2406 for (i = 0; i < len; i++) {
2407 hb_set_add(aLookups, lookups[i]);
2409 offset += len;
2410 } while (len == ArrayLength(lookups));
2413 static void
2414 CollectLookupsByLanguage(hb_face_t *aFace, hb_tag_t aTableTag,
2415 const nsTHashtable<nsUint32HashKey>&
2416 aSpecificFeatures,
2417 hb_set_t *aOtherLookups,
2418 hb_set_t *aSpecificFeatureLookups,
2419 uint32_t aScriptIndex, uint32_t aLangIndex)
2421 uint32_t reqFeatureIndex;
2422 if (hb_ot_layout_language_get_required_feature_index(aFace, aTableTag,
2423 aScriptIndex,
2424 aLangIndex,
2425 &reqFeatureIndex)) {
2426 CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex,
2427 aOtherLookups);
2430 uint32_t featureIndexes[32];
2431 uint32_t i, len, offset;
2433 offset = 0;
2434 do {
2435 len = ArrayLength(featureIndexes);
2436 hb_ot_layout_language_get_feature_indexes(aFace, aTableTag,
2437 aScriptIndex, aLangIndex,
2438 offset, &len, featureIndexes);
2440 for (i = 0; i < len; i++) {
2441 uint32_t featureIndex = featureIndexes[i];
2443 // get the feature tag
2444 hb_tag_t featureTag;
2445 uint32_t tagLen = 1;
2446 hb_ot_layout_language_get_feature_tags(aFace, aTableTag,
2447 aScriptIndex, aLangIndex,
2448 offset + i, &tagLen,
2449 &featureTag);
2451 // collect lookups
2452 hb_set_t *lookups = aSpecificFeatures.GetEntry(featureTag) ?
2453 aSpecificFeatureLookups : aOtherLookups;
2454 CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
2456 offset += len;
2457 } while (len == ArrayLength(featureIndexes));
2460 static bool
2461 HasLookupRuleWithGlyphByScript(hb_face_t *aFace, hb_tag_t aTableTag,
2462 hb_tag_t aScriptTag, uint32_t aScriptIndex,
2463 uint16_t aGlyph,
2464 const nsTHashtable<nsUint32HashKey>&
2465 aDefaultFeatures,
2466 bool& aHasDefaultFeatureWithGlyph)
2468 uint32_t numLangs, lang;
2469 hb_set_t *defaultFeatureLookups = hb_set_create();
2470 hb_set_t *nonDefaultFeatureLookups = hb_set_create();
2472 // default lang
2473 CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
2474 nonDefaultFeatureLookups, defaultFeatureLookups,
2475 aScriptIndex,
2476 HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
2478 // iterate over langs
2479 numLangs = hb_ot_layout_script_get_language_tags(aFace, aTableTag,
2480 aScriptIndex, 0,
2481 nullptr, nullptr);
2482 for (lang = 0; lang < numLangs; lang++) {
2483 CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
2484 nonDefaultFeatureLookups,
2485 defaultFeatureLookups,
2486 aScriptIndex, lang);
2489 // look for the glyph among default feature lookups
2490 aHasDefaultFeatureWithGlyph = false;
2491 hb_set_t *glyphs = hb_set_create();
2492 hb_codepoint_t index = -1;
2493 while (hb_set_next(defaultFeatureLookups, &index)) {
2494 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
2495 glyphs, glyphs, glyphs,
2496 nullptr);
2497 if (hb_set_has(glyphs, aGlyph)) {
2498 aHasDefaultFeatureWithGlyph = true;
2499 break;
2503 // look for the glyph among non-default feature lookups
2504 // if no default feature lookups contained spaces
2505 bool hasNonDefaultFeatureWithGlyph = false;
2506 if (!aHasDefaultFeatureWithGlyph) {
2507 hb_set_clear(glyphs);
2508 index = -1;
2509 while (hb_set_next(nonDefaultFeatureLookups, &index)) {
2510 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
2511 glyphs, glyphs, glyphs,
2512 nullptr);
2513 if (hb_set_has(glyphs, aGlyph)) {
2514 hasNonDefaultFeatureWithGlyph = true;
2515 break;
2520 hb_set_destroy(glyphs);
2521 hb_set_destroy(defaultFeatureLookups);
2522 hb_set_destroy(nonDefaultFeatureLookups);
2524 return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
2527 static void
2528 HasLookupRuleWithGlyph(hb_face_t *aFace, hb_tag_t aTableTag, bool& aHasGlyph,
2529 hb_tag_t aSpecificFeature, bool& aHasGlyphSpecific,
2530 uint16_t aGlyph)
2532 // iterate over the scripts in the font
2533 uint32_t numScripts, numLangs, script, lang;
2534 hb_set_t *otherLookups = hb_set_create();
2535 hb_set_t *specificFeatureLookups = hb_set_create();
2536 nsTHashtable<nsUint32HashKey> specificFeature;
2538 specificFeature.PutEntry(aSpecificFeature);
2540 numScripts = hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0,
2541 nullptr, nullptr);
2543 for (script = 0; script < numScripts; script++) {
2544 // default lang
2545 CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
2546 otherLookups, specificFeatureLookups,
2547 script, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
2549 // iterate over langs
2550 numLangs = hb_ot_layout_script_get_language_tags(aFace, HB_OT_TAG_GPOS,
2551 script, 0,
2552 nullptr, nullptr);
2553 for (lang = 0; lang < numLangs; lang++) {
2554 CollectLookupsByLanguage(aFace, aTableTag, specificFeature,
2555 otherLookups, specificFeatureLookups,
2556 script, lang);
2560 // look for the glyph among non-specific feature lookups
2561 hb_set_t *glyphs = hb_set_create();
2562 hb_codepoint_t index = -1;
2563 while (hb_set_next(otherLookups, &index)) {
2564 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
2565 glyphs, glyphs, glyphs,
2566 nullptr);
2567 if (hb_set_has(glyphs, aGlyph)) {
2568 aHasGlyph = true;
2569 break;
2573 // look for the glyph among specific feature lookups
2574 hb_set_clear(glyphs);
2575 index = -1;
2576 while (hb_set_next(specificFeatureLookups, &index)) {
2577 hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index,
2578 glyphs, glyphs, glyphs,
2579 nullptr);
2580 if (hb_set_has(glyphs, aGlyph)) {
2581 aHasGlyphSpecific = true;
2582 break;
2586 hb_set_destroy(glyphs);
2587 hb_set_destroy(specificFeatureLookups);
2588 hb_set_destroy(otherLookups);
2591 nsDataHashtable<nsUint32HashKey, int32_t> *gfxFont::sScriptTagToCode = nullptr;
2592 nsTHashtable<nsUint32HashKey> *gfxFont::sDefaultFeatures = nullptr;
2594 static inline bool
2595 HasSubstitution(uint32_t *aBitVector, uint32_t aBit) {
2596 return (aBitVector[aBit >> 5] & (1 << (aBit & 0x1f))) != 0;
2599 // union of all default substitution features across scripts
2600 static const hb_tag_t defaultFeatures[] = {
2601 HB_TAG('a','b','v','f'),
2602 HB_TAG('a','b','v','s'),
2603 HB_TAG('a','k','h','n'),
2604 HB_TAG('b','l','w','f'),
2605 HB_TAG('b','l','w','s'),
2606 HB_TAG('c','a','l','t'),
2607 HB_TAG('c','c','m','p'),
2608 HB_TAG('c','f','a','r'),
2609 HB_TAG('c','j','c','t'),
2610 HB_TAG('c','l','i','g'),
2611 HB_TAG('f','i','n','2'),
2612 HB_TAG('f','i','n','3'),
2613 HB_TAG('f','i','n','a'),
2614 HB_TAG('h','a','l','f'),
2615 HB_TAG('h','a','l','n'),
2616 HB_TAG('i','n','i','t'),
2617 HB_TAG('i','s','o','l'),
2618 HB_TAG('l','i','g','a'),
2619 HB_TAG('l','j','m','o'),
2620 HB_TAG('l','o','c','l'),
2621 HB_TAG('l','t','r','a'),
2622 HB_TAG('l','t','r','m'),
2623 HB_TAG('m','e','d','2'),
2624 HB_TAG('m','e','d','i'),
2625 HB_TAG('m','s','e','t'),
2626 HB_TAG('n','u','k','t'),
2627 HB_TAG('p','r','e','f'),
2628 HB_TAG('p','r','e','s'),
2629 HB_TAG('p','s','t','f'),
2630 HB_TAG('p','s','t','s'),
2631 HB_TAG('r','c','l','t'),
2632 HB_TAG('r','l','i','g'),
2633 HB_TAG('r','k','r','f'),
2634 HB_TAG('r','p','h','f'),
2635 HB_TAG('r','t','l','a'),
2636 HB_TAG('r','t','l','m'),
2637 HB_TAG('t','j','m','o'),
2638 HB_TAG('v','a','t','u'),
2639 HB_TAG('v','e','r','t'),
2640 HB_TAG('v','j','m','o')
2643 void
2644 gfxFont::CheckForFeaturesInvolvingSpace()
2646 mFontEntry->mHasSpaceFeaturesInitialized = true;
2648 #ifdef PR_LOGGING
2649 bool log = LOG_FONTINIT_ENABLED();
2650 TimeStamp start;
2651 if (MOZ_UNLIKELY(log)) {
2652 start = TimeStamp::Now();
2654 #endif
2656 bool result = false;
2658 uint32_t spaceGlyph = GetSpaceGlyph();
2659 if (!spaceGlyph) {
2660 return;
2663 hb_face_t *face = GetFontEntry()->GetHBFace();
2665 // GSUB lookups - examine per script
2666 if (hb_ot_layout_has_substitution(face)) {
2668 // set up the script ==> code hashtable if needed
2669 if (!sScriptTagToCode) {
2670 sScriptTagToCode =
2671 new nsDataHashtable<nsUint32HashKey,
2672 int32_t>(MOZ_NUM_SCRIPT_CODES);
2673 sScriptTagToCode->Put(HB_TAG('D','F','L','T'), MOZ_SCRIPT_COMMON);
2674 for (int32_t s = MOZ_SCRIPT_ARABIC; s < MOZ_NUM_SCRIPT_CODES; s++) {
2675 hb_script_t scriptTag = hb_script_t(GetScriptTagForCode(s));
2676 hb_tag_t s1, s2;
2677 hb_ot_tags_from_script(scriptTag, &s1, &s2);
2678 sScriptTagToCode->Put(s1, s);
2679 if (s2 != HB_OT_TAG_DEFAULT_SCRIPT) {
2680 sScriptTagToCode->Put(s2, s);
2684 uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
2685 sDefaultFeatures =
2686 new nsTHashtable<nsUint32HashKey>(numDefaultFeatures);
2687 for (uint32_t i = 0; i < numDefaultFeatures; i++) {
2688 sDefaultFeatures->PutEntry(defaultFeatures[i]);
2692 // iterate over the scripts in the font
2693 hb_tag_t scriptTags[8];
2695 uint32_t len, offset = 0;
2696 do {
2697 len = ArrayLength(scriptTags);
2698 hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset,
2699 &len, scriptTags);
2700 for (uint32_t i = 0; i < len; i++) {
2701 bool isDefaultFeature = false;
2702 int32_t s;
2703 if (!HasLookupRuleWithGlyphByScript(face, HB_OT_TAG_GSUB,
2704 scriptTags[i], offset + i,
2705 spaceGlyph,
2706 *sDefaultFeatures,
2707 isDefaultFeature) ||
2708 !sScriptTagToCode->Get(scriptTags[i], &s))
2710 continue;
2712 result = true;
2713 uint32_t index = s >> 5;
2714 uint32_t bit = s & 0x1f;
2715 if (isDefaultFeature) {
2716 mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
2717 } else {
2718 mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
2721 offset += len;
2722 } while (len == ArrayLength(scriptTags));
2725 // spaces in default features of default script?
2726 // ==> can't use word cache, skip GPOS analysis
2727 bool canUseWordCache = true;
2728 if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
2729 MOZ_SCRIPT_COMMON)) {
2730 canUseWordCache = false;
2733 // GPOS lookups - distinguish kerning from non-kerning features
2734 mFontEntry->mHasSpaceFeaturesKerning = false;
2735 mFontEntry->mHasSpaceFeaturesNonKerning = false;
2737 if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
2738 bool hasKerning = false, hasNonKerning = false;
2739 HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
2740 HB_TAG('k','e','r','n'), hasKerning, spaceGlyph);
2741 if (hasKerning || hasNonKerning) {
2742 result = true;
2744 mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
2745 mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
2748 hb_face_destroy(face);
2749 mFontEntry->mHasSpaceFeatures = result;
2751 #ifdef PR_LOGGING
2752 if (MOZ_UNLIKELY(log)) {
2753 TimeDuration elapsed = TimeStamp::Now() - start;
2754 LOG_FONTINIT((
2755 "(fontinit-spacelookups) font: %s - "
2756 "subst default: %8.8x %8.8x %8.8x %8.8x "
2757 "subst non-default: %8.8x %8.8x %8.8x %8.8x "
2758 "kerning: %s non-kerning: %s time: %6.3f\n",
2759 NS_ConvertUTF16toUTF8(mFontEntry->Name()).get(),
2760 mFontEntry->mDefaultSubSpaceFeatures[3],
2761 mFontEntry->mDefaultSubSpaceFeatures[2],
2762 mFontEntry->mDefaultSubSpaceFeatures[1],
2763 mFontEntry->mDefaultSubSpaceFeatures[0],
2764 mFontEntry->mNonDefaultSubSpaceFeatures[3],
2765 mFontEntry->mNonDefaultSubSpaceFeatures[2],
2766 mFontEntry->mNonDefaultSubSpaceFeatures[1],
2767 mFontEntry->mNonDefaultSubSpaceFeatures[0],
2768 (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
2769 (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
2770 elapsed.ToMilliseconds()
2773 #endif
2776 bool
2777 gfxFont::HasSubstitutionRulesWithSpaceLookups(int32_t aRunScript)
2779 NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
2780 "need to initialize space lookup flags");
2781 NS_ASSERTION(aRunScript < MOZ_NUM_SCRIPT_CODES, "weird script code");
2782 if (aRunScript == MOZ_SCRIPT_INVALID ||
2783 aRunScript >= MOZ_NUM_SCRIPT_CODES) {
2784 return false;
2787 // default features have space lookups ==> true
2788 if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
2789 MOZ_SCRIPT_COMMON) ||
2790 HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures,
2791 aRunScript))
2793 return true;
2796 // non-default features have space lookups and some type of
2797 // font feature, in font or style is specified ==> true
2798 if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
2799 MOZ_SCRIPT_COMMON) ||
2800 HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
2801 aRunScript)) &&
2802 (!mStyle.featureSettings.IsEmpty() ||
2803 !mFontEntry->mFeatureSettings.IsEmpty()))
2805 return true;
2808 return false;
2811 bool
2812 gfxFont::SpaceMayParticipateInShaping(int32_t aRunScript)
2814 // avoid checking fonts known not to include default space-dependent features
2815 if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
2816 if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
2817 mFontEntry->mFeatureSettings.IsEmpty()) {
2818 return false;
2822 // We record the presence of space-dependent features in the font entry
2823 // so that subsequent instantiations for the same font face won't
2824 // require us to re-check the tables; however, the actual check is done
2825 // by gfxFont because not all font entry subclasses know how to create
2826 // a harfbuzz face for introspection.
2827 if (!mFontEntry->mHasSpaceFeaturesInitialized) {
2828 CheckForFeaturesInvolvingSpace();
2831 if (!mFontEntry->mHasSpaceFeatures) {
2832 return false;
2835 // if font has substitution rules or non-kerning positioning rules
2836 // that involve spaces, bypass
2837 if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
2838 mFontEntry->mHasSpaceFeaturesNonKerning) {
2839 return true;
2842 // if kerning explicitly enabled/disabled via font-feature-settings or
2843 // font-kerning and kerning rules use spaces, only bypass when enabled
2844 if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
2845 return mKerningEnabled;
2848 return false;
2851 bool
2852 gfxFont::SupportsFeature(int32_t aScript, uint32_t aFeatureTag)
2854 if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
2855 return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
2857 return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
2860 bool
2861 gfxFont::SupportsVariantCaps(int32_t aScript,
2862 uint32_t aVariantCaps,
2863 bool& aFallbackToSmallCaps,
2864 bool& aSyntheticLowerToSmallCaps,
2865 bool& aSyntheticUpperToSmallCaps)
2867 bool ok = true; // cases without fallback are fine
2868 aFallbackToSmallCaps = false;
2869 aSyntheticLowerToSmallCaps = false;
2870 aSyntheticUpperToSmallCaps = false;
2871 switch (aVariantCaps) {
2872 case NS_FONT_VARIANT_CAPS_SMALLCAPS:
2873 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
2874 if (!ok) {
2875 aSyntheticLowerToSmallCaps = true;
2877 break;
2878 case NS_FONT_VARIANT_CAPS_ALLSMALL:
2879 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
2880 SupportsFeature(aScript, HB_TAG('c','2','s','c'));
2881 if (!ok) {
2882 aSyntheticLowerToSmallCaps = true;
2883 aSyntheticUpperToSmallCaps = true;
2885 break;
2886 case NS_FONT_VARIANT_CAPS_PETITECAPS:
2887 ok = SupportsFeature(aScript, HB_TAG('p','c','a','p'));
2888 if (!ok) {
2889 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p'));
2890 aFallbackToSmallCaps = ok;
2892 if (!ok) {
2893 aSyntheticLowerToSmallCaps = true;
2895 break;
2896 case NS_FONT_VARIANT_CAPS_ALLPETITE:
2897 ok = SupportsFeature(aScript, HB_TAG('p','c','a','p')) &&
2898 SupportsFeature(aScript, HB_TAG('c','2','p','c'));
2899 if (!ok) {
2900 ok = SupportsFeature(aScript, HB_TAG('s','m','c','p')) &&
2901 SupportsFeature(aScript, HB_TAG('c','2','s','c'));
2902 aFallbackToSmallCaps = ok;
2904 if (!ok) {
2905 aSyntheticLowerToSmallCaps = true;
2906 aSyntheticUpperToSmallCaps = true;
2908 break;
2909 default:
2910 break;
2913 NS_ASSERTION(!(ok && (aSyntheticLowerToSmallCaps ||
2914 aSyntheticUpperToSmallCaps)),
2915 "shouldn't use synthetic features if we found real ones");
2917 NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
2918 "if we found a usable fallback, that counts as ok");
2920 return ok;
2923 bool
2924 gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
2925 const uint8_t *aString,
2926 uint32_t aLength, int32_t aRunScript)
2928 NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
2929 aLength);
2930 return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(),
2931 aLength, aRunScript);
2934 bool
2935 gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
2936 const char16_t *aString,
2937 uint32_t aLength, int32_t aRunScript)
2939 NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
2940 aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
2941 "unknown value of font-variant-position");
2943 uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ?
2944 HB_TAG('s','u','p','s') : HB_TAG('s','u','b','s');
2946 if (!SupportsFeature(aRunScript, feature)) {
2947 return false;
2950 // xxx - for graphite, don't really know how to sniff lookups so bail
2951 if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
2952 return true;
2955 if (!mHarfBuzzShaper) {
2956 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
2958 gfxHarfBuzzShaper* shaper =
2959 static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
2960 if (!shaper->Initialize()) {
2961 return false;
2964 // get the hbset containing input glyphs for the feature
2965 const hb_set_t *inputGlyphs = mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
2967 // create an hbset containing default glyphs for the script run
2968 hb_set_t *defaultGlyphsInRun = hb_set_create();
2970 // for each character, get the glyph id
2971 for (uint32_t i = 0; i < aLength; i++) {
2972 uint32_t ch = aString[i];
2974 if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
2975 NS_IS_LOW_SURROGATE(aString[i + 1])) {
2976 i++;
2977 ch = SURROGATE_TO_UCS4(ch, aString[i]);
2980 if (ch == 0xa0) {
2981 ch = ' ';
2984 hb_codepoint_t gid = shaper->GetGlyph(ch, 0);
2985 hb_set_add(defaultGlyphsInRun, gid);
2988 // intersect with input glyphs, if size is not the same ==> fallback
2989 uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
2990 hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
2991 uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
2992 hb_set_destroy(defaultGlyphsInRun);
2994 return origSize == intersectionSize;
2997 bool
2998 gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn)
3000 aFeatureOn = false;
3002 if (mStyle.featureSettings.IsEmpty() &&
3003 GetFontEntry()->mFeatureSettings.IsEmpty()) {
3004 return false;
3007 // add feature values from font
3008 bool featureSet = false;
3009 uint32_t i, count;
3011 nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
3012 count = fontFeatures.Length();
3013 for (i = 0; i < count; i++) {
3014 const gfxFontFeature& feature = fontFeatures.ElementAt(i);
3015 if (feature.mTag == aFeature) {
3016 featureSet = true;
3017 aFeatureOn = (feature.mValue != 0);
3021 // add feature values from style rules
3022 nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
3023 count = styleFeatures.Length();
3024 for (i = 0; i < count; i++) {
3025 const gfxFontFeature& feature = styleFeatures.ElementAt(i);
3026 if (feature.mTag == aFeature) {
3027 featureSet = true;
3028 aFeatureOn = (feature.mValue != 0);
3032 return featureSet;
3036 * A helper function in case we need to do any rounding or other
3037 * processing here.
3039 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
3040 (double(aAppUnits)*double(aDevUnitsPerAppUnit))
3042 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
3043 switch (aAAOption) {
3044 case gfxFont::kAntialiasSubpixel:
3045 return AntialiasMode::SUBPIXEL;
3046 case gfxFont::kAntialiasGrayscale:
3047 return AntialiasMode::GRAY;
3048 case gfxFont::kAntialiasNone:
3049 return AntialiasMode::NONE;
3050 default:
3051 return AntialiasMode::DEFAULT;
3055 // Parameters passed to gfxFont methods for drawing glyphs from a textrun.
3056 // The TextRunDrawParams are set up once per textrun; the FontDrawParams
3057 // are dependent on the specific font, so they are set per GlyphRun.
3059 struct TextRunDrawParams {
3060 RefPtr<DrawTarget> dt;
3061 gfxContext *context;
3062 gfxFont::Spacing *spacing;
3063 gfxTextRunDrawCallbacks *callbacks;
3064 gfxTextContextPaint *runContextPaint;
3065 gfxFloat direction;
3066 double devPerApp;
3067 DrawMode drawMode;
3068 bool isRTL;
3069 bool paintSVGGlyphs;
3072 struct FontDrawParams {
3073 RefPtr<ScaledFont> scaledFont;
3074 RefPtr<GlyphRenderingOptions> renderingOptions;
3075 gfxTextContextPaint *contextPaint;
3076 Matrix *passedInvMatrix;
3077 Matrix matInv;
3078 double synBoldOnePixelOffset;
3079 int32_t extraStrikes;
3080 DrawOptions drawOptions;
3081 bool haveSVGGlyphs;
3082 bool haveColorGlyphs;
3085 class GlyphBufferAzure
3087 public:
3088 GlyphBufferAzure(const TextRunDrawParams& aRunParams,
3089 const FontDrawParams& aFontParams)
3090 : mRunParams(aRunParams)
3091 , mFontParams(aFontParams)
3092 , mNumGlyphs(0)
3096 ~GlyphBufferAzure()
3098 Flush(true); // flush any remaining buffered glyphs
3101 void OutputGlyph(uint32_t aGlyphID, const gfxPoint& aPt)
3103 Glyph *glyph = AppendGlyph();
3104 glyph->mIndex = aGlyphID;
3105 glyph->mPosition.x = aPt.x;
3106 glyph->mPosition.y = aPt.y;
3107 glyph->mPosition = mFontParams.matInv * glyph->mPosition;
3108 Flush(false); // this will flush only if the buffer is full
3111 const TextRunDrawParams& mRunParams;
3112 const FontDrawParams& mFontParams;
3114 private:
3115 #define GLYPH_BUFFER_SIZE (2048/sizeof(Glyph))
3117 Glyph *AppendGlyph()
3119 return &mGlyphBuffer[mNumGlyphs++];
3122 // Render the buffered glyphs to the draw target and clear the buffer.
3123 // This actually flushes the glyphs only if the buffer is full, or if the
3124 // aFinish parameter is true; otherwise it simply returns.
3125 void Flush(bool aFinish)
3127 // Ensure there's enough room for a glyph to be added to the buffer
3128 if ((!aFinish && mNumGlyphs < GLYPH_BUFFER_SIZE) || !mNumGlyphs) {
3129 return;
3132 if (mRunParams.isRTL) {
3133 Glyph *begin = &mGlyphBuffer[0];
3134 Glyph *end = &mGlyphBuffer[mNumGlyphs];
3135 std::reverse(begin, end);
3138 gfx::GlyphBuffer buf;
3139 buf.mGlyphs = mGlyphBuffer;
3140 buf.mNumGlyphs = mNumGlyphs;
3142 gfxContext::AzureState state = mRunParams.context->CurrentState();
3143 if ((int(mRunParams.drawMode) &
3144 (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
3145 (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) {
3146 FlushStroke(buf, state);
3148 if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_FILL)) {
3149 if (state.pattern || mFontParams.contextPaint) {
3150 Pattern *pat;
3152 nsRefPtr<gfxPattern> fillPattern;
3153 if (!mFontParams.contextPaint ||
3154 !(fillPattern = mFontParams.contextPaint->GetFillPattern(
3155 mRunParams.context->CurrentMatrix()))) {
3156 if (state.pattern) {
3157 pat = state.pattern->GetPattern(mRunParams.dt,
3158 state.patternTransformChanged ?
3159 &state.patternTransform : nullptr);
3160 } else {
3161 pat = nullptr;
3163 } else {
3164 pat = fillPattern->GetPattern(mRunParams.dt);
3167 if (pat) {
3168 Matrix saved;
3169 Matrix *mat = nullptr;
3170 if (mFontParams.passedInvMatrix) {
3171 // The brush matrix needs to be multiplied with the
3172 // inverted matrix as well, to move the brush into the
3173 // space of the glyphs.
3175 // This relies on the returned Pattern not to be reused
3176 // by others, but regenerated on GetPattern calls. This
3177 // is true!
3178 if (pat->GetType() == PatternType::LINEAR_GRADIENT) {
3179 mat = &static_cast<LinearGradientPattern*>(pat)->mMatrix;
3180 } else if (pat->GetType() == PatternType::RADIAL_GRADIENT) {
3181 mat = &static_cast<RadialGradientPattern*>(pat)->mMatrix;
3182 } else if (pat->GetType() == PatternType::SURFACE) {
3183 mat = &static_cast<SurfacePattern*>(pat)->mMatrix;
3186 if (mat) {
3187 saved = *mat;
3188 *mat = (*mat) * (*mFontParams.passedInvMatrix);
3192 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
3193 *pat, mFontParams.drawOptions,
3194 mFontParams.renderingOptions);
3196 if (mat) {
3197 *mat = saved;
3200 } else if (state.sourceSurface) {
3201 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
3202 SurfacePattern(state.sourceSurface,
3203 ExtendMode::CLAMP,
3204 state.surfTransform),
3205 mFontParams.drawOptions,
3206 mFontParams.renderingOptions);
3207 } else {
3208 mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
3209 ColorPattern(state.color),
3210 mFontParams.drawOptions,
3211 mFontParams.renderingOptions);
3214 if (int(mRunParams.drawMode) & int(DrawMode::GLYPH_PATH)) {
3215 mRunParams.context->EnsurePathBuilder();
3216 Matrix mat = mRunParams.dt->GetTransform();
3217 mFontParams.scaledFont->CopyGlyphsToBuilder(
3218 buf, mRunParams.context->mPathBuilder,
3219 mRunParams.dt->GetBackendType(), &mat);
3221 if ((int(mRunParams.drawMode) &
3222 (int(DrawMode::GLYPH_STROKE) | int(DrawMode::GLYPH_STROKE_UNDERNEATH))) ==
3223 int(DrawMode::GLYPH_STROKE)) {
3224 FlushStroke(buf, state);
3227 mNumGlyphs = 0;
3230 void FlushStroke(gfx::GlyphBuffer& aBuf, gfxContext::AzureState& aState)
3232 RefPtr<Path> path =
3233 mFontParams.scaledFont->GetPathForGlyphs(aBuf, mRunParams.dt);
3234 if (mFontParams.contextPaint) {
3235 nsRefPtr<gfxPattern> strokePattern =
3236 mFontParams.contextPaint->GetStrokePattern(
3237 mRunParams.context->CurrentMatrix());
3238 if (strokePattern) {
3239 mRunParams.dt->Stroke(path,
3240 *strokePattern->GetPattern(mRunParams.dt),
3241 aState.strokeOptions);
3246 Glyph mGlyphBuffer[GLYPH_BUFFER_SIZE];
3247 unsigned int mNumGlyphs;
3249 #undef GLYPH_BUFFER_SIZE
3252 // Bug 674909. When synthetic bolding text by drawing twice, need to
3253 // render using a pixel offset in device pixels, otherwise text
3254 // doesn't appear bolded, it appears as if a bad text shadow exists
3255 // when a non-identity transform exists. Use an offset factor so that
3256 // the second draw occurs at a constant offset in device pixels.
3258 double
3259 gfxFont::CalcXScale(gfxContext *aContext)
3261 // determine magnitude of a 1px x offset in device space
3262 gfxSize t = aContext->UserToDevice(gfxSize(1.0, 0.0));
3263 if (t.width == 1.0 && t.height == 0.0) {
3264 // short-circuit the most common case to avoid sqrt() and division
3265 return 1.0;
3268 double m = sqrt(t.width * t.width + t.height * t.height);
3270 NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
3271 if (m == 0.0) {
3272 return 0.0; // effectively disables offset
3275 // scale factor so that offsets are 1px in device pixels
3276 return 1.0 / m;
3279 static DrawMode
3280 ForcePaintingDrawMode(DrawMode aDrawMode)
3282 return aDrawMode == DrawMode::GLYPH_PATH ?
3283 DrawMode(int(DrawMode::GLYPH_FILL) | int(DrawMode::GLYPH_STROKE)) :
3284 aDrawMode;
3287 // Draw an individual glyph at a specific location.
3288 // *aPt is the glyph position in appUnits; it is converted to device
3289 // coordinates (devPt) here.
3290 void
3291 gfxFont::DrawOneGlyph(uint32_t aGlyphID, double aAdvance, gfxPoint *aPt,
3292 GlyphBufferAzure& aBuffer, bool *aEmittedGlyphs) const
3294 const TextRunDrawParams& runParams(aBuffer.mRunParams);
3295 const FontDrawParams& fontParams(aBuffer.mFontParams);
3297 double glyphX;
3298 if (runParams.isRTL) {
3299 aPt->x -= aAdvance;
3300 glyphX = aPt->x;
3301 } else {
3302 glyphX = aPt->x;
3303 aPt->x += aAdvance;
3305 gfxPoint devPt(ToDeviceUnits(glyphX, runParams.devPerApp),
3306 ToDeviceUnits(aPt->y, runParams.devPerApp));
3308 if (fontParams.haveSVGGlyphs) {
3309 if (!runParams.paintSVGGlyphs) {
3310 return;
3312 DrawMode mode = ForcePaintingDrawMode(runParams.drawMode);
3313 if (RenderSVGGlyph(runParams.context, devPt, mode,
3314 aGlyphID, fontParams.contextPaint,
3315 runParams.callbacks, *aEmittedGlyphs)) {
3316 return;
3320 if (fontParams.haveColorGlyphs &&
3321 RenderColorGlyph(runParams.context, fontParams.scaledFont,
3322 fontParams.renderingOptions, fontParams.drawOptions,
3323 fontParams.matInv * gfx::Point(devPt.x, devPt.y),
3324 aGlyphID)) {
3325 return;
3328 aBuffer.OutputGlyph(aGlyphID, devPt);
3330 // Synthetic bolding (if required) by multi-striking.
3331 for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
3332 devPt.x += fontParams.synBoldOnePixelOffset;
3333 aBuffer.OutputGlyph(aGlyphID, devPt);
3336 *aEmittedGlyphs = true;
3339 // Draw a run of CharacterGlyph records from the given offset in aShapedText.
3340 // Returns true if glyph paths were actually emitted.
3341 bool
3342 gfxFont::DrawGlyphs(gfxShapedText *aShapedText,
3343 uint32_t aOffset, // offset in the textrun
3344 uint32_t aCount, // length of run to draw
3345 gfxPoint *aPt,
3346 const TextRunDrawParams& aRunParams,
3347 const FontDrawParams& aFontParams)
3349 bool emittedGlyphs = false;
3350 GlyphBufferAzure buffer(aRunParams, aFontParams);
3352 if (aRunParams.spacing) {
3353 aPt->x += aRunParams.direction * aRunParams.spacing[0].mBefore;
3356 const gfxShapedText::CompressedGlyph *glyphData =
3357 &aShapedText->GetCharacterGlyphs()[aOffset];
3359 for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
3360 if (glyphData->IsSimpleGlyph()) {
3361 DrawOneGlyph(glyphData->GetSimpleGlyph(),
3362 glyphData->GetSimpleAdvance(),
3363 aPt, buffer, &emittedGlyphs);
3364 } else {
3365 uint32_t glyphCount = glyphData->GetGlyphCount();
3366 if (glyphCount > 0) {
3367 const gfxShapedText::DetailedGlyph *details =
3368 aShapedText->GetDetailedGlyphs(aOffset + i);
3369 NS_ASSERTION(details, "detailedGlyph should not be missing!");
3370 for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
3371 double advance = details->mAdvance;
3372 if (glyphData->IsMissing()) {
3373 // Default-ignorable chars will have zero advance width;
3374 // we don't have to draw the hexbox for them.
3375 if (aRunParams.drawMode != DrawMode::GLYPH_PATH &&
3376 advance > 0) {
3377 double glyphX = aPt->x;
3378 if (aRunParams.isRTL) {
3379 glyphX -= advance;
3381 gfxPoint pt(ToDeviceUnits(glyphX, aRunParams.devPerApp),
3382 ToDeviceUnits(aPt->y, aRunParams.devPerApp));
3383 gfxFloat advanceDevUnits =
3384 ToDeviceUnits(advance, aRunParams.devPerApp);
3385 gfxFloat height = GetMetrics().maxAscent;
3386 gfxRect glyphRect(pt.x, pt.y - height,
3387 advanceDevUnits, height);
3389 // If there's a fake-italic skew in effect as part
3390 // of the drawTarget's transform, we need to remove
3391 // this before drawing the hexbox. (Bug 983985)
3392 Matrix oldMat;
3393 if (aFontParams.passedInvMatrix) {
3394 oldMat = aRunParams.dt->GetTransform();
3395 aRunParams.dt->SetTransform(
3396 *aFontParams.passedInvMatrix * oldMat);
3399 gfxFontMissingGlyphs::DrawMissingGlyph(
3400 aRunParams.context, glyphRect, details->mGlyphID,
3401 aShapedText->GetAppUnitsPerDevUnit());
3403 // Restore the matrix, if we modified it before
3404 // drawing the hexbox.
3405 if (aFontParams.passedInvMatrix) {
3406 aRunParams.dt->SetTransform(oldMat);
3409 } else {
3410 gfxPoint glyphXY(*aPt);
3411 glyphXY.x += details->mXOffset;
3412 glyphXY.y += details->mYOffset;
3413 DrawOneGlyph(details->mGlyphID, advance, &glyphXY,
3414 buffer, &emittedGlyphs);
3416 aPt->x += aRunParams.direction * advance;
3421 if (aRunParams.spacing) {
3422 double space = aRunParams.spacing[i].mAfter;
3423 if (i + 1 < aCount) {
3424 space += aRunParams.spacing[i + 1].mBefore;
3426 aPt->x += aRunParams.direction * space;
3430 return emittedGlyphs;
3433 void
3434 gfxFont::Draw(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
3435 gfxPoint *aPt, const TextRunDrawParams& aRunParams)
3437 NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
3438 !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
3439 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
3441 if (aStart >= aEnd) {
3442 return;
3445 FontDrawParams fontParams;
3447 fontParams.scaledFont = GetScaledFont(aRunParams.dt);
3448 if (!fontParams.scaledFont) {
3449 return;
3452 fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
3453 fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
3454 fontParams.contextPaint = aRunParams.runContextPaint;
3456 nsAutoPtr<gfxTextContextPaint> contextPaint;
3457 if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
3458 // If no pattern is specified for fill, use the current pattern
3459 NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
3460 "no pattern supplied for stroking text");
3461 nsRefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
3462 contextPaint =
3463 new SimpleTextContextPaint(fillPattern, nullptr,
3464 aRunParams.context->CurrentMatrix());
3465 fontParams.contextPaint = contextPaint;
3468 // Synthetic-bold strikes are each offset one device pixel in run direction.
3469 // (these values are only needed if IsSyntheticBold() is true)
3470 if (IsSyntheticBold()) {
3471 double xscale = CalcXScale(aRunParams.context);
3472 fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
3473 if (xscale != 0.0) {
3474 // use as many strikes as needed for the the increased advance
3475 fontParams.extraStrikes =
3476 std::max(1, NS_lroundf(GetSyntheticBoldOffset() / xscale));
3478 } else {
3479 fontParams.synBoldOnePixelOffset = 0;
3480 fontParams.extraStrikes = 0;
3483 bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
3484 if (!AllowSubpixelAA()) {
3485 aRunParams.dt->SetPermitSubpixelAA(false);
3488 Matrix mat;
3489 Matrix oldMat = aRunParams.dt->GetTransform();
3491 // This is nullptr when we have inverse-transformed glyphs and we need
3492 // to transform the Brush inside flush.
3493 fontParams.passedInvMatrix = nullptr;
3495 fontParams.renderingOptions = GetGlyphRenderingOptions();
3496 fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
3498 // The cairo DrawTarget backend uses the cairo_scaled_font directly
3499 // and so has the font skew matrix applied already.
3500 if (mScaledFont &&
3501 aRunParams.dt->GetBackendType() != BackendType::CAIRO) {
3502 cairo_matrix_t matrix;
3503 cairo_scaled_font_get_font_matrix(mScaledFont, &matrix);
3504 if (matrix.xy != 0) {
3505 // If this matrix applies a skew, which can happen when drawing
3506 // oblique fonts, we will set the DrawTarget matrix to apply the
3507 // skew. We'll need to move the glyphs by the inverse of the skew to
3508 // get the glyphs positioned correctly in the new device space
3509 // though, since the font matrix should only be applied to drawing
3510 // the glyphs, and not to their position.
3511 mat = ToMatrix(*reinterpret_cast<gfxMatrix*>(&matrix));
3513 mat._11 = mat._22 = 1.0;
3514 mat._21 /= GetAdjustedSize();
3516 aRunParams.dt->SetTransform(mat * oldMat);
3518 fontParams.matInv = mat;
3519 fontParams.matInv.Invert();
3521 fontParams.passedInvMatrix = &fontParams.matInv;
3525 double origY = aPt->y;
3526 if (mStyle.baselineOffset != 0.0) {
3527 aPt->y += mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit();
3530 bool emittedGlyphs =
3531 DrawGlyphs(aTextRun, aStart, aEnd - aStart, aPt,
3532 aRunParams, fontParams);
3534 aPt->y = origY;
3536 if (aRunParams.callbacks && emittedGlyphs) {
3537 aRunParams.callbacks->NotifyGlyphPathEmitted();
3540 aRunParams.dt->SetTransform(oldMat);
3541 aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
3544 bool
3545 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
3546 uint32_t aGlyphId, gfxTextContextPaint *aContextPaint) const
3548 if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
3549 return false;
3552 const gfxFloat devUnitsPerSVGUnit =
3553 GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
3554 gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
3556 aContext->Translate(gfxPoint(aPoint.x, aPoint.y));
3557 aContext->Scale(devUnitsPerSVGUnit, devUnitsPerSVGUnit);
3559 aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
3561 return GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, int(aDrawMode),
3562 aContextPaint);
3565 bool
3566 gfxFont::RenderSVGGlyph(gfxContext *aContext, gfxPoint aPoint, DrawMode aDrawMode,
3567 uint32_t aGlyphId, gfxTextContextPaint *aContextPaint,
3568 gfxTextRunDrawCallbacks *aCallbacks,
3569 bool& aEmittedGlyphs) const
3571 if (aCallbacks) {
3572 if (aEmittedGlyphs) {
3573 aCallbacks->NotifyGlyphPathEmitted();
3574 aEmittedGlyphs = false;
3576 aCallbacks->NotifyBeforeSVGGlyphPainted();
3578 bool rendered = RenderSVGGlyph(aContext, aPoint, aDrawMode, aGlyphId,
3579 aContextPaint);
3580 if (aCallbacks) {
3581 aCallbacks->NotifyAfterSVGGlyphPainted();
3583 return rendered;
3586 bool
3587 gfxFont::RenderColorGlyph(gfxContext* aContext,
3588 mozilla::gfx::ScaledFont* scaledFont,
3589 GlyphRenderingOptions* aRenderingOptions,
3590 mozilla::gfx::DrawOptions aDrawOptions,
3591 const mozilla::gfx::Point& aPoint,
3592 uint32_t aGlyphId) const
3594 nsAutoTArray<uint16_t, 8> layerGlyphs;
3595 nsAutoTArray<mozilla::gfx::Color, 8> layerColors;
3597 if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, layerGlyphs, layerColors)) {
3598 return false;
3601 RefPtr<DrawTarget> dt = aContext->GetDrawTarget();
3602 for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
3603 layerIndex++) {
3604 Glyph glyph;
3605 glyph.mIndex = layerGlyphs[layerIndex];
3606 glyph.mPosition = aPoint;
3608 mozilla::gfx::GlyphBuffer buffer;
3609 buffer.mGlyphs = &glyph;
3610 buffer.mNumGlyphs = 1;
3612 dt->FillGlyphs(scaledFont, buffer,
3613 ColorPattern(layerColors[layerIndex]),
3614 aDrawOptions, aRenderingOptions);
3616 return true;
3619 static void
3620 UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax)
3622 *aDestMin = std::min(*aDestMin, aX);
3623 *aDestMax = std::max(*aDestMax, aX);
3626 // We get precise glyph extents if the textrun creator requested them, or
3627 // if the font is a user font --- in which case the author may be relying
3628 // on overflowing glyphs.
3629 static bool
3630 NeedsGlyphExtents(gfxFont *aFont, gfxTextRun *aTextRun)
3632 return (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX) ||
3633 aFont->GetFontEntry()->IsUserFont();
3636 static bool
3637 NeedsGlyphExtents(gfxTextRun *aTextRun)
3639 if (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_NEED_BOUNDING_BOX)
3640 return true;
3641 uint32_t numRuns;
3642 const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
3643 for (uint32_t i = 0; i < numRuns; ++i) {
3644 if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
3645 return true;
3647 return false;
3650 gfxFont::RunMetrics
3651 gfxFont::Measure(gfxTextRun *aTextRun,
3652 uint32_t aStart, uint32_t aEnd,
3653 BoundingBoxType aBoundingBoxType,
3654 gfxContext *aRefContext,
3655 Spacing *aSpacing)
3657 // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
3658 // and the underlying cairo font may be antialiased,
3659 // we need to create a copy in order to avoid getting cached extents.
3660 // This is only used by MathML layout at present.
3661 if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
3662 mAntialiasOption != kAntialiasNone) {
3663 if (!mNonAAFont) {
3664 mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
3666 // if font subclass doesn't implement CopyWithAntialiasOption(),
3667 // it will return null and we'll proceed to use the existing font
3668 if (mNonAAFont) {
3669 return mNonAAFont->Measure(aTextRun, aStart, aEnd,
3670 TIGHT_HINTED_OUTLINE_EXTENTS,
3671 aRefContext, aSpacing);
3675 const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3676 // Current position in appunits
3677 const gfxFont::Metrics& fontMetrics = GetMetrics();
3679 RunMetrics metrics;
3680 metrics.mAscent = fontMetrics.maxAscent*appUnitsPerDevUnit;
3681 metrics.mDescent = fontMetrics.maxDescent*appUnitsPerDevUnit;
3682 if (aStart == aEnd) {
3683 // exit now before we look at aSpacing[0], which is undefined
3684 metrics.mBoundingBox = gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
3685 return metrics;
3688 gfxFloat advanceMin = 0, advanceMax = 0;
3689 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
3690 bool isRTL = aTextRun->IsRightToLeft();
3691 double direction = aTextRun->GetDirection();
3692 bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
3693 gfxGlyphExtents *extents =
3694 ((aBoundingBoxType == LOOSE_INK_EXTENTS &&
3695 !needsGlyphExtents &&
3696 !aTextRun->HasDetailedGlyphs()) ||
3697 (MOZ_UNLIKELY(GetStyle()->size == 0))) ? nullptr
3698 : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
3699 double x = 0;
3700 if (aSpacing) {
3701 x += direction*aSpacing[0].mBefore;
3703 uint32_t i;
3704 for (i = aStart; i < aEnd; ++i) {
3705 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[i];
3706 if (glyphData->IsSimpleGlyph()) {
3707 double advance = glyphData->GetSimpleAdvance();
3708 // Only get the real glyph horizontal extent if we were asked
3709 // for the tight bounding box or we're in quality mode
3710 if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
3711 extents) {
3712 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
3713 uint16_t extentsWidth = extents->GetContainedGlyphWidthAppUnits(glyphIndex);
3714 if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
3715 aBoundingBoxType == LOOSE_INK_EXTENTS) {
3716 UnionRange(x, &advanceMin, &advanceMax);
3717 UnionRange(x + direction*extentsWidth, &advanceMin, &advanceMax);
3718 } else {
3719 gfxRect glyphRect;
3720 if (!extents->GetTightGlyphExtentsAppUnits(this,
3721 aRefContext, glyphIndex, &glyphRect)) {
3722 glyphRect = gfxRect(0, metrics.mBoundingBox.Y(),
3723 advance, metrics.mBoundingBox.Height());
3725 if (isRTL) {
3726 glyphRect -= gfxPoint(advance, 0);
3728 glyphRect += gfxPoint(x, 0);
3729 metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
3732 x += direction*advance;
3733 } else {
3734 uint32_t glyphCount = glyphData->GetGlyphCount();
3735 if (glyphCount > 0) {
3736 const gfxTextRun::DetailedGlyph *details =
3737 aTextRun->GetDetailedGlyphs(i);
3738 NS_ASSERTION(details != nullptr,
3739 "detaiedGlyph record should not be missing!");
3740 uint32_t j;
3741 for (j = 0; j < glyphCount; ++j, ++details) {
3742 uint32_t glyphIndex = details->mGlyphID;
3743 gfxPoint glyphPt(x + details->mXOffset, details->mYOffset);
3744 double advance = details->mAdvance;
3745 gfxRect glyphRect;
3746 if (glyphData->IsMissing() || !extents ||
3747 !extents->GetTightGlyphExtentsAppUnits(this,
3748 aRefContext, glyphIndex, &glyphRect)) {
3749 // We might have failed to get glyph extents due to
3750 // OOM or something
3751 glyphRect = gfxRect(0, -metrics.mAscent,
3752 advance, metrics.mAscent + metrics.mDescent);
3754 if (isRTL) {
3755 glyphRect -= gfxPoint(advance, 0);
3757 glyphRect += gfxPoint(x, 0);
3758 metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
3759 x += direction*advance;
3763 // Every other glyph type is ignored
3764 if (aSpacing) {
3765 double space = aSpacing[i - aStart].mAfter;
3766 if (i + 1 < aEnd) {
3767 space += aSpacing[i + 1 - aStart].mBefore;
3769 x += direction*space;
3773 if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
3774 UnionRange(x, &advanceMin, &advanceMax);
3775 gfxRect fontBox(advanceMin, -metrics.mAscent,
3776 advanceMax - advanceMin, metrics.mAscent + metrics.mDescent);
3777 metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
3779 if (isRTL) {
3780 metrics.mBoundingBox -= gfxPoint(x, 0);
3783 metrics.mAdvanceWidth = x*direction;
3784 return metrics;
3787 static PLDHashOperator
3788 NotifyGlyphChangeObservers(nsPtrHashKey<gfxFont::GlyphChangeObserver>* aKey,
3789 void* aClosure)
3791 aKey->GetKey()->NotifyGlyphsChanged();
3792 return PL_DHASH_NEXT;
3795 void
3796 gfxFont::NotifyGlyphsChanged()
3798 uint32_t i, count = mGlyphExtentsArray.Length();
3799 for (i = 0; i < count; ++i) {
3800 // Flush cached extents array
3801 mGlyphExtentsArray[i]->NotifyGlyphsChanged();
3804 if (mGlyphChangeObservers) {
3805 mGlyphChangeObservers->EnumerateEntries(NotifyGlyphChangeObservers, nullptr);
3809 static bool
3810 IsBoundarySpace(char16_t aChar, char16_t aNextChar)
3812 return (aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar);
3815 static inline uint32_t
3816 HashMix(uint32_t aHash, char16_t aCh)
3818 return (aHash >> 28) ^ (aHash << 4) ^ aCh;
3821 #ifdef __GNUC__
3822 #define GFX_MAYBE_UNUSED __attribute__((unused))
3823 #else
3824 #define GFX_MAYBE_UNUSED
3825 #endif
3827 template<typename T>
3828 gfxShapedWord*
3829 gfxFont::GetShapedWord(gfxContext *aContext,
3830 const T *aText,
3831 uint32_t aLength,
3832 uint32_t aHash,
3833 int32_t aRunScript,
3834 int32_t aAppUnitsPerDevUnit,
3835 uint32_t aFlags,
3836 gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
3838 // if the cache is getting too big, flush it and start over
3839 uint32_t wordCacheMaxEntries =
3840 gfxPlatform::GetPlatform()->WordCacheMaxEntries();
3841 if (mWordCache->Count() > wordCacheMaxEntries) {
3842 NS_WARNING("flushing shaped-word cache");
3843 ClearCachedWords();
3846 // if there's a cached entry for this word, just return it
3847 CacheHashKey key(aText, aLength, aHash,
3848 aRunScript,
3849 aAppUnitsPerDevUnit,
3850 aFlags);
3852 CacheHashEntry *entry = mWordCache->PutEntry(key);
3853 if (!entry) {
3854 NS_WARNING("failed to create word cache entry - expect missing text");
3855 return nullptr;
3857 gfxShapedWord *sw = entry->mShapedWord;
3859 bool isContent = !mStyle.systemFont;
3861 if (sw) {
3862 sw->ResetAge();
3863 Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_HITS_CONTENT :
3864 Telemetry::WORD_CACHE_HITS_CHROME),
3865 aLength);
3866 #ifndef RELEASE_BUILD
3867 if (aTextPerf) {
3868 aTextPerf->current.wordCacheHit++;
3870 #endif
3871 return sw;
3874 Telemetry::Accumulate((isContent ? Telemetry::WORD_CACHE_MISSES_CONTENT :
3875 Telemetry::WORD_CACHE_MISSES_CHROME),
3876 aLength);
3877 #ifndef RELEASE_BUILD
3878 if (aTextPerf) {
3879 aTextPerf->current.wordCacheMiss++;
3881 #endif
3883 sw = entry->mShapedWord = gfxShapedWord::Create(aText, aLength,
3884 aRunScript,
3885 aAppUnitsPerDevUnit,
3886 aFlags);
3887 if (!sw) {
3888 NS_WARNING("failed to create gfxShapedWord - expect missing text");
3889 return nullptr;
3892 DebugOnly<bool> ok =
3893 ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
3895 NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
3897 return sw;
3900 bool
3901 gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const
3903 const gfxShapedWord *sw = mShapedWord;
3904 if (!sw) {
3905 return false;
3907 if (sw->GetLength() != aKey->mLength ||
3908 sw->Flags() != aKey->mFlags ||
3909 sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
3910 sw->Script() != aKey->mScript) {
3911 return false;
3913 if (sw->TextIs8Bit()) {
3914 if (aKey->mTextIs8Bit) {
3915 return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
3916 aKey->mLength * sizeof(uint8_t)));
3918 // The key has 16-bit text, even though all the characters are < 256,
3919 // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
3920 // comparing with will have 8-bit text.
3921 const uint8_t *s1 = sw->Text8Bit();
3922 const char16_t *s2 = aKey->mText.mDouble;
3923 const char16_t *s2end = s2 + aKey->mLength;
3924 while (s2 < s2end) {
3925 if (*s1++ != *s2++) {
3926 return false;
3929 return true;
3931 NS_ASSERTION((aKey->mFlags & gfxTextRunFactory::TEXT_IS_8BIT) == 0 &&
3932 !aKey->mTextIs8Bit, "didn't expect 8-bit text here");
3933 return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
3934 aKey->mLength * sizeof(char16_t)));
3937 bool
3938 gfxFont::ShapeText(gfxContext *aContext,
3939 const uint8_t *aText,
3940 uint32_t aOffset,
3941 uint32_t aLength,
3942 int32_t aScript,
3943 gfxShapedText *aShapedText)
3945 nsDependentCSubstring ascii((const char*)aText, aLength);
3946 nsAutoString utf16;
3947 AppendASCIItoUTF16(ascii, utf16);
3948 if (utf16.Length() != aLength) {
3949 return false;
3951 return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
3952 aScript, aShapedText);
3955 bool
3956 gfxFont::ShapeText(gfxContext *aContext,
3957 const char16_t *aText,
3958 uint32_t aOffset,
3959 uint32_t aLength,
3960 int32_t aScript,
3961 gfxShapedText *aShapedText)
3963 bool ok = false;
3965 if (FontCanSupportGraphite()) {
3966 if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
3967 if (!mGraphiteShaper) {
3968 mGraphiteShaper = new gfxGraphiteShaper(this);
3970 ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
3971 aScript, aShapedText);
3975 if (!ok) {
3976 if (!mHarfBuzzShaper) {
3977 mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
3979 ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
3980 aScript, aShapedText);
3983 NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
3985 PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
3987 return ok;
3990 void
3991 gfxFont::PostShapingFixup(gfxContext *aContext,
3992 const char16_t *aText,
3993 uint32_t aOffset,
3994 uint32_t aLength,
3995 gfxShapedText *aShapedText)
3997 if (IsSyntheticBold()) {
3998 float synBoldOffset =
3999 GetSyntheticBoldOffset() * CalcXScale(aContext);
4000 aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset,
4001 aOffset, aLength);
4005 #define MAX_SHAPING_LENGTH 32760 // slightly less than 32K, trying to avoid
4006 // over-stressing platform shapers
4007 #define BACKTRACK_LIMIT 16 // backtrack this far looking for a good place
4008 // to split into fragments for separate shaping
4010 template<typename T>
4011 bool
4012 gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
4013 const T *aText,
4014 uint32_t aOffset,
4015 uint32_t aLength,
4016 int32_t aScript,
4017 gfxTextRun *aTextRun)
4019 aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
4021 bool ok = true;
4023 while (ok && aLength > 0) {
4024 uint32_t fragLen = aLength;
4026 // limit the length of text we pass to shapers in a single call
4027 if (fragLen > MAX_SHAPING_LENGTH) {
4028 fragLen = MAX_SHAPING_LENGTH;
4030 // in the 8-bit case, there are no multi-char clusters,
4031 // so we don't need to do this check
4032 if (sizeof(T) == sizeof(char16_t)) {
4033 uint32_t i;
4034 for (i = 0; i < BACKTRACK_LIMIT; ++i) {
4035 if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
4036 fragLen -= i;
4037 break;
4040 if (i == BACKTRACK_LIMIT) {
4041 // if we didn't find any cluster start while backtracking,
4042 // just check that we're not in the middle of a surrogate
4043 // pair; back up by one code unit if we are.
4044 if (NS_IS_LOW_SURROGATE(aText[fragLen]) &&
4045 NS_IS_HIGH_SURROGATE(aText[fragLen - 1])) {
4046 --fragLen;
4052 ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
4054 aText += fragLen;
4055 aOffset += fragLen;
4056 aLength -= fragLen;
4059 return ok;
4062 // Check if aCh is an unhandled control character that should be displayed
4063 // as a hexbox rather than rendered by some random font on the system.
4064 // We exclude \r as stray &#13;s are rather common (bug 941940).
4065 // Note that \n and \t don't come through here, as they have specific
4066 // meanings that have already been handled.
4067 static bool
4068 IsInvalidControlChar(uint32_t aCh)
4070 return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
4073 template<typename T>
4074 bool
4075 gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
4076 const T *aText,
4077 uint32_t aOffset,
4078 uint32_t aLength,
4079 int32_t aScript,
4080 gfxTextRun *aTextRun)
4082 uint32_t fragStart = 0;
4083 bool ok = true;
4085 for (uint32_t i = 0; i <= aLength && ok; ++i) {
4086 T ch = (i < aLength) ? aText[i] : '\n';
4087 bool invalid = gfxFontGroup::IsInvalidChar(ch);
4088 uint32_t length = i - fragStart;
4090 // break into separate fragments when we hit an invalid char
4091 if (!invalid) {
4092 continue;
4095 if (length > 0) {
4096 ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
4097 aOffset + fragStart, length,
4098 aScript, aTextRun);
4101 if (i == aLength) {
4102 break;
4105 // fragment was terminated by an invalid char: skip it,
4106 // unless it's a control char that we want to show as a hexbox,
4107 // but record where TAB or NEWLINE occur
4108 if (ch == '\t') {
4109 aTextRun->SetIsTab(aOffset + i);
4110 } else if (ch == '\n') {
4111 aTextRun->SetIsNewline(aOffset + i);
4112 } else if (IsInvalidControlChar(ch) &&
4113 !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
4114 aTextRun->SetMissingGlyph(aOffset + i, ch, this);
4116 fragStart = i + 1;
4119 NS_WARN_IF_FALSE(ok, "failed to shape text - expect garbled text");
4120 return ok;
4123 #ifndef RELEASE_BUILD
4124 #define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
4125 #else
4126 #define TEXT_PERF_INCR(tp, m)
4127 #endif
4129 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
4130 inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
4132 inline static bool HasSpaces(const uint8_t *aString, uint32_t aLen)
4134 return memchr(aString, 0x20, aLen) != nullptr;
4137 inline static bool HasSpaces(const char16_t *aString, uint32_t aLen)
4139 for (const char16_t *ch = aString; ch < aString + aLen; ch++) {
4140 if (*ch == 0x20) {
4141 return true;
4144 return false;
4147 template<typename T>
4148 bool
4149 gfxFont::SplitAndInitTextRun(gfxContext *aContext,
4150 gfxTextRun *aTextRun,
4151 const T *aString, // text for this font run
4152 uint32_t aRunStart, // position in the textrun
4153 uint32_t aRunLength,
4154 int32_t aRunScript)
4156 if (aRunLength == 0) {
4157 return true;
4160 gfxTextPerfMetrics *tp = nullptr;
4162 #ifndef RELEASE_BUILD
4163 tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
4164 if (tp) {
4165 if (mStyle.systemFont) {
4166 tp->current.numChromeTextRuns++;
4167 } else {
4168 tp->current.numContentTextRuns++;
4170 tp->current.numChars += aRunLength;
4171 if (aRunLength > tp->current.maxTextRunLen) {
4172 tp->current.maxTextRunLen = aRunLength;
4175 #endif
4177 uint32_t wordCacheCharLimit =
4178 gfxPlatform::GetPlatform()->WordCacheCharLimit();
4180 // If spaces can participate in shaping (e.g. within lookups for automatic
4181 // fractions), need to shape without using the word cache which segments
4182 // textruns on space boundaries. Word cache can be used if the textrun
4183 // is short enough to fit in the word cache and it lacks spaces.
4184 if (SpaceMayParticipateInShaping(aRunScript)) {
4185 if (aRunLength > wordCacheCharLimit ||
4186 HasSpaces(aString, aRunLength)) {
4187 TEXT_PERF_INCR(tp, wordCacheSpaceRules);
4188 return ShapeTextWithoutWordCache(aContext, aString,
4189 aRunStart, aRunLength, aRunScript,
4190 aTextRun);
4194 InitWordCache();
4196 // the only flags we care about for ShapedWord construction/caching
4197 uint32_t flags = aTextRun->GetFlags();
4198 flags &= (gfxTextRunFactory::TEXT_IS_RTL |
4199 gfxTextRunFactory::TEXT_DISABLE_OPTIONAL_LIGATURES |
4200 gfxTextRunFactory::TEXT_USE_MATH_SCRIPT);
4201 if (sizeof(T) == sizeof(uint8_t)) {
4202 flags |= gfxTextRunFactory::TEXT_IS_8BIT;
4205 uint32_t wordStart = 0;
4206 uint32_t hash = 0;
4207 bool wordIs8Bit = true;
4208 int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
4210 T nextCh = aString[0];
4211 for (uint32_t i = 0; i <= aRunLength; ++i) {
4212 T ch = nextCh;
4213 nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
4214 bool boundary = IsBoundarySpace(ch, nextCh);
4215 bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
4216 uint32_t length = i - wordStart;
4218 // break into separate ShapedWords when we hit an invalid char,
4219 // or a boundary space (always handled individually),
4220 // or the first non-space after a space
4221 if (!boundary && !invalid) {
4222 if (!IsChar8Bit(ch)) {
4223 wordIs8Bit = false;
4225 // include this character in the hash, and move on to next
4226 hash = HashMix(hash, ch);
4227 continue;
4230 // We've decided to break here (i.e. we're at the end of a "word");
4231 // shape the word and add it to the textrun.
4232 // For words longer than the limit, we don't use the
4233 // font's word cache but just shape directly into the textrun.
4234 if (length > wordCacheCharLimit) {
4235 TEXT_PERF_INCR(tp, wordCacheLong);
4236 bool ok = ShapeFragmentWithoutWordCache(aContext,
4237 aString + wordStart,
4238 aRunStart + wordStart,
4239 length,
4240 aRunScript,
4241 aTextRun);
4242 if (!ok) {
4243 return false;
4245 } else if (length > 0) {
4246 uint32_t wordFlags = flags;
4247 // in the 8-bit version of this method, TEXT_IS_8BIT was
4248 // already set as part of |flags|, so no need for a per-word
4249 // adjustment here
4250 if (sizeof(T) == sizeof(char16_t)) {
4251 if (wordIs8Bit) {
4252 wordFlags |= gfxTextRunFactory::TEXT_IS_8BIT;
4255 gfxShapedWord *sw = GetShapedWord(aContext,
4256 aString + wordStart, length,
4257 hash, aRunScript,
4258 appUnitsPerDevUnit,
4259 wordFlags, tp);
4260 if (sw) {
4261 aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
4262 } else {
4263 return false; // failed, presumably out of memory?
4267 if (boundary) {
4268 // word was terminated by a space: add that to the textrun
4269 if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
4270 aRunStart + i, ch))
4272 static const uint8_t space = ' ';
4273 gfxShapedWord *sw =
4274 GetShapedWord(aContext,
4275 &space, 1,
4276 HashMix(0, ' '), aRunScript,
4277 appUnitsPerDevUnit,
4278 flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
4279 if (sw) {
4280 aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
4281 } else {
4282 return false;
4285 hash = 0;
4286 wordStart = i + 1;
4287 wordIs8Bit = true;
4288 continue;
4291 if (i == aRunLength) {
4292 break;
4295 NS_ASSERTION(invalid,
4296 "how did we get here except via an invalid char?");
4298 // word was terminated by an invalid char: skip it,
4299 // unless it's a control char that we want to show as a hexbox,
4300 // but record where TAB or NEWLINE occur
4301 if (ch == '\t') {
4302 aTextRun->SetIsTab(aRunStart + i);
4303 } else if (ch == '\n') {
4304 aTextRun->SetIsNewline(aRunStart + i);
4305 } else if (IsInvalidControlChar(ch) &&
4306 !(aTextRun->GetFlags() & gfxTextRunFactory::TEXT_HIDE_CONTROL_CHARACTERS)) {
4307 aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
4310 hash = 0;
4311 wordStart = i + 1;
4312 wordIs8Bit = true;
4315 return true;
4318 gfxGlyphExtents *
4319 gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
4320 uint32_t i, count = mGlyphExtentsArray.Length();
4321 for (i = 0; i < count; ++i) {
4322 if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
4323 return mGlyphExtentsArray[i];
4325 gfxGlyphExtents *glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
4326 if (glyphExtents) {
4327 mGlyphExtentsArray.AppendElement(glyphExtents);
4328 // Initialize the extents of a space glyph, assuming that spaces don't
4329 // render anything!
4330 glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
4332 return glyphExtents;
4335 void
4336 gfxFont::SetupGlyphExtents(gfxContext *aContext, uint32_t aGlyphID, bool aNeedTight,
4337 gfxGlyphExtents *aExtents)
4339 gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
4340 aContext->IdentityMatrix();
4342 gfxRect svgBounds;
4343 if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
4344 mFontEntry->GetSVGGlyphExtents(aContext, aGlyphID, &svgBounds)) {
4345 gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
4346 aExtents->SetTightGlyphExtents(aGlyphID,
4347 gfxRect(svgBounds.x * d2a,
4348 svgBounds.y * d2a,
4349 svgBounds.width * d2a,
4350 svgBounds.height * d2a));
4351 return;
4354 cairo_glyph_t glyph;
4355 glyph.index = aGlyphID;
4356 glyph.x = 0;
4357 glyph.y = 0;
4358 cairo_text_extents_t extents;
4359 cairo_glyph_extents(aContext->GetCairo(), &glyph, 1, &extents);
4361 const Metrics& fontMetrics = GetMetrics();
4362 int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
4363 if (!aNeedTight && extents.x_bearing >= 0 &&
4364 extents.y_bearing >= -fontMetrics.maxAscent &&
4365 extents.height + extents.y_bearing <= fontMetrics.maxDescent) {
4366 uint32_t appUnitsWidth =
4367 uint32_t(ceil((extents.x_bearing + extents.width)*appUnitsPerDevUnit));
4368 if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
4369 aExtents->SetContainedGlyphWidthAppUnits(aGlyphID, uint16_t(appUnitsWidth));
4370 return;
4373 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
4374 if (!aNeedTight) {
4375 ++gGlyphExtentsSetupFallBackToTight;
4377 #endif
4379 gfxFloat d2a = appUnitsPerDevUnit;
4380 gfxRect bounds(extents.x_bearing*d2a, extents.y_bearing*d2a,
4381 extents.width*d2a, extents.height*d2a);
4382 aExtents->SetTightGlyphExtents(aGlyphID, bounds);
4385 // Try to initialize font metrics by reading sfnt tables directly;
4386 // set mIsValid=TRUE and return TRUE on success.
4387 // Return FALSE if the gfxFontEntry subclass does not
4388 // implement GetFontTable(), or for non-sfnt fonts where tables are
4389 // not available.
4390 // If this returns TRUE without setting the mIsValid flag, then we -did-
4391 // apparently find an sfnt, but it was too broken to be used.
4392 bool
4393 gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
4395 mIsValid = false; // font is NOT valid in case of early return
4397 const uint32_t kHheaTableTag = TRUETYPE_TAG('h','h','e','a');
4398 const uint32_t kPostTableTag = TRUETYPE_TAG('p','o','s','t');
4399 const uint32_t kOS_2TableTag = TRUETYPE_TAG('O','S','/','2');
4401 uint32_t len;
4403 if (mFUnitsConvFactor == 0.0) {
4404 // If the conversion factor from FUnits is not yet set,
4405 // get the unitsPerEm from the 'head' table via the font entry
4406 uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
4407 if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
4408 return false;
4410 mFUnitsConvFactor = mAdjustedSize / unitsPerEm;
4413 // 'hhea' table is required to get vertical extents
4414 gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
4415 if (!hheaTable) {
4416 return false; // no 'hhea' table -> not an sfnt
4418 const HheaTable* hhea =
4419 reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
4420 if (len < sizeof(HheaTable)) {
4421 return false;
4424 #define SET_UNSIGNED(field,src) aMetrics.field = uint16_t(src) * mFUnitsConvFactor
4425 #define SET_SIGNED(field,src) aMetrics.field = int16_t(src) * mFUnitsConvFactor
4427 SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
4428 SET_SIGNED(maxAscent, hhea->ascender);
4429 SET_SIGNED(maxDescent, -int16_t(hhea->descender));
4430 SET_SIGNED(externalLeading, hhea->lineGap);
4432 // 'post' table is required for underline metrics
4433 gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
4434 if (!postTable) {
4435 return true; // no 'post' table -> sfnt is not valid
4437 const PostTable *post =
4438 reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
4439 if (len < offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
4440 return true; // bad post table -> sfnt is not valid
4443 SET_SIGNED(underlineOffset, post->underlinePosition);
4444 SET_UNSIGNED(underlineSize, post->underlineThickness);
4446 // 'OS/2' table is optional, if not found we'll estimate xHeight
4447 // and aveCharWidth by measuring glyphs
4448 gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
4449 if (os2Table) {
4450 const OS2Table *os2 =
4451 reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
4452 if (len >= offsetof(OS2Table, sxHeight) + sizeof(int16_t) &&
4453 uint16_t(os2->version) >= 2) {
4454 // version 2 and later includes the x-height field
4455 SET_SIGNED(xHeight, os2->sxHeight);
4456 // Abs because of negative xHeight seen in Kokonor (Tibetan) font
4457 aMetrics.xHeight = Abs(aMetrics.xHeight);
4459 // this should always be present in any valid OS/2 of any version
4460 if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
4461 SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
4462 SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
4463 SET_SIGNED(strikeoutOffset, os2->yStrikeoutPosition);
4465 // for fonts with USE_TYPO_METRICS set in the fsSelection field,
4466 // and for all OpenType math fonts (having a 'MATH' table),
4467 // let the OS/2 sTypo* metrics override those from the hhea table
4468 // (see http://www.microsoft.com/typography/otspec/os2.htm#fss)
4469 const uint16_t kUseTypoMetricsMask = 1 << 7;
4470 if ((uint16_t(os2->fsSelection) & kUseTypoMetricsMask) ||
4471 mFontEntry->HasFontTable(TRUETYPE_TAG('M','A','T','H'))) {
4472 SET_SIGNED(maxAscent, os2->sTypoAscender);
4473 SET_SIGNED(maxDescent, - int16_t(os2->sTypoDescender));
4474 SET_SIGNED(externalLeading, os2->sTypoLineGap);
4479 mIsValid = true;
4481 return true;
4484 static double
4485 RoundToNearestMultiple(double aValue, double aFraction)
4487 return floor(aValue/aFraction + 0.5) * aFraction;
4490 void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics)
4492 aMetrics.maxAscent =
4493 ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1/1024.0));
4494 aMetrics.maxDescent =
4495 ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1/1024.0));
4497 if (aMetrics.xHeight <= 0) {
4498 // only happens if we couldn't find either font metrics
4499 // or a char to measure;
4500 // pick an arbitrary value that's better than zero
4501 aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
4504 aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
4506 if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
4507 aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
4508 } else {
4509 aMetrics.internalLeading = 0.0;
4512 aMetrics.emAscent = aMetrics.maxAscent * aMetrics.emHeight
4513 / aMetrics.maxHeight;
4514 aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
4516 if (GetFontEntry()->IsFixedPitch()) {
4517 // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
4518 // advance than the average character width... this forces
4519 // those fonts to be recognized like fixed pitch fonts by layout.
4520 aMetrics.maxAdvance = aMetrics.aveCharWidth;
4523 if (!aMetrics.strikeoutOffset) {
4524 aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
4526 if (!aMetrics.strikeoutSize) {
4527 aMetrics.strikeoutSize = aMetrics.underlineSize;
4531 void
4532 gfxFont::SanitizeMetrics(gfxFont::Metrics *aMetrics, bool aIsBadUnderlineFont)
4534 // Even if this font size is zero, this font is created with non-zero size.
4535 // However, for layout and others, we should return the metrics of zero size font.
4536 if (mStyle.size == 0.0) {
4537 memset(aMetrics, 0, sizeof(gfxFont::Metrics));
4538 return;
4541 aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
4542 aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
4544 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
4546 if (aMetrics->maxAscent < 1.0) {
4547 // We cannot draw strikeout line and overline in the ascent...
4548 aMetrics->underlineSize = 0;
4549 aMetrics->underlineOffset = 0;
4550 aMetrics->strikeoutSize = 0;
4551 aMetrics->strikeoutOffset = 0;
4552 return;
4556 * Some CJK fonts have bad underline offset. Therefore, if this is such font,
4557 * we need to lower the underline offset to bottom of *em* descent.
4558 * However, if this is system font, we should not do this for the rendering compatibility with
4559 * another application's UI on the platform.
4560 * XXX Should not use this hack if the font size is too small?
4561 * Such text cannot be read, this might be used for tight CSS rendering? (E.g., Acid2)
4563 if (!mStyle.systemFont && aIsBadUnderlineFont) {
4564 // First, we need 2 pixels between baseline and underline at least. Because many CJK characters
4565 // put their glyphs on the baseline, so, 1 pixel is too close for CJK characters.
4566 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
4568 // Next, we put the underline to bottom of below of the descent space.
4569 if (aMetrics->internalLeading + aMetrics->externalLeading > aMetrics->underlineSize) {
4570 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
4571 } else {
4572 aMetrics->underlineOffset = std::min(aMetrics->underlineOffset,
4573 aMetrics->underlineSize - aMetrics->emDescent);
4576 // If underline positioned is too far from the text, descent position is preferred so that underline
4577 // will stay within the boundary.
4578 else if (aMetrics->underlineSize - aMetrics->underlineOffset > aMetrics->maxDescent) {
4579 if (aMetrics->underlineSize > aMetrics->maxDescent)
4580 aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
4581 // The max underlineOffset is 1px (the min underlineSize is 1px, and min maxDescent is 0px.)
4582 aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
4585 // If strikeout line is overflowed from the ascent, the line should be resized and moved for
4586 // that being in the ascent space.
4587 // Note that the strikeoutOffset is *middle* of the strikeout line position.
4588 gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
4589 if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
4590 if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
4591 aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
4592 halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
4594 gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
4595 aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
4598 // If overline is larger than the ascent, the line should be resized.
4599 if (aMetrics->underlineSize > aMetrics->maxAscent) {
4600 aMetrics->underlineSize = aMetrics->maxAscent;
4604 gfxFloat
4605 gfxFont::SynthesizeSpaceWidth(uint32_t aCh)
4607 // return an appropriate width for various Unicode space characters
4608 // that we "fake" if they're not actually present in the font;
4609 // returns negative value if the char is not a known space.
4610 switch (aCh) {
4611 case 0x2000: // en quad
4612 case 0x2002: return GetAdjustedSize() / 2; // en space
4613 case 0x2001: // em quad
4614 case 0x2003: return GetAdjustedSize(); // em space
4615 case 0x2004: return GetAdjustedSize() / 3; // three-per-em space
4616 case 0x2005: return GetAdjustedSize() / 4; // four-per-em space
4617 case 0x2006: return GetAdjustedSize() / 6; // six-per-em space
4618 case 0x2007: return GetMetrics().zeroOrAveCharWidth; // figure space
4619 case 0x2008: return GetMetrics().spaceWidth; // punctuation space
4620 case 0x2009: return GetAdjustedSize() / 5; // thin space
4621 case 0x200a: return GetAdjustedSize() / 10; // hair space
4622 case 0x202f: return GetAdjustedSize() / 5; // narrow no-break space
4623 default: return -1.0;
4627 void
4628 gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
4629 FontCacheSizes* aSizes) const
4631 for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
4632 aSizes->mFontInstances +=
4633 mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
4635 if (mWordCache) {
4636 aSizes->mShapedWords += mWordCache->SizeOfIncludingThis(aMallocSizeOf);
4640 void
4641 gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
4642 FontCacheSizes* aSizes) const
4644 aSizes->mFontInstances += aMallocSizeOf(this);
4645 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
4648 void
4649 gfxFont::AddGlyphChangeObserver(GlyphChangeObserver *aObserver)
4651 if (!mGlyphChangeObservers) {
4652 mGlyphChangeObservers = new nsTHashtable<nsPtrHashKey<GlyphChangeObserver> >;
4654 mGlyphChangeObservers->PutEntry(aObserver);
4657 void
4658 gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver *aObserver)
4660 NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
4661 NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver), "Observer not registered");
4662 mGlyphChangeObservers->RemoveEntry(aObserver);
4665 gfxGlyphExtents::~gfxGlyphExtents()
4667 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
4668 gGlyphExtentsWidthsTotalSize +=
4669 mContainedGlyphWidths.SizeOfExcludingThis(&FontCacheMallocSizeOf);
4670 gGlyphExtentsCount++;
4671 #endif
4672 MOZ_COUNT_DTOR(gfxGlyphExtents);
4675 bool
4676 gfxGlyphExtents::GetTightGlyphExtentsAppUnits(gfxFont *aFont,
4677 gfxContext *aContext, uint32_t aGlyphID, gfxRect *aExtents)
4679 HashEntry *entry = mTightGlyphExtents.GetEntry(aGlyphID);
4680 if (!entry) {
4681 if (!aContext) {
4682 NS_WARNING("Could not get glyph extents (no aContext)");
4683 return false;
4686 if (aFont->SetupCairoFont(aContext)) {
4687 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
4688 ++gGlyphExtentsSetupLazyTight;
4689 #endif
4690 aFont->SetupGlyphExtents(aContext, aGlyphID, true, this);
4691 entry = mTightGlyphExtents.GetEntry(aGlyphID);
4693 if (!entry) {
4694 NS_WARNING("Could not get glyph extents");
4695 return false;
4699 *aExtents = gfxRect(entry->x, entry->y, entry->width, entry->height);
4700 return true;
4703 gfxGlyphExtents::GlyphWidths::~GlyphWidths()
4705 uint32_t i, count = mBlocks.Length();
4706 for (i = 0; i < count; ++i) {
4707 uintptr_t bits = mBlocks[i];
4708 if (bits && !(bits & 0x1)) {
4709 delete[] reinterpret_cast<uint16_t *>(bits);
4714 uint32_t
4715 gfxGlyphExtents::GlyphWidths::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
4717 uint32_t i;
4718 uint32_t size = mBlocks.SizeOfExcludingThis(aMallocSizeOf);
4719 for (i = 0; i < mBlocks.Length(); ++i) {
4720 uintptr_t bits = mBlocks[i];
4721 if (bits && !(bits & 0x1)) {
4722 size += aMallocSizeOf(reinterpret_cast<void*>(bits));
4725 return size;
4728 void
4729 gfxGlyphExtents::GlyphWidths::Set(uint32_t aGlyphID, uint16_t aWidth)
4731 uint32_t block = aGlyphID >> BLOCK_SIZE_BITS;
4732 uint32_t len = mBlocks.Length();
4733 if (block >= len) {
4734 uintptr_t *elems = mBlocks.AppendElements(block + 1 - len);
4735 if (!elems)
4736 return;
4737 memset(elems, 0, sizeof(uintptr_t)*(block + 1 - len));
4740 uintptr_t bits = mBlocks[block];
4741 uint32_t glyphOffset = aGlyphID & (BLOCK_SIZE - 1);
4742 if (!bits) {
4743 mBlocks[block] = MakeSingle(glyphOffset, aWidth);
4744 return;
4747 uint16_t *newBlock;
4748 if (bits & 0x1) {
4749 // Expand the block to a real block. We could avoid this by checking
4750 // glyphOffset == GetGlyphOffset(bits), but that never happens so don't bother
4751 newBlock = new uint16_t[BLOCK_SIZE];
4752 if (!newBlock)
4753 return;
4754 uint32_t i;
4755 for (i = 0; i < BLOCK_SIZE; ++i) {
4756 newBlock[i] = INVALID_WIDTH;
4758 newBlock[GetGlyphOffset(bits)] = GetWidth(bits);
4759 mBlocks[block] = reinterpret_cast<uintptr_t>(newBlock);
4760 } else {
4761 newBlock = reinterpret_cast<uint16_t *>(bits);
4763 newBlock[glyphOffset] = aWidth;
4766 void
4767 gfxGlyphExtents::SetTightGlyphExtents(uint32_t aGlyphID, const gfxRect& aExtentsAppUnits)
4769 HashEntry *entry = mTightGlyphExtents.PutEntry(aGlyphID);
4770 if (!entry)
4771 return;
4772 entry->x = aExtentsAppUnits.X();
4773 entry->y = aExtentsAppUnits.Y();
4774 entry->width = aExtentsAppUnits.Width();
4775 entry->height = aExtentsAppUnits.Height();
4778 size_t
4779 gfxGlyphExtents::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
4781 return mContainedGlyphWidths.SizeOfExcludingThis(aMallocSizeOf) +
4782 mTightGlyphExtents.SizeOfExcludingThis(nullptr, aMallocSizeOf);
4785 size_t
4786 gfxGlyphExtents::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
4788 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
4791 gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
4792 const gfxFontStyle *aStyle,
4793 gfxUserFontSet *aUserFontSet)
4794 : mFamilyList(aFontFamilyList)
4795 , mStyle(*aStyle)
4796 , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
4797 , mHyphenWidth(-1)
4798 , mUserFontSet(aUserFontSet)
4799 , mTextPerf(nullptr)
4800 , mPageLang(gfxPlatform::GetFontPrefLangFor(aStyle->language))
4801 , mSkipDrawing(false)
4803 // We don't use SetUserFontSet() here, as we want to unconditionally call
4804 // BuildFontList() rather than only do UpdateFontList() if it changed.
4805 mCurrGeneration = GetGeneration();
4806 BuildFontList();
4809 void
4810 gfxFontGroup::FindGenericFonts(FontFamilyType aGenericType,
4811 nsIAtom *aLanguage,
4812 void *aClosure)
4814 nsAutoTArray<nsString, 5> resolvedGenerics;
4815 ResolveGenericFontNames(aGenericType, aLanguage, resolvedGenerics);
4816 uint32_t g = 0, numGenerics = resolvedGenerics.Length();
4817 for (g = 0; g < numGenerics; g++) {
4818 FindPlatformFont(resolvedGenerics[g], false, aClosure);
4822 /* static */ void
4823 gfxFontGroup::ResolveGenericFontNames(FontFamilyType aGenericType,
4824 nsIAtom *aLanguage,
4825 nsTArray<nsString>& aGenericFamilies)
4827 static const char kGeneric_serif[] = "serif";
4828 static const char kGeneric_sans_serif[] = "sans-serif";
4829 static const char kGeneric_monospace[] = "monospace";
4830 static const char kGeneric_cursive[] = "cursive";
4831 static const char kGeneric_fantasy[] = "fantasy";
4833 // treat -moz-fixed as monospace
4834 if (aGenericType == eFamily_moz_fixed) {
4835 aGenericType = eFamily_monospace;
4838 // type should be standard generic type at this point
4839 NS_ASSERTION(aGenericType >= eFamily_serif &&
4840 aGenericType <= eFamily_fantasy,
4841 "standard generic font family type required");
4843 // create the lang string
4844 nsIAtom *langGroupAtom = nullptr;
4845 nsAutoCString langGroupString;
4846 if (aLanguage) {
4847 if (!gLangService) {
4848 CallGetService(NS_LANGUAGEATOMSERVICE_CONTRACTID, &gLangService);
4850 if (gLangService) {
4851 nsresult rv;
4852 langGroupAtom = gLangService->GetLanguageGroup(aLanguage, &rv);
4855 if (!langGroupAtom) {
4856 langGroupAtom = nsGkAtoms::Unicode;
4858 langGroupAtom->ToUTF8String(langGroupString);
4860 // map generic type to string
4861 const char *generic = nullptr;
4862 switch (aGenericType) {
4863 case eFamily_serif:
4864 generic = kGeneric_serif;
4865 break;
4866 case eFamily_sans_serif:
4867 generic = kGeneric_sans_serif;
4868 break;
4869 case eFamily_monospace:
4870 generic = kGeneric_monospace;
4871 break;
4872 case eFamily_cursive:
4873 generic = kGeneric_cursive;
4874 break;
4875 case eFamily_fantasy:
4876 generic = kGeneric_fantasy;
4877 break;
4878 default:
4879 break;
4882 if (!generic) {
4883 return;
4886 aGenericFamilies.Clear();
4888 // load family for "font.name.generic.lang"
4889 nsAutoCString prefFontName("font.name.");
4890 prefFontName.Append(generic);
4891 prefFontName.Append('.');
4892 prefFontName.Append(langGroupString);
4893 gfxFontUtils::AppendPrefsFontList(prefFontName.get(),
4894 aGenericFamilies);
4896 // if lang has pref fonts, also load fonts for "font.name-list.generic.lang"
4897 if (!aGenericFamilies.IsEmpty()) {
4898 nsAutoCString prefFontListName("font.name-list.");
4899 prefFontListName.Append(generic);
4900 prefFontListName.Append('.');
4901 prefFontListName.Append(langGroupString);
4902 gfxFontUtils::AppendPrefsFontList(prefFontListName.get(),
4903 aGenericFamilies);
4906 #if 0 // dump out generic mappings
4907 printf("%s ===> ", prefFontName.get());
4908 for (uint32_t k = 0; k < aGenericFamilies.Length(); k++) {
4909 if (k > 0) printf(", ");
4910 printf("%s", NS_ConvertUTF16toUTF8(aGenericFamilies[k]).get());
4912 printf("\n");
4913 #endif
4916 void gfxFontGroup::EnumerateFontList(nsIAtom *aLanguage, void *aClosure)
4918 // initialize fonts in the font family list
4919 const nsTArray<FontFamilyName>& fontlist = mFamilyList.GetFontlist();
4921 // lookup fonts in the fontlist
4922 uint32_t i, numFonts = fontlist.Length();
4923 for (i = 0; i < numFonts; i++) {
4924 const FontFamilyName& name = fontlist[i];
4925 if (name.IsNamed()) {
4926 FindPlatformFont(name.mName, true, aClosure);
4927 } else {
4928 FindGenericFonts(name.mType, aLanguage, aClosure);
4932 // if necessary, append default generic onto the end
4933 if (mFamilyList.GetDefaultFontType() != eFamily_none &&
4934 !mFamilyList.HasDefaultGeneric()) {
4935 FindGenericFonts(mFamilyList.GetDefaultFontType(),
4936 aLanguage,
4937 aClosure);
4941 void
4942 gfxFontGroup::BuildFontList()
4944 // gfxPangoFontGroup behaves differently, so this method is a no-op on that platform
4945 #if defined(XP_MACOSX) || defined(XP_WIN) || defined(ANDROID)
4947 EnumerateFontList(mStyle.language);
4949 // at this point, fontlist should have been filled in
4950 // get a default font if none exists
4951 if (mFonts.Length() == 0) {
4952 bool needsBold;
4953 gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
4954 gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
4955 NS_ASSERTION(defaultFamily,
4956 "invalid default font returned by GetDefaultFont");
4958 if (defaultFamily) {
4959 gfxFontEntry *fe = defaultFamily->FindFontForStyle(mStyle,
4960 needsBold);
4961 if (fe) {
4962 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
4963 needsBold);
4964 if (font) {
4965 mFonts.AppendElement(FamilyFace(defaultFamily, font));
4970 if (mFonts.Length() == 0) {
4971 // Try for a "font of last resort...."
4972 // Because an empty font list would be Really Bad for later code
4973 // that assumes it will be able to get valid metrics for layout,
4974 // just look for the first usable font and put in the list.
4975 // (see bug 554544)
4976 nsAutoTArray<nsRefPtr<gfxFontFamily>,200> families;
4977 pfl->GetFontFamilyList(families);
4978 uint32_t count = families.Length();
4979 for (uint32_t i = 0; i < count; ++i) {
4980 gfxFontEntry *fe = families[i]->FindFontForStyle(mStyle,
4981 needsBold);
4982 if (fe) {
4983 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle,
4984 needsBold);
4985 if (font) {
4986 mFonts.AppendElement(FamilyFace(families[i], font));
4987 break;
4993 if (mFonts.Length() == 0) {
4994 // an empty font list at this point is fatal; we're not going to
4995 // be able to do even the most basic layout operations
4996 char msg[256]; // CHECK buffer length if revising message below
4997 nsAutoString families;
4998 mFamilyList.ToString(families);
4999 sprintf(msg, "unable to find a usable font (%.220s)",
5000 NS_ConvertUTF16toUTF8(families).get());
5001 NS_RUNTIMEABORT(msg);
5005 if (!mStyle.systemFont) {
5006 uint32_t count = mFonts.Length();
5007 for (uint32_t i = 0; i < count; ++i) {
5008 gfxFont* font = mFonts[i].Font();
5009 if (font->GetFontEntry()->mIsBadUnderlineFont) {
5010 gfxFloat first = mFonts[0].Font()->GetMetrics().underlineOffset;
5011 gfxFloat bad = font->GetMetrics().underlineOffset;
5012 mUnderlineOffset = std::min(first, bad);
5013 break;
5017 #endif
5020 void
5021 gfxFontGroup::FindPlatformFont(const nsAString& aName,
5022 bool aUseFontSet,
5023 void *aClosure)
5025 bool needsBold;
5026 gfxFontFamily *family = nullptr;
5027 gfxFontEntry *fe = nullptr;
5029 if (aUseFontSet) {
5030 // First, look up in the user font set...
5031 // If the fontSet matches the family, we must not look for a platform
5032 // font of the same name, even if we fail to actually get a fontEntry
5033 // here; we'll fall back to the next name in the CSS font-family list.
5034 if (mUserFontSet) {
5035 // If the fontSet matches the family, but the font has not yet finished
5036 // loading (nor has its load timeout fired), the fontGroup should wait
5037 // for the download, and not actually draw its text yet.
5038 family = mUserFontSet->LookupFamily(aName);
5039 if (family) {
5040 bool waitForUserFont = false;
5041 fe = mUserFontSet->FindFontEntry(family, mStyle,
5042 needsBold, waitForUserFont);
5043 if (!fe && waitForUserFont) {
5044 mSkipDrawing = true;
5050 // Not known in the user font set ==> check system fonts
5051 if (!family) {
5052 gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
5053 family = fontList->FindFamily(aName, mStyle.systemFont);
5054 if (family) {
5055 fe = family->FindFontForStyle(mStyle, needsBold);
5059 // add to the font group, unless it's already there
5060 if (fe && !HasFont(fe)) {
5061 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
5062 if (font) {
5063 mFonts.AppendElement(FamilyFace(family, font));
5068 bool
5069 gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
5071 uint32_t count = mFonts.Length();
5072 for (uint32_t i = 0; i < count; ++i) {
5073 if (mFonts[i].Font()->GetFontEntry() == aFontEntry)
5074 return true;
5076 return false;
5079 gfxFontGroup::~gfxFontGroup()
5081 mFonts.Clear();
5084 gfxFont *
5085 gfxFontGroup::GetFirstMathFont()
5087 uint32_t count = mFonts.Length();
5088 for (uint32_t i = 0; i < count; ++i) {
5089 gfxFont* font = GetFontAt(i);
5090 if (font->GetFontEntry()->TryGetMathTable()) {
5091 return font;
5094 return nullptr;
5097 gfxFontGroup *
5098 gfxFontGroup::Copy(const gfxFontStyle *aStyle)
5100 gfxFontGroup *fg = new gfxFontGroup(mFamilyList, aStyle, mUserFontSet);
5101 fg->SetTextPerfMetrics(mTextPerf);
5102 return fg;
5105 bool
5106 gfxFontGroup::IsInvalidChar(uint8_t ch)
5108 return ((ch & 0x7f) < 0x20 || ch == 0x7f);
5111 bool
5112 gfxFontGroup::IsInvalidChar(char16_t ch)
5114 // All printable 7-bit ASCII values are OK
5115 if (ch >= ' ' && ch < 0x7f) {
5116 return false;
5118 // No point in sending non-printing control chars through font shaping
5119 if (ch <= 0x9f) {
5120 return true;
5122 return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
5123 (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ || ch == 0x2029/*PSEP*/)) ||
5124 IsBidiControl(ch));
5127 gfxTextRun *
5128 gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams, uint32_t aFlags)
5130 aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
5131 return gfxTextRun::Create(aParams, 0, this, aFlags);
5134 gfxTextRun *
5135 gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams, uint32_t aFlags)
5137 aFlags |= TEXT_IS_8BIT | TEXT_IS_ASCII | TEXT_IS_PERSISTENT;
5139 gfxTextRun *textRun = gfxTextRun::Create(aParams, 1, this, aFlags);
5140 if (!textRun) {
5141 return nullptr;
5144 gfxFont *font = GetFontAt(0);
5145 if (MOZ_UNLIKELY(GetStyle()->size == 0)) {
5146 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
5147 // them, and always create at least size 1 fonts, i.e. they still
5148 // render something for size 0 fonts.
5149 textRun->AddGlyphRun(font, gfxTextRange::kFontGroup, 0, false);
5151 else {
5152 if (font->GetSpaceGlyph()) {
5153 // Normally, the font has a cached space glyph, so we can avoid
5154 // the cost of calling FindFontForChar.
5155 textRun->SetSpaceGlyph(font, aParams->mContext, 0);
5156 } else {
5157 // In case the primary font doesn't have <space> (bug 970891),
5158 // find one that does.
5159 uint8_t matchType;
5160 nsRefPtr<gfxFont> spaceFont =
5161 FindFontForChar(' ', 0, MOZ_SCRIPT_LATIN, nullptr, &matchType);
5162 if (spaceFont) {
5163 textRun->SetSpaceGlyph(spaceFont, aParams->mContext, 0);
5168 // Note that the gfxGlyphExtents glyph bounds storage for the font will
5169 // always contain an entry for the font's space glyph, so we don't have
5170 // to call FetchGlyphExtents here.
5171 return textRun;
5174 gfxTextRun *
5175 gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
5176 const Parameters *aParams, uint32_t aFlags)
5178 gfxTextRun *textRun =
5179 gfxTextRun::Create(aParams, aLength, this, aFlags);
5180 if (!textRun) {
5181 return nullptr;
5184 textRun->AddGlyphRun(GetFontAt(0), gfxTextRange::kFontGroup, 0, false);
5185 return textRun;
5188 gfxTextRun *
5189 gfxFontGroup::MakeHyphenTextRun(gfxContext *aCtx, uint32_t aAppUnitsPerDevUnit)
5191 // only use U+2010 if it is supported by the first font in the group;
5192 // it's better to use ASCII '-' from the primary font than to fall back to
5193 // U+2010 from some other, possibly poorly-matching face
5194 static const char16_t hyphen = 0x2010;
5195 gfxFont *font = GetFontAt(0);
5196 if (font && font->HasCharacter(hyphen)) {
5197 return MakeTextRun(&hyphen, 1, aCtx, aAppUnitsPerDevUnit,
5198 gfxFontGroup::TEXT_IS_PERSISTENT);
5201 static const uint8_t dash = '-';
5202 return MakeTextRun(&dash, 1, aCtx, aAppUnitsPerDevUnit,
5203 gfxFontGroup::TEXT_IS_PERSISTENT);
5206 gfxFloat
5207 gfxFontGroup::GetHyphenWidth(gfxTextRun::PropertyProvider *aProvider)
5209 if (mHyphenWidth < 0) {
5210 nsRefPtr<gfxContext> ctx(aProvider->GetContext());
5211 if (ctx) {
5212 nsAutoPtr<gfxTextRun>
5213 hyphRun(MakeHyphenTextRun(ctx,
5214 aProvider->GetAppUnitsPerDevUnit()));
5215 mHyphenWidth = hyphRun.get() ?
5216 hyphRun->GetAdvanceWidth(0, hyphRun->GetLength(), nullptr) : 0;
5219 return mHyphenWidth;
5222 gfxTextRun *
5223 gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
5224 const Parameters *aParams, uint32_t aFlags)
5226 if (aLength == 0) {
5227 return MakeEmptyTextRun(aParams, aFlags);
5229 if (aLength == 1 && aString[0] == ' ') {
5230 return MakeSpaceTextRun(aParams, aFlags);
5233 aFlags |= TEXT_IS_8BIT;
5235 if (GetStyle()->size == 0) {
5236 // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
5237 // them, and always create at least size 1 fonts, i.e. they still
5238 // render something for size 0 fonts.
5239 return MakeBlankTextRun(aLength, aParams, aFlags);
5242 gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
5243 this, aFlags);
5244 if (!textRun) {
5245 return nullptr;
5248 InitTextRun(aParams->mContext, textRun, aString, aLength);
5250 textRun->FetchGlyphExtents(aParams->mContext);
5252 return textRun;
5255 gfxTextRun *
5256 gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
5257 const Parameters *aParams, uint32_t aFlags)
5259 if (aLength == 0) {
5260 return MakeEmptyTextRun(aParams, aFlags);
5262 if (aLength == 1 && aString[0] == ' ') {
5263 return MakeSpaceTextRun(aParams, aFlags);
5265 if (GetStyle()->size == 0) {
5266 return MakeBlankTextRun(aLength, aParams, aFlags);
5269 gfxTextRun *textRun = gfxTextRun::Create(aParams, aLength,
5270 this, aFlags);
5271 if (!textRun) {
5272 return nullptr;
5275 InitTextRun(aParams->mContext, textRun, aString, aLength);
5277 textRun->FetchGlyphExtents(aParams->mContext);
5279 return textRun;
5282 template<typename T>
5283 void
5284 gfxFontGroup::InitTextRun(gfxContext *aContext,
5285 gfxTextRun *aTextRun,
5286 const T *aString,
5287 uint32_t aLength)
5289 NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
5291 // we need to do numeral processing even on 8-bit text,
5292 // in case we're converting Western to Hindi/Arabic digits
5293 int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
5294 nsAutoArrayPtr<char16_t> transformedString;
5295 if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
5296 // scan the string for numerals that may need to be transformed;
5297 // if we find any, we'll make a local copy here and use that for
5298 // font matching and glyph generation/shaping
5299 bool prevIsArabic =
5300 (aTextRun->GetFlags() & gfxTextRunFactory::TEXT_INCOMING_ARABICCHAR) != 0;
5301 for (uint32_t i = 0; i < aLength; ++i) {
5302 char16_t origCh = aString[i];
5303 char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
5304 if (newCh != origCh) {
5305 if (!transformedString) {
5306 transformedString = new char16_t[aLength];
5307 if (sizeof(T) == sizeof(char16_t)) {
5308 memcpy(transformedString.get(), aString, i * sizeof(char16_t));
5309 } else {
5310 for (uint32_t j = 0; j < i; ++j) {
5311 transformedString[j] = aString[j];
5316 if (transformedString) {
5317 transformedString[i] = newCh;
5319 prevIsArabic = IS_ARABIC_CHAR(newCh);
5323 #ifdef PR_LOGGING
5324 PRLogModuleInfo *log = (mStyle.systemFont ?
5325 gfxPlatform::GetLog(eGfxLog_textrunui) :
5326 gfxPlatform::GetLog(eGfxLog_textrun));
5327 #endif
5329 // variant fallback handling may end up passing through this twice
5330 bool redo;
5331 do {
5332 redo = false;
5334 if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
5336 #ifdef PR_LOGGING
5337 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
5338 nsAutoCString lang;
5339 mStyle.language->ToUTF8String(lang);
5340 nsAutoString families;
5341 mFamilyList.ToString(families);
5342 nsAutoCString str((const char*)aString, aLength);
5343 PR_LOG(log, PR_LOG_WARNING,\
5344 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
5345 "len %d weight: %d width: %d style: %s size: %6.2f %d-byte "
5346 "TEXTRUN [%s] ENDTEXTRUN\n",
5347 (mStyle.systemFont ? "textrunui" : "textrun"),
5348 NS_ConvertUTF16toUTF8(families).get(),
5349 (mFamilyList.GetDefaultFontType() == eFamily_serif ?
5350 "serif" :
5351 (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
5352 "sans-serif" : "none")),
5353 lang.get(), MOZ_SCRIPT_LATIN, aLength,
5354 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
5355 (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
5356 (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
5357 "normal")),
5358 mStyle.size,
5359 sizeof(T),
5360 str.get()));
5362 #endif
5364 // the text is still purely 8-bit; bypass the script-run itemizer
5365 // and treat it as a single Latin run
5366 InitScriptRun(aContext, aTextRun, aString,
5367 0, aLength, MOZ_SCRIPT_LATIN);
5368 } else {
5369 const char16_t *textPtr;
5370 if (transformedString) {
5371 textPtr = transformedString.get();
5372 } else {
5373 // typecast to avoid compilation error for the 8-bit version,
5374 // even though this is dead code in that case
5375 textPtr = reinterpret_cast<const char16_t*>(aString);
5378 // split into script runs so that script can potentially influence
5379 // the font matching process below
5380 gfxScriptItemizer scriptRuns(textPtr, aLength);
5382 uint32_t runStart = 0, runLimit = aLength;
5383 int32_t runScript = MOZ_SCRIPT_LATIN;
5384 while (scriptRuns.Next(runStart, runLimit, runScript)) {
5386 #ifdef PR_LOGGING
5387 if (MOZ_UNLIKELY(PR_LOG_TEST(log, PR_LOG_WARNING))) {
5388 nsAutoCString lang;
5389 mStyle.language->ToUTF8String(lang);
5390 nsAutoString families;
5391 mFamilyList.ToString(families);
5392 uint32_t runLen = runLimit - runStart;
5393 PR_LOG(log, PR_LOG_WARNING,\
5394 ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
5395 "len %d weight: %d width: %d style: %s size: %6.2f "
5396 "%d-byte TEXTRUN [%s] ENDTEXTRUN\n",
5397 (mStyle.systemFont ? "textrunui" : "textrun"),
5398 NS_ConvertUTF16toUTF8(families).get(),
5399 (mFamilyList.GetDefaultFontType() == eFamily_serif ?
5400 "serif" :
5401 (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
5402 "sans-serif" : "none")),
5403 lang.get(), runScript, runLen,
5404 uint32_t(mStyle.weight), uint32_t(mStyle.stretch),
5405 (mStyle.style & NS_FONT_STYLE_ITALIC ? "italic" :
5406 (mStyle.style & NS_FONT_STYLE_OBLIQUE ? "oblique" :
5407 "normal")),
5408 mStyle.size,
5409 sizeof(T),
5410 NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
5412 #endif
5414 InitScriptRun(aContext, aTextRun, textPtr + runStart,
5415 runStart, runLimit - runStart, runScript);
5419 // if shaping was aborted due to lack of feature support, clear out
5420 // glyph runs and redo shaping with fallback forced on
5421 if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
5422 redo = true;
5423 aTextRun->SetShapingState(
5424 gfxTextRun::eShapingState_ForceFallbackFeature);
5425 aTextRun->ClearGlyphsAndCharacters();
5428 } while (redo);
5430 if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
5431 gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
5432 if (!glyph->IsSimpleGlyph()) {
5433 glyph->SetClusterStart(true);
5437 // It's possible for CoreText to omit glyph runs if it decides they contain
5438 // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
5439 // need to eliminate them from the glyph run array to avoid drawing "partial
5440 // ligatures" with the wrong font.
5441 // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
5442 // it will iterate back over all glyphruns in the textrun, which leads to
5443 // pathologically-bad perf in the case where a textrun contains many script
5444 // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
5445 // every time a new script subrun is processed.
5446 aTextRun->SanitizeGlyphRuns();
5448 aTextRun->SortGlyphRuns();
5451 template<typename T>
5452 void
5453 gfxFontGroup::InitScriptRun(gfxContext *aContext,
5454 gfxTextRun *aTextRun,
5455 const T *aString, // text for this script run,
5456 // not the entire textrun
5457 uint32_t aOffset, // position of the script run
5458 // within the textrun
5459 uint32_t aLength, // length of the script run
5460 int32_t aRunScript)
5462 NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
5463 NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
5464 "don't call InitScriptRun with aborted shaping state");
5466 gfxFont *mainFont = GetFontAt(0);
5468 uint32_t runStart = 0;
5469 nsAutoTArray<gfxTextRange,3> fontRanges;
5470 ComputeRanges(fontRanges, aString, aLength, aRunScript);
5471 uint32_t numRanges = fontRanges.Length();
5473 for (uint32_t r = 0; r < numRanges; r++) {
5474 const gfxTextRange& range = fontRanges[r];
5475 uint32_t matchedLength = range.Length();
5476 gfxFont *matchedFont = range.font;
5478 // create the glyph run for this range
5479 if (matchedFont && mStyle.noFallbackVariantFeatures) {
5480 // common case - just do glyph layout and record the
5481 // resulting positioned glyphs
5482 aTextRun->AddGlyphRun(matchedFont, range.matchType,
5483 aOffset + runStart, (matchedLength > 0));
5484 if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
5485 aString + runStart,
5486 aOffset + runStart,
5487 matchedLength,
5488 aRunScript)) {
5489 // glyph layout failed! treat as missing glyphs
5490 matchedFont = nullptr;
5492 } else if (matchedFont) {
5493 // shape with some variant feature that requires fallback handling
5494 bool petiteToSmallCaps = false;
5495 bool syntheticLower = false;
5496 bool syntheticUpper = false;
5498 if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
5499 (aTextRun->GetShapingState() ==
5500 gfxTextRun::eShapingState_ForceFallbackFeature ||
5501 !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
5502 aString, aLength,
5503 aRunScript)))
5505 // fallback for subscript/superscript variant glyphs
5507 // if the feature was already used, abort and force
5508 // fallback across the entire textrun
5509 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
5511 if (ss == gfxTextRun::eShapingState_Normal) {
5512 aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFallback);
5513 } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
5514 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
5515 return;
5518 nsRefPtr<gfxFont> subSuperFont =
5519 matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
5520 aTextRun->AddGlyphRun(subSuperFont, range.matchType,
5521 aOffset + runStart, (matchedLength > 0));
5522 if (!subSuperFont->SplitAndInitTextRun(aContext, aTextRun,
5523 aString + runStart,
5524 aOffset + runStart,
5525 matchedLength,
5526 aRunScript)) {
5527 // glyph layout failed! treat as missing glyphs
5528 matchedFont = nullptr;
5530 } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
5531 !matchedFont->SupportsVariantCaps(aRunScript,
5532 mStyle.variantCaps,
5533 petiteToSmallCaps,
5534 syntheticLower,
5535 syntheticUpper))
5537 // fallback for small-caps variant glyphs
5538 if (!matchedFont->InitFakeSmallCapsRun(aContext, aTextRun,
5539 aString + runStart,
5540 aOffset + runStart,
5541 matchedLength,
5542 range.matchType,
5543 aRunScript,
5544 syntheticLower,
5545 syntheticUpper)) {
5546 matchedFont = nullptr;
5548 } else {
5549 // shape normally with variant feature enabled
5550 gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
5552 // adjust the shaping state if necessary
5553 if (ss == gfxTextRun::eShapingState_Normal) {
5554 aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFeature);
5555 } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
5556 // already have shaping results using fallback, need to redo
5557 aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
5558 return;
5561 // do glyph layout and record the resulting positioned glyphs
5562 aTextRun->AddGlyphRun(matchedFont, range.matchType,
5563 aOffset + runStart, (matchedLength > 0));
5564 if (!matchedFont->SplitAndInitTextRun(aContext, aTextRun,
5565 aString + runStart,
5566 aOffset + runStart,
5567 matchedLength,
5568 aRunScript)) {
5569 // glyph layout failed! treat as missing glyphs
5570 matchedFont = nullptr;
5573 } else {
5574 aTextRun->AddGlyphRun(mainFont, gfxTextRange::kFontGroup,
5575 aOffset + runStart, (matchedLength > 0));
5578 if (!matchedFont) {
5579 // We need to set cluster boundaries (and mark spaces) so that
5580 // surrogate pairs, combining characters, etc behave properly,
5581 // even if we don't have glyphs for them
5582 aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
5583 matchedLength);
5585 // various "missing" characters may need special handling,
5586 // so we check for them here
5587 uint32_t runLimit = runStart + matchedLength;
5588 for (uint32_t index = runStart; index < runLimit; index++) {
5589 T ch = aString[index];
5591 // tab and newline are not to be displayed as hexboxes,
5592 // but do need to be recorded in the textrun
5593 if (ch == '\n') {
5594 aTextRun->SetIsNewline(aOffset + index);
5595 continue;
5597 if (ch == '\t') {
5598 aTextRun->SetIsTab(aOffset + index);
5599 continue;
5602 // for 16-bit textruns only, check for surrogate pairs and
5603 // special Unicode spaces; omit these checks in 8-bit runs
5604 if (sizeof(T) == sizeof(char16_t)) {
5605 if (NS_IS_HIGH_SURROGATE(ch) &&
5606 index + 1 < aLength &&
5607 NS_IS_LOW_SURROGATE(aString[index + 1]))
5609 aTextRun->SetMissingGlyph(aOffset + index,
5610 SURROGATE_TO_UCS4(ch,
5611 aString[index + 1]),
5612 mainFont);
5613 index++;
5614 continue;
5617 // check if this is a known Unicode whitespace character that
5618 // we can render using the space glyph with a custom width
5619 gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
5620 if (wid >= 0.0) {
5621 nscoord advance =
5622 aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
5623 if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
5624 aTextRun->GetCharacterGlyphs()[aOffset + index].
5625 SetSimpleGlyph(advance,
5626 mainFont->GetSpaceGlyph());
5627 } else {
5628 gfxTextRun::DetailedGlyph detailedGlyph;
5629 detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
5630 detailedGlyph.mAdvance = advance;
5631 detailedGlyph.mXOffset = detailedGlyph.mYOffset = 0;
5632 gfxShapedText::CompressedGlyph g;
5633 g.SetComplex(true, true, 1);
5634 aTextRun->SetGlyphs(aOffset + index,
5635 g, &detailedGlyph);
5637 continue;
5641 if (IsInvalidChar(ch)) {
5642 // invalid chars are left as zero-width/invisible
5643 continue;
5646 // record char code so we can draw a box with the Unicode value
5647 aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
5651 runStart += matchedLength;
5655 bool
5656 gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
5657 gfxTextRun *aTextRun,
5658 const uint8_t *aText,
5659 uint32_t aOffset,
5660 uint32_t aLength,
5661 uint8_t aMatchType,
5662 int32_t aScript,
5663 bool aSyntheticLower,
5664 bool aSyntheticUpper)
5666 NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
5667 aLength);
5668 return InitFakeSmallCapsRun(aContext, aTextRun, unicodeString.get(),
5669 aOffset, aLength, aMatchType, aScript,
5670 aSyntheticLower, aSyntheticUpper);
5673 bool
5674 gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
5675 gfxTextRun *aTextRun,
5676 const char16_t *aText,
5677 uint32_t aOffset,
5678 uint32_t aLength,
5679 uint8_t aMatchType,
5680 int32_t aScript,
5681 bool aSyntheticLower,
5682 bool aSyntheticUpper)
5684 bool ok = true;
5686 nsRefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
5688 enum RunCaseAction {
5689 kNoChange,
5690 kUppercaseReduce,
5691 kUppercase
5694 RunCaseAction runAction = kNoChange;
5695 uint32_t runStart = 0;
5697 for (uint32_t i = 0; i <= aLength; ++i) {
5698 uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
5699 // a trailing surrogate as well as the
5700 // current code unit.
5701 RunCaseAction chAction = kNoChange;
5702 // Unless we're at the end, figure out what treatment the current
5703 // character will need.
5704 if (i < aLength) {
5705 uint32_t ch = aText[i];
5706 if (NS_IS_HIGH_SURROGATE(ch) && i < aLength - 1 &&
5707 NS_IS_LOW_SURROGATE(aText[i + 1])) {
5708 ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
5709 extraCodeUnits = 1;
5711 // Characters that aren't the start of a cluster are ignored here.
5712 // They get added to whatever lowercase/non-lowercase run we're in.
5713 if (IsClusterExtender(ch)) {
5714 chAction = runAction;
5715 } else {
5716 if (ch != ToUpperCase(ch) || mozilla::unicode::SpecialUpper(ch)) {
5717 // ch is lower case
5718 chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
5719 } else if (ch != ToLowerCase(ch)) {
5720 // ch is upper case
5721 chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
5722 if (mStyle.language == nsGkAtoms::el) {
5723 // In Greek, check for characters that will be modified by
5724 // the GreekUpperCase mapping - this catches accented
5725 // capitals where the accent is to be removed (bug 307039).
5726 // These are handled by using the full-size font with the
5727 // uppercasing transform.
5728 mozilla::GreekCasing::State state;
5729 uint32_t ch2 = mozilla::GreekCasing::UpperCase(ch, state);
5730 if (ch != ch2 && !aSyntheticUpper) {
5731 chAction = kUppercase;
5738 // At the end of the text or when the current character needs different
5739 // casing treatment from the current run, finish the run-in-progress
5740 // and prepare to accumulate a new run.
5741 // Note that we do not look at any source data for offset [i] here,
5742 // as that would be invalid in the case where i==length.
5743 if ((i == aLength || runAction != chAction) && runStart < i) {
5744 uint32_t runLength = i - runStart;
5745 gfxFont* f = this;
5746 switch (runAction) {
5747 case kNoChange:
5748 // just use the current font and the existing string
5749 aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true);
5750 if (!f->SplitAndInitTextRun(aContext, aTextRun,
5751 aText + runStart,
5752 aOffset + runStart, runLength,
5753 aScript)) {
5754 ok = false;
5756 break;
5758 case kUppercaseReduce:
5759 // use reduced-size font, then fall through to uppercase the text
5760 f = smallCapsFont;
5762 case kUppercase:
5763 // apply uppercase transform to the string
5764 nsDependentSubstring origString(aText + runStart, runLength);
5765 nsAutoString convertedString;
5766 nsAutoTArray<bool,50> charsToMergeArray;
5767 nsAutoTArray<bool,50> deletedCharsArray;
5769 bool mergeNeeded = nsCaseTransformTextRunFactory::
5770 TransformString(origString,
5771 convertedString,
5772 true,
5773 mStyle.language,
5774 charsToMergeArray,
5775 deletedCharsArray);
5777 if (mergeNeeded) {
5778 // This is the hard case: the transformation caused chars
5779 // to be inserted or deleted, so we can't shape directly
5780 // into the destination textrun but have to handle the
5781 // mismatch of character positions.
5782 gfxTextRunFactory::Parameters params = {
5783 aContext, nullptr, nullptr, nullptr, 0,
5784 aTextRun->GetAppUnitsPerDevUnit()
5786 nsAutoPtr<gfxTextRun> tempRun;
5787 tempRun =
5788 gfxTextRun::Create(&params, convertedString.Length(),
5789 aTextRun->GetFontGroup(), 0);
5790 tempRun->AddGlyphRun(f, aMatchType, 0, true);
5791 if (!f->SplitAndInitTextRun(aContext, tempRun,
5792 convertedString.BeginReading(),
5793 0, convertedString.Length(),
5794 aScript)) {
5795 ok = false;
5796 } else {
5797 nsAutoPtr<gfxTextRun> mergedRun;
5798 mergedRun =
5799 gfxTextRun::Create(&params, runLength,
5800 aTextRun->GetFontGroup(), 0);
5801 MergeCharactersInTextRun(mergedRun, tempRun,
5802 charsToMergeArray.Elements(),
5803 deletedCharsArray.Elements());
5804 aTextRun->CopyGlyphDataFrom(mergedRun, 0, runLength,
5805 aOffset + runStart);
5807 } else {
5808 aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart,
5809 true);
5810 if (!f->SplitAndInitTextRun(aContext, aTextRun,
5811 convertedString.BeginReading(),
5812 aOffset + runStart, runLength,
5813 aScript)) {
5814 ok = false;
5817 break;
5820 runStart = i;
5823 i += extraCodeUnits;
5824 if (i < aLength) {
5825 runAction = chAction;
5829 return ok;
5832 already_AddRefed<gfxFont>
5833 gfxFont::GetSmallCapsFont()
5835 gfxFontStyle style(*GetStyle());
5836 style.size *= SMALL_CAPS_SCALE_FACTOR;
5837 style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
5838 gfxFontEntry* fe = GetFontEntry();
5839 bool needsBold = style.weight >= 600 && !fe->IsBold();
5840 return fe->FindOrMakeFont(&style, needsBold);
5843 already_AddRefed<gfxFont>
5844 gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)
5846 gfxFontStyle style(*GetStyle());
5847 style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
5848 gfxFontEntry* fe = GetFontEntry();
5849 bool needsBold = style.weight >= 600 && !fe->IsBold();
5850 return fe->FindOrMakeFont(&style, needsBold);
5853 gfxTextRun *
5854 gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
5855 LazyReferenceContextGetter& aRefContextGetter)
5857 if (mCachedEllipsisTextRun &&
5858 mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
5859 return mCachedEllipsisTextRun;
5862 // Use a Unicode ellipsis if the font supports it,
5863 // otherwise use three ASCII periods as fallback.
5864 gfxFont* firstFont = GetFontAt(0);
5865 nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
5866 ? nsDependentString(kEllipsisChar,
5867 ArrayLength(kEllipsisChar) - 1)
5868 : nsDependentString(kASCIIPeriodsChar,
5869 ArrayLength(kASCIIPeriodsChar) - 1);
5871 nsRefPtr<gfxContext> refCtx = aRefContextGetter.GetRefContext();
5872 Parameters params = {
5873 refCtx, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
5875 gfxTextRun* textRun =
5876 MakeTextRun(ellipsis.get(), ellipsis.Length(), &params, TEXT_IS_PERSISTENT);
5877 if (!textRun) {
5878 return nullptr;
5880 mCachedEllipsisTextRun = textRun;
5881 textRun->ReleaseFontGroup(); // don't let the presence of a cached ellipsis
5882 // textrun prolong the fontgroup's life
5883 return textRun;
5886 already_AddRefed<gfxFont>
5887 gfxFontGroup::TryAllFamilyMembers(gfxFontFamily* aFamily, uint32_t aCh)
5889 if (!aFamily->TestCharacterMap(aCh)) {
5890 return nullptr;
5893 // Note that we don't need the actual runScript in matchData for
5894 // gfxFontFamily::SearchAllFontsForChar, it's only used for the
5895 // system-fallback case. So we can just set it to 0 here.
5896 GlobalFontMatch matchData(aCh, 0, &mStyle);
5897 aFamily->SearchAllFontsForChar(&matchData);
5898 gfxFontEntry *fe = matchData.mBestMatch;
5899 if (!fe) {
5900 return nullptr;
5903 bool needsBold = mStyle.weight >= 600 && !fe->IsBold();
5904 nsRefPtr<gfxFont> font = fe->FindOrMakeFont(&mStyle, needsBold);
5905 return font.forget();
5908 already_AddRefed<gfxFont>
5909 gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh,
5910 int32_t aRunScript, gfxFont *aPrevMatchedFont,
5911 uint8_t *aMatchType)
5913 // To optimize common cases, try the first font in the font-group
5914 // before going into the more detailed checks below
5915 uint32_t nextIndex = 0;
5916 bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
5917 bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
5918 bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
5920 if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
5921 nsRefPtr<gfxFont> firstFont = mFonts[0].Font();
5922 if (firstFont->HasCharacter(aCh)) {
5923 *aMatchType = gfxTextRange::kFontGroup;
5924 return firstFont.forget();
5926 // It's possible that another font in the family (e.g. regular face,
5927 // where the requested style was italic) will support the character
5928 nsRefPtr<gfxFont> font = TryAllFamilyMembers(mFonts[0].Family(), aCh);
5929 if (font) {
5930 *aMatchType = gfxTextRange::kFontGroup;
5931 return font.forget();
5933 // we don't need to check the first font again below
5934 ++nextIndex;
5937 if (aPrevMatchedFont) {
5938 // Don't switch fonts for control characters, regardless of
5939 // whether they are present in the current font, as they won't
5940 // actually be rendered (see bug 716229)
5941 if (isJoinControl ||
5942 GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
5943 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
5944 return ret.forget();
5947 // if previous character was a join-causer (ZWJ),
5948 // use the same font as the previous range if we can
5949 if (wasJoinCauser) {
5950 if (aPrevMatchedFont->HasCharacter(aCh)) {
5951 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
5952 return ret.forget();
5957 // if this character is a variation selector,
5958 // use the previous font regardless of whether it supports VS or not.
5959 // otherwise the text run will be divided.
5960 if (isVarSelector) {
5961 if (aPrevMatchedFont) {
5962 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
5963 return ret.forget();
5965 // VS alone. it's meaningless to search different fonts
5966 return nullptr;
5969 // 1. check remaining fonts in the font group
5970 uint32_t fontListLength = FontListLength();
5971 for (uint32_t i = nextIndex; i < fontListLength; i++) {
5972 nsRefPtr<gfxFont> font = mFonts[i].Font();
5973 if (font->HasCharacter(aCh)) {
5974 *aMatchType = gfxTextRange::kFontGroup;
5975 return font.forget();
5978 font = TryAllFamilyMembers(mFonts[i].Family(), aCh);
5979 if (font) {
5980 *aMatchType = gfxTextRange::kFontGroup;
5981 return font.forget();
5985 // if character is in Private Use Area, don't do matching against pref or system fonts
5986 if ((aCh >= 0xE000 && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
5987 return nullptr;
5989 // 2. search pref fonts
5990 nsRefPtr<gfxFont> font = WhichPrefFontSupportsChar(aCh);
5991 if (font) {
5992 *aMatchType = gfxTextRange::kPrefsFallback;
5993 return font.forget();
5996 // 3. use fallback fonts
5997 // -- before searching for something else check the font used for the previous character
5998 if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
5999 *aMatchType = gfxTextRange::kSystemFallback;
6000 nsRefPtr<gfxFont> ret = aPrevMatchedFont;
6001 return ret.forget();
6004 // never fall back for characters from unknown scripts
6005 if (aRunScript == HB_SCRIPT_UNKNOWN) {
6006 return nullptr;
6009 // for known "space" characters, don't do a full system-fallback search;
6010 // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
6011 if (GetGeneralCategory(aCh) ==
6012 HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
6013 GetFontAt(0)->SynthesizeSpaceWidth(aCh) >= 0.0)
6015 return nullptr;
6018 // -- otherwise look for other stuff
6019 *aMatchType = gfxTextRange::kSystemFallback;
6020 font = WhichSystemFontSupportsChar(aCh, aRunScript);
6021 return font.forget();
6024 template<typename T>
6025 void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
6026 const T *aString, uint32_t aLength,
6027 int32_t aRunScript)
6029 NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
6030 NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
6032 uint32_t prevCh = 0;
6033 int32_t lastRangeIndex = -1;
6035 // initialize prevFont to the group's primary font, so that this will be
6036 // used for string-initial control chars, etc rather than risk hitting font
6037 // fallback for these (bug 716229)
6038 gfxFont *prevFont = GetFontAt(0);
6040 // if we use the initial value of prevFont, we treat this as a match from
6041 // the font group; fixes bug 978313
6042 uint8_t matchType = gfxTextRange::kFontGroup;
6044 for (uint32_t i = 0; i < aLength; i++) {
6046 const uint32_t origI = i; // save off in case we increase for surrogate
6048 // set up current ch
6049 uint32_t ch = aString[i];
6051 // in 16-bit case only, check for surrogate pair
6052 if (sizeof(T) == sizeof(char16_t)) {
6053 if ((i + 1 < aLength) && NS_IS_HIGH_SURROGATE(ch) &&
6054 NS_IS_LOW_SURROGATE(aString[i + 1])) {
6055 i++;
6056 ch = SURROGATE_TO_UCS4(ch, aString[i]);
6060 if (ch == 0xa0) {
6061 ch = ' ';
6064 // find the font for this char
6065 nsRefPtr<gfxFont> font =
6066 FindFontForChar(ch, prevCh, aRunScript, prevFont, &matchType);
6068 #ifndef RELEASE_BUILD
6069 if (MOZ_UNLIKELY(mTextPerf)) {
6070 if (matchType == gfxTextRange::kPrefsFallback) {
6071 mTextPerf->current.fallbackPrefs++;
6072 } else if (matchType == gfxTextRange::kSystemFallback) {
6073 mTextPerf->current.fallbackSystem++;
6076 #endif
6078 prevCh = ch;
6080 if (lastRangeIndex == -1) {
6081 // first char ==> make a new range
6082 aRanges.AppendElement(gfxTextRange(0, 1, font, matchType));
6083 lastRangeIndex++;
6084 prevFont = font;
6085 } else {
6086 // if font has changed, make a new range
6087 gfxTextRange& prevRange = aRanges[lastRangeIndex];
6088 if (prevRange.font != font || prevRange.matchType != matchType) {
6089 // close out the previous range
6090 prevRange.end = origI;
6091 aRanges.AppendElement(gfxTextRange(origI, i + 1,
6092 font, matchType));
6093 lastRangeIndex++;
6095 // update prevFont for the next match, *unless* we switched
6096 // fonts on a ZWJ, in which case propagating the changed font
6097 // is probably not a good idea (see bug 619511)
6098 if (sizeof(T) == sizeof(uint8_t) ||
6099 !gfxFontUtils::IsJoinCauser(ch))
6101 prevFont = font;
6107 aRanges[lastRangeIndex].end = aLength;
6110 gfxUserFontSet*
6111 gfxFontGroup::GetUserFontSet()
6113 return mUserFontSet;
6116 void
6117 gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
6119 if (aUserFontSet == mUserFontSet) {
6120 return;
6122 mUserFontSet = aUserFontSet;
6123 mCurrGeneration = GetGeneration() - 1;
6124 UpdateFontList();
6127 uint64_t
6128 gfxFontGroup::GetGeneration()
6130 if (!mUserFontSet)
6131 return 0;
6132 return mUserFontSet->GetGeneration();
6135 // note: gfxPangoFontGroup overrides UpdateFontList, such that
6136 // BuildFontList is never used
6137 void
6138 gfxFontGroup::UpdateFontList()
6140 if (mCurrGeneration != GetGeneration()) {
6141 // xxx - can probably improve this to detect when all fonts were found, so no need to update list
6142 mFonts.Clear();
6143 mUnderlineOffset = UNDERLINE_OFFSET_NOT_SET;
6144 mSkipDrawing = false;
6145 BuildFontList();
6146 mCurrGeneration = GetGeneration();
6147 mCachedEllipsisTextRun = nullptr;
6151 struct PrefFontCallbackData {
6152 explicit PrefFontCallbackData(nsTArray<nsRefPtr<gfxFontFamily> >& aFamiliesArray)
6153 : mPrefFamilies(aFamiliesArray)
6156 nsTArray<nsRefPtr<gfxFontFamily> >& mPrefFamilies;
6158 static bool AddFontFamilyEntry(eFontPrefLang aLang, const nsAString& aName, void *aClosure)
6160 PrefFontCallbackData *prefFontData = static_cast<PrefFontCallbackData*>(aClosure);
6162 gfxFontFamily *family = gfxPlatformFontList::PlatformFontList()->FindFamily(aName);
6163 if (family) {
6164 prefFontData->mPrefFamilies.AppendElement(family);
6166 return true;
6170 already_AddRefed<gfxFont>
6171 gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh)
6173 nsRefPtr<gfxFont> font;
6175 // get the pref font list if it hasn't been set up already
6176 uint32_t unicodeRange = FindCharUnicodeRange(aCh);
6177 eFontPrefLang charLang = gfxPlatform::GetPlatform()->GetFontPrefLangFor(unicodeRange);
6179 // if the last pref font was the first family in the pref list, no need to recheck through a list of families
6180 if (mLastPrefFont && charLang == mLastPrefLang &&
6181 mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
6182 font = mLastPrefFont;
6183 return font.forget();
6186 // based on char lang and page lang, set up list of pref lang fonts to check
6187 eFontPrefLang prefLangs[kMaxLenPrefLangList];
6188 uint32_t i, numLangs = 0;
6190 gfxPlatform::GetPlatform()->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
6192 for (i = 0; i < numLangs; i++) {
6193 nsAutoTArray<nsRefPtr<gfxFontFamily>, 5> families;
6194 eFontPrefLang currentLang = prefLangs[i];
6196 gfxPlatformFontList *fontList = gfxPlatformFontList::PlatformFontList();
6198 // get the pref families for a single pref lang
6199 if (!fontList->GetPrefFontFamilyEntries(currentLang, &families)) {
6200 eFontPrefLang prefLangsToSearch[1] = { currentLang };
6201 PrefFontCallbackData prefFontData(families);
6202 gfxPlatform::ForEachPrefFont(prefLangsToSearch, 1, PrefFontCallbackData::AddFontFamilyEntry,
6203 &prefFontData);
6204 fontList->SetPrefFontFamilyEntries(currentLang, families);
6207 // find the first pref font that includes the character
6208 uint32_t j, numPrefs;
6209 numPrefs = families.Length();
6210 for (j = 0; j < numPrefs; j++) {
6211 // look up the appropriate face
6212 gfxFontFamily *family = families[j];
6213 if (!family) continue;
6215 // if a pref font is used, it's likely to be used again in the same text run.
6216 // the style doesn't change so the face lookup can be cached rather than calling
6217 // FindOrMakeFont repeatedly. speeds up FindFontForChar lookup times for subsequent
6218 // pref font lookups
6219 if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
6220 font = mLastPrefFont;
6221 return font.forget();
6224 bool needsBold;
6225 gfxFontEntry *fe = family->FindFontForStyle(mStyle, needsBold);
6226 // if ch in cmap, create and return a gfxFont
6227 if (fe && fe->TestCharacterMap(aCh)) {
6228 nsRefPtr<gfxFont> prefFont = fe->FindOrMakeFont(&mStyle, needsBold);
6229 if (!prefFont) continue;
6230 mLastPrefFamily = family;
6231 mLastPrefFont = prefFont;
6232 mLastPrefLang = charLang;
6233 mLastPrefFirstFont = (i == 0 && j == 0);
6234 return prefFont.forget();
6240 return nullptr;
6243 already_AddRefed<gfxFont>
6244 gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, int32_t aRunScript)
6246 gfxFontEntry *fe =
6247 gfxPlatformFontList::PlatformFontList()->
6248 SystemFindFontForChar(aCh, aRunScript, &mStyle);
6249 if (fe) {
6250 bool wantBold = mStyle.ComputeWeight() >= 6;
6251 nsRefPtr<gfxFont> font =
6252 fe->FindOrMakeFont(&mStyle, wantBold && !fe->IsBold());
6253 return font.forget();
6256 return nullptr;
6259 /*static*/ void
6260 gfxFontGroup::Shutdown()
6262 NS_IF_RELEASE(gLangService);
6265 nsILanguageAtomService* gfxFontGroup::gLangService = nullptr;
6268 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
6270 /*static*/ uint32_t
6271 gfxFontStyle::ParseFontLanguageOverride(const nsString& aLangTag)
6273 if (!aLangTag.Length() || aLangTag.Length() > 4) {
6274 return NO_FONT_LANGUAGE_OVERRIDE;
6276 uint32_t index, result = 0;
6277 for (index = 0; index < aLangTag.Length(); ++index) {
6278 char16_t ch = aLangTag[index];
6279 if (!nsCRT::IsAscii(ch)) { // valid tags are pure ASCII
6280 return NO_FONT_LANGUAGE_OVERRIDE;
6282 result = (result << 8) + ch;
6284 while (index++ < 4) {
6285 result = (result << 8) + 0x20;
6287 return result;
6290 gfxFontStyle::gfxFontStyle() :
6291 language(nsGkAtoms::x_western),
6292 size(DEFAULT_PIXEL_FONT_SIZE), sizeAdjust(0.0f), baselineOffset(0.0f),
6293 languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
6294 weight(NS_FONT_WEIGHT_NORMAL), stretch(NS_FONT_STRETCH_NORMAL),
6295 systemFont(true), printerFont(false), useGrayscaleAntialiasing(false),
6296 style(NS_FONT_STYLE_NORMAL),
6297 allowSyntheticWeight(true), allowSyntheticStyle(true),
6298 noFallbackVariantFeatures(true),
6299 variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
6300 variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
6304 gfxFontStyle::gfxFontStyle(uint8_t aStyle, uint16_t aWeight, int16_t aStretch,
6305 gfxFloat aSize, nsIAtom *aLanguage,
6306 float aSizeAdjust, bool aSystemFont,
6307 bool aPrinterFont,
6308 bool aAllowWeightSynthesis,
6309 bool aAllowStyleSynthesis,
6310 const nsString& aLanguageOverride):
6311 language(aLanguage),
6312 size(aSize), sizeAdjust(aSizeAdjust), baselineOffset(0.0f),
6313 languageOverride(ParseFontLanguageOverride(aLanguageOverride)),
6314 weight(aWeight), stretch(aStretch),
6315 systemFont(aSystemFont), printerFont(aPrinterFont),
6316 useGrayscaleAntialiasing(false),
6317 style(aStyle),
6318 allowSyntheticWeight(aAllowWeightSynthesis),
6319 allowSyntheticStyle(aAllowStyleSynthesis),
6320 noFallbackVariantFeatures(true),
6321 variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
6322 variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL)
6324 MOZ_ASSERT(!mozilla::IsNaN(size));
6325 MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
6327 if (weight > 900)
6328 weight = 900;
6329 if (weight < 100)
6330 weight = 100;
6332 if (size >= FONT_MAX_SIZE) {
6333 size = FONT_MAX_SIZE;
6334 sizeAdjust = 0.0;
6335 } else if (size < 0.0) {
6336 NS_WARNING("negative font size");
6337 size = 0.0;
6340 if (!language) {
6341 NS_WARNING("null language");
6342 language = nsGkAtoms::x_western;
6346 gfxFontStyle::gfxFontStyle(const gfxFontStyle& aStyle) :
6347 language(aStyle.language),
6348 featureValueLookup(aStyle.featureValueLookup),
6349 size(aStyle.size), sizeAdjust(aStyle.sizeAdjust),
6350 baselineOffset(aStyle.baselineOffset),
6351 languageOverride(aStyle.languageOverride),
6352 weight(aStyle.weight), stretch(aStyle.stretch),
6353 systemFont(aStyle.systemFont), printerFont(aStyle.printerFont),
6354 useGrayscaleAntialiasing(aStyle.useGrayscaleAntialiasing),
6355 style(aStyle.style),
6356 allowSyntheticWeight(aStyle.allowSyntheticWeight),
6357 allowSyntheticStyle(aStyle.allowSyntheticStyle),
6358 noFallbackVariantFeatures(aStyle.noFallbackVariantFeatures),
6359 variantCaps(aStyle.variantCaps),
6360 variantSubSuper(aStyle.variantSubSuper)
6362 featureSettings.AppendElements(aStyle.featureSettings);
6363 alternateValues.AppendElements(aStyle.alternateValues);
6366 int8_t
6367 gfxFontStyle::ComputeWeight() const
6369 int8_t baseWeight = (weight + 50) / 100;
6371 if (baseWeight < 0)
6372 baseWeight = 0;
6373 if (baseWeight > 9)
6374 baseWeight = 9;
6376 return baseWeight;
6379 void
6380 gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)
6382 NS_PRECONDITION(variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
6383 baselineOffset == 0,
6384 "can't adjust this style for sub/superscript");
6386 // calculate the baseline offset (before changing the size)
6387 if (variantSubSuper == NS_FONT_VARIANT_POSITION_SUPER) {
6388 baselineOffset = size * -NS_FONT_SUPERSCRIPT_OFFSET_RATIO;
6389 } else {
6390 baselineOffset = size * NS_FONT_SUBSCRIPT_OFFSET_RATIO;
6393 // calculate reduced size, roughly mimicing behavior of font-size: smaller
6394 float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
6395 if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
6396 cssSize *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
6397 } else if (cssSize >= NS_FONT_SUB_SUPER_SMALL_SIZE) {
6398 cssSize *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
6399 } else {
6400 gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
6401 (NS_FONT_SUB_SUPER_LARGE_SIZE -
6402 NS_FONT_SUB_SUPER_SMALL_SIZE);
6403 size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
6404 t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
6407 // clear the variant field
6408 variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
6411 void
6412 gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
6413 const char16_t *aString,
6414 uint32_t aLength)
6416 CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
6418 gfxTextRun::CompressedGlyph extendCluster;
6419 extendCluster.SetComplex(false, true, 0);
6421 ClusterIterator iter(aString, aLength);
6423 // the ClusterIterator won't be able to tell us if the string
6424 // _begins_ with a cluster-extender, so we handle that here
6425 if (aLength && IsClusterExtender(*aString)) {
6426 *glyphs = extendCluster;
6429 while (!iter.AtEnd()) {
6430 if (*iter == char16_t(' ')) {
6431 glyphs->SetIsSpace();
6433 // advance iter to the next cluster-start (or end of text)
6434 iter.Next();
6435 // step past the first char of the cluster
6436 aString++;
6437 glyphs++;
6438 // mark all the rest as cluster-continuations
6439 while (aString < iter) {
6440 *glyphs = extendCluster;
6441 if (NS_IS_LOW_SURROGATE(*aString)) {
6442 glyphs->SetIsLowSurrogate();
6444 glyphs++;
6445 aString++;
6450 void
6451 gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
6452 const uint8_t *aString,
6453 uint32_t aLength)
6455 CompressedGlyph *glyphs = GetCharacterGlyphs() + aOffset;
6456 const uint8_t *limit = aString + aLength;
6458 while (aString < limit) {
6459 if (*aString == uint8_t(' ')) {
6460 glyphs->SetIsSpace();
6462 aString++;
6463 glyphs++;
6467 gfxShapedText::DetailedGlyph *
6468 gfxShapedText::AllocateDetailedGlyphs(uint32_t aIndex, uint32_t aCount)
6470 NS_ASSERTION(aIndex < GetLength(), "Index out of range");
6472 if (!mDetailedGlyphs) {
6473 mDetailedGlyphs = new DetailedGlyphStore();
6476 return mDetailedGlyphs->Allocate(aIndex, aCount);
6479 void
6480 gfxShapedText::SetGlyphs(uint32_t aIndex, CompressedGlyph aGlyph,
6481 const DetailedGlyph *aGlyphs)
6483 NS_ASSERTION(!aGlyph.IsSimpleGlyph(), "Simple glyphs not handled here");
6484 NS_ASSERTION(aIndex > 0 || aGlyph.IsLigatureGroupStart(),
6485 "First character can't be a ligature continuation!");
6487 uint32_t glyphCount = aGlyph.GetGlyphCount();
6488 if (glyphCount > 0) {
6489 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, glyphCount);
6490 memcpy(details, aGlyphs, sizeof(DetailedGlyph)*glyphCount);
6492 GetCharacterGlyphs()[aIndex] = aGlyph;
6495 #define ZWNJ 0x200C
6496 #define ZWJ 0x200D
6497 // U+061C ARABIC LETTER MARK is expected to be added to XIDMOD_DEFAULT_IGNORABLE
6498 // in a future Unicode update. Add it manually for now
6499 #define ALM 0x061C
6500 static inline bool
6501 IsDefaultIgnorable(uint32_t aChar)
6503 return GetIdentifierModification(aChar) == XIDMOD_DEFAULT_IGNORABLE ||
6504 aChar == ZWNJ || aChar == ZWJ || aChar == ALM;
6507 void
6508 gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar, gfxFont *aFont)
6510 uint8_t category = GetGeneralCategory(aChar);
6511 if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
6512 category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
6514 GetCharacterGlyphs()[aIndex].SetComplex(false, true, 0);
6517 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
6519 details->mGlyphID = aChar;
6520 if (IsDefaultIgnorable(aChar)) {
6521 // Setting advance width to zero will prevent drawing the hexbox
6522 details->mAdvance = 0;
6523 } else {
6524 gfxFloat width =
6525 std::max(aFont->GetMetrics().aveCharWidth,
6526 gfxFontMissingGlyphs::GetDesiredMinWidth(aChar,
6527 mAppUnitsPerDevUnit));
6528 details->mAdvance = uint32_t(width * mAppUnitsPerDevUnit);
6530 details->mXOffset = 0;
6531 details->mYOffset = 0;
6532 GetCharacterGlyphs()[aIndex].SetMissing(1);
6535 bool
6536 gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh)
6538 if (IsDefaultIgnorable(aCh)) {
6539 DetailedGlyph *details = AllocateDetailedGlyphs(aIndex, 1);
6540 details->mGlyphID = aCh;
6541 details->mAdvance = 0;
6542 details->mXOffset = 0;
6543 details->mYOffset = 0;
6544 GetCharacterGlyphs()[aIndex].SetMissing(1);
6545 return true;
6547 return false;
6550 void
6551 gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
6552 uint32_t aOffset,
6553 uint32_t aLength)
6555 uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
6556 CompressedGlyph *charGlyphs = GetCharacterGlyphs();
6557 for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
6558 CompressedGlyph *glyphData = charGlyphs + i;
6559 if (glyphData->IsSimpleGlyph()) {
6560 // simple glyphs ==> just add the advance
6561 int32_t advance = glyphData->GetSimpleAdvance() + synAppUnitOffset;
6562 if (CompressedGlyph::IsSimpleAdvance(advance)) {
6563 glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
6564 } else {
6565 // rare case, tested by making this the default
6566 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
6567 glyphData->SetComplex(true, true, 1);
6568 DetailedGlyph detail = {glyphIndex, advance, 0, 0};
6569 SetGlyphs(i, *glyphData, &detail);
6571 } else {
6572 // complex glyphs ==> add offset at cluster/ligature boundaries
6573 uint32_t detailedLength = glyphData->GetGlyphCount();
6574 if (detailedLength) {
6575 DetailedGlyph *details = GetDetailedGlyphs(i);
6576 if (!details) {
6577 continue;
6579 if (IsRightToLeft()) {
6580 details[0].mAdvance += synAppUnitOffset;
6581 } else {
6582 details[detailedLength - 1].mAdvance += synAppUnitOffset;
6589 bool
6590 gfxTextRun::GlyphRunIterator::NextRun() {
6591 if (mNextIndex >= mTextRun->mGlyphRuns.Length())
6592 return false;
6593 mGlyphRun = &mTextRun->mGlyphRuns[mNextIndex];
6594 if (mGlyphRun->mCharacterOffset >= mEndOffset)
6595 return false;
6597 mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
6598 uint32_t last = mNextIndex + 1 < mTextRun->mGlyphRuns.Length()
6599 ? mTextRun->mGlyphRuns[mNextIndex + 1].mCharacterOffset : mTextRun->GetLength();
6600 mStringEnd = std::min(mEndOffset, last);
6602 ++mNextIndex;
6603 return true;
6606 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
6607 static void
6608 AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
6610 // Ignores detailed glyphs... we don't know when those have been constructed
6611 // Also ignores gfxSkipChars dynamic storage (which won't be anything
6612 // for preformatted text)
6613 // Also ignores GlyphRun array, again because it hasn't been constructed
6614 // by the time this gets called. If there's only one glyphrun that's stored
6615 // directly in the textrun anyway so no additional overhead.
6616 uint32_t length = aTextRun->GetLength();
6617 int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
6618 bytes += sizeof(gfxTextRun);
6619 gTextRunStorage += bytes*aSign;
6620 gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
6622 #endif
6624 // Helper for textRun creation to preallocate storage for glyph records;
6625 // this function returns a pointer to the newly-allocated glyph storage.
6626 // Returns nullptr if allocation fails.
6627 void *
6628 gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
6630 // Allocate the storage we need, returning nullptr on failure rather than
6631 // throwing an exception (because web content can create huge runs).
6632 void *storage = moz_malloc(aSize + aLength * sizeof(CompressedGlyph));
6633 if (!storage) {
6634 NS_WARNING("failed to allocate storage for text run!");
6635 return nullptr;
6638 // Initialize the glyph storage (beyond aSize) to zero
6639 memset(reinterpret_cast<char*>(storage) + aSize, 0,
6640 aLength * sizeof(CompressedGlyph));
6642 return storage;
6645 gfxTextRun *
6646 gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
6647 uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
6649 void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
6650 if (!storage) {
6651 return nullptr;
6654 return new (storage) gfxTextRun(aParams, aLength, aFontGroup, aFlags);
6657 gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
6658 uint32_t aLength, gfxFontGroup *aFontGroup, uint32_t aFlags)
6659 : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
6660 , mUserData(aParams->mUserData)
6661 , mFontGroup(aFontGroup)
6662 , mReleasedFontGroup(false)
6663 , mShapingState(eShapingState_Normal)
6665 NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
6666 MOZ_COUNT_CTOR(gfxTextRun);
6667 NS_ADDREF(mFontGroup);
6669 #ifndef RELEASE_BUILD
6670 gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
6671 if (tp) {
6672 tp->current.textrunConst++;
6674 #endif
6676 mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
6678 if (aParams->mSkipChars) {
6679 mSkipChars.TakeFrom(aParams->mSkipChars);
6682 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
6683 AccountStorageForTextRun(this, 1);
6684 #endif
6686 mSkipDrawing = mFontGroup->ShouldSkipDrawing();
6689 gfxTextRun::~gfxTextRun()
6691 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
6692 AccountStorageForTextRun(this, -1);
6693 #endif
6694 #ifdef DEBUG
6695 // Make it easy to detect a dead text run
6696 mFlags = 0xFFFFFFFF;
6697 #endif
6699 // The cached ellipsis textrun (if any) in a fontgroup will have already
6700 // been told to release its reference to the group, so we mustn't do that
6701 // again here.
6702 if (!mReleasedFontGroup) {
6703 #ifndef RELEASE_BUILD
6704 gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
6705 if (tp) {
6706 tp->current.textrunDestr++;
6708 #endif
6709 NS_RELEASE(mFontGroup);
6712 MOZ_COUNT_DTOR(gfxTextRun);
6715 void
6716 gfxTextRun::ReleaseFontGroup()
6718 NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
6719 NS_RELEASE(mFontGroup);
6720 mReleasedFontGroup = true;
6723 bool
6724 gfxTextRun::SetPotentialLineBreaks(uint32_t aStart, uint32_t aLength,
6725 uint8_t *aBreakBefore,
6726 gfxContext *aRefContext)
6728 NS_ASSERTION(aStart + aLength <= GetLength(), "Overflow");
6730 uint32_t changed = 0;
6731 uint32_t i;
6732 CompressedGlyph *charGlyphs = mCharacterGlyphs + aStart;
6733 for (i = 0; i < aLength; ++i) {
6734 uint8_t canBreak = aBreakBefore[i];
6735 if (canBreak && !charGlyphs[i].IsClusterStart()) {
6736 // This can happen ... there is no guarantee that our linebreaking rules
6737 // align with the platform's idea of what constitutes a cluster.
6738 NS_WARNING("Break suggested inside cluster!");
6739 canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
6741 changed |= charGlyphs[i].SetCanBreakBefore(canBreak);
6743 return changed != 0;
6746 gfxTextRun::LigatureData
6747 gfxTextRun::ComputeLigatureData(uint32_t aPartStart, uint32_t aPartEnd,
6748 PropertyProvider *aProvider)
6750 NS_ASSERTION(aPartStart < aPartEnd, "Computing ligature data for empty range");
6751 NS_ASSERTION(aPartEnd <= GetLength(), "Character length overflow");
6753 LigatureData result;
6754 CompressedGlyph *charGlyphs = mCharacterGlyphs;
6756 uint32_t i;
6757 for (i = aPartStart; !charGlyphs[i].IsLigatureGroupStart(); --i) {
6758 NS_ASSERTION(i > 0, "Ligature at the start of the run??");
6760 result.mLigatureStart = i;
6761 for (i = aPartStart + 1; i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
6763 result.mLigatureEnd = i;
6765 int32_t ligatureWidth =
6766 GetAdvanceForGlyphs(result.mLigatureStart, result.mLigatureEnd);
6767 // Count the number of started clusters we have seen
6768 uint32_t totalClusterCount = 0;
6769 uint32_t partClusterIndex = 0;
6770 uint32_t partClusterCount = 0;
6771 for (i = result.mLigatureStart; i < result.mLigatureEnd; ++i) {
6772 // Treat the first character of the ligature as the start of a
6773 // cluster for our purposes of allocating ligature width to its
6774 // characters.
6775 if (i == result.mLigatureStart || charGlyphs[i].IsClusterStart()) {
6776 ++totalClusterCount;
6777 if (i < aPartStart) {
6778 ++partClusterIndex;
6779 } else if (i < aPartEnd) {
6780 ++partClusterCount;
6784 NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
6785 result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
6786 result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
6788 // Any rounding errors are apportioned to the final part of the ligature,
6789 // so that measuring all parts of a ligature and summing them is equal to
6790 // the ligature width.
6791 if (aPartEnd == result.mLigatureEnd) {
6792 gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
6793 result.mPartWidth += ligatureWidth - allParts;
6796 if (partClusterCount == 0) {
6797 // nothing to draw
6798 result.mClipBeforePart = result.mClipAfterPart = true;
6799 } else {
6800 // Determine whether we should clip before or after this part when
6801 // drawing its slice of the ligature.
6802 // We need to clip before the part if any cluster is drawn before
6803 // this part.
6804 result.mClipBeforePart = partClusterIndex > 0;
6805 // We need to clip after the part if any cluster is drawn after
6806 // this part.
6807 result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
6810 if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
6811 gfxFont::Spacing spacing;
6812 if (aPartStart == result.mLigatureStart) {
6813 aProvider->GetSpacing(aPartStart, 1, &spacing);
6814 result.mPartWidth += spacing.mBefore;
6816 if (aPartEnd == result.mLigatureEnd) {
6817 aProvider->GetSpacing(aPartEnd - 1, 1, &spacing);
6818 result.mPartWidth += spacing.mAfter;
6822 return result;
6825 gfxFloat
6826 gfxTextRun::ComputePartialLigatureWidth(uint32_t aPartStart, uint32_t aPartEnd,
6827 PropertyProvider *aProvider)
6829 if (aPartStart >= aPartEnd)
6830 return 0;
6831 LigatureData data = ComputeLigatureData(aPartStart, aPartEnd, aProvider);
6832 return data.mPartWidth;
6835 int32_t
6836 gfxTextRun::GetAdvanceForGlyphs(uint32_t aStart, uint32_t aEnd)
6838 const CompressedGlyph *glyphData = mCharacterGlyphs + aStart;
6839 int32_t advance = 0;
6840 uint32_t i;
6841 for (i = aStart; i < aEnd; ++i, ++glyphData) {
6842 if (glyphData->IsSimpleGlyph()) {
6843 advance += glyphData->GetSimpleAdvance();
6844 } else {
6845 uint32_t glyphCount = glyphData->GetGlyphCount();
6846 if (glyphCount == 0) {
6847 continue;
6849 const DetailedGlyph *details = GetDetailedGlyphs(i);
6850 if (details) {
6851 uint32_t j;
6852 for (j = 0; j < glyphCount; ++j, ++details) {
6853 advance += details->mAdvance;
6858 return advance;
6861 static void
6862 GetAdjustedSpacing(gfxTextRun *aTextRun, uint32_t aStart, uint32_t aEnd,
6863 gfxTextRun::PropertyProvider *aProvider,
6864 gfxTextRun::PropertyProvider::Spacing *aSpacing)
6866 if (aStart >= aEnd)
6867 return;
6869 aProvider->GetSpacing(aStart, aEnd - aStart, aSpacing);
6871 #ifdef DEBUG
6872 // Check to see if we have spacing inside ligatures
6874 const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
6875 uint32_t i;
6877 for (i = aStart; i < aEnd; ++i) {
6878 if (!charGlyphs[i].IsLigatureGroupStart()) {
6879 NS_ASSERTION(i == aStart || aSpacing[i - aStart].mBefore == 0,
6880 "Before-spacing inside a ligature!");
6881 NS_ASSERTION(i - 1 <= aStart || aSpacing[i - 1 - aStart].mAfter == 0,
6882 "After-spacing inside a ligature!");
6885 #endif
6888 bool
6889 gfxTextRun::GetAdjustedSpacingArray(uint32_t aStart, uint32_t aEnd,
6890 PropertyProvider *aProvider,
6891 uint32_t aSpacingStart, uint32_t aSpacingEnd,
6892 nsTArray<PropertyProvider::Spacing> *aSpacing)
6894 if (!aProvider || !(mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING))
6895 return false;
6896 if (!aSpacing->AppendElements(aEnd - aStart))
6897 return false;
6898 memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing)*(aSpacingStart - aStart));
6899 GetAdjustedSpacing(this, aSpacingStart, aSpacingEnd, aProvider,
6900 aSpacing->Elements() + aSpacingStart - aStart);
6901 memset(aSpacing->Elements() + aSpacingEnd - aStart, 0, sizeof(gfxFont::Spacing)*(aEnd - aSpacingEnd));
6902 return true;
6905 void
6906 gfxTextRun::ShrinkToLigatureBoundaries(uint32_t *aStart, uint32_t *aEnd)
6908 if (*aStart >= *aEnd)
6909 return;
6911 CompressedGlyph *charGlyphs = mCharacterGlyphs;
6913 while (*aStart < *aEnd && !charGlyphs[*aStart].IsLigatureGroupStart()) {
6914 ++(*aStart);
6916 if (*aEnd < GetLength()) {
6917 while (*aEnd > *aStart && !charGlyphs[*aEnd].IsLigatureGroupStart()) {
6918 --(*aEnd);
6923 void
6924 gfxTextRun::DrawGlyphs(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
6925 gfxPoint *aPt, PropertyProvider *aProvider,
6926 uint32_t aSpacingStart, uint32_t aSpacingEnd,
6927 TextRunDrawParams& aParams)
6929 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
6930 bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
6931 aSpacingStart, aSpacingEnd, &spacingBuffer);
6932 aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
6933 aFont->Draw(this, aStart, aEnd, aPt, aParams);
6936 static void
6937 ClipPartialLigature(gfxTextRun *aTextRun, gfxFloat *aLeft, gfxFloat *aRight,
6938 gfxFloat aXOrigin, gfxTextRun::LigatureData *aLigature)
6940 if (aLigature->mClipBeforePart) {
6941 if (aTextRun->IsRightToLeft()) {
6942 *aRight = std::min(*aRight, aXOrigin);
6943 } else {
6944 *aLeft = std::max(*aLeft, aXOrigin);
6947 if (aLigature->mClipAfterPart) {
6948 gfxFloat endEdge = aXOrigin + aTextRun->GetDirection()*aLigature->mPartWidth;
6949 if (aTextRun->IsRightToLeft()) {
6950 *aLeft = std::max(*aLeft, endEdge);
6951 } else {
6952 *aRight = std::min(*aRight, endEdge);
6957 void
6958 gfxTextRun::DrawPartialLigature(gfxFont *aFont, uint32_t aStart, uint32_t aEnd,
6959 gfxPoint *aPt, PropertyProvider *aProvider,
6960 TextRunDrawParams& aParams)
6962 if (aStart >= aEnd)
6963 return;
6965 // Draw partial ligature. We hack this by clipping the ligature.
6966 LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
6967 gfxRect clipExtents = aParams.context->GetClipExtents();
6968 gfxFloat left = clipExtents.X() * mAppUnitsPerDevUnit;
6969 gfxFloat right = clipExtents.XMost() * mAppUnitsPerDevUnit;
6970 ClipPartialLigature(this, &left, &right, aPt->x, &data);
6973 // Need to preserve the path, otherwise this can break canvas text-on-path;
6974 // in general it seems like a good thing, as naive callers probably won't
6975 // expect gfxTextRun::Draw to implicitly destroy the current path.
6976 gfxContextPathAutoSaveRestore savePath(aParams.context);
6978 // use division here to ensure that when the rect is aligned on multiples
6979 // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
6980 // Also, make sure we snap the rectangle to device pixels.
6981 aParams.context->Save();
6982 aParams.context->NewPath();
6983 aParams.context->Rectangle(gfxRect(left / mAppUnitsPerDevUnit,
6984 clipExtents.Y(),
6985 (right - left) / mAppUnitsPerDevUnit,
6986 clipExtents.Height()), true);
6987 aParams.context->Clip();
6990 gfxPoint pt(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
6991 DrawGlyphs(aFont, data.mLigatureStart, data.mLigatureEnd, &pt,
6992 aProvider, aStart, aEnd, aParams);
6993 aParams.context->Restore();
6995 aPt->x += aParams.direction * data.mPartWidth;
6998 // returns true if a glyph run is using a font with synthetic bolding enabled, false otherwise
6999 static bool
7000 HasSyntheticBold(gfxTextRun *aRun, uint32_t aStart, uint32_t aLength)
7002 gfxTextRun::GlyphRunIterator iter(aRun, aStart, aLength);
7003 while (iter.NextRun()) {
7004 gfxFont *font = iter.GetGlyphRun()->mFont;
7005 if (font && font->IsSyntheticBold()) {
7006 return true;
7010 return false;
7013 // returns true if color is non-opaque (i.e. alpha != 1.0) or completely transparent, false otherwise
7014 // if true, color is set on output
7015 static bool
7016 HasNonOpaqueColor(gfxContext *aContext, gfxRGBA& aCurrentColor)
7018 if (aContext->GetDeviceColor(aCurrentColor)) {
7019 if (aCurrentColor.a < 1.0 && aCurrentColor.a > 0.0) {
7020 return true;
7024 return false;
7027 // helper class for double-buffering drawing with non-opaque color
7028 struct BufferAlphaColor {
7029 explicit BufferAlphaColor(gfxContext *aContext)
7030 : mContext(aContext)
7035 ~BufferAlphaColor() {}
7037 void PushSolidColor(const gfxRect& aBounds, const gfxRGBA& aAlphaColor, uint32_t appsPerDevUnit)
7039 mContext->Save();
7040 mContext->NewPath();
7041 mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
7042 aBounds.Y() / appsPerDevUnit,
7043 aBounds.Width() / appsPerDevUnit,
7044 aBounds.Height() / appsPerDevUnit), true);
7045 mContext->Clip();
7046 mContext->SetColor(gfxRGBA(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
7047 mContext->PushGroup(gfxContentType::COLOR_ALPHA);
7048 mAlpha = aAlphaColor.a;
7051 void PopAlpha()
7053 // pop the text, using the color alpha as the opacity
7054 mContext->PopGroupToSource();
7055 mContext->SetOperator(gfxContext::OPERATOR_OVER);
7056 mContext->Paint(mAlpha);
7057 mContext->Restore();
7060 gfxContext *mContext;
7061 gfxFloat mAlpha;
7064 void
7065 gfxTextRun::Draw(gfxContext *aContext, gfxPoint aPt, DrawMode aDrawMode,
7066 uint32_t aStart, uint32_t aLength,
7067 PropertyProvider *aProvider, gfxFloat *aAdvanceWidth,
7068 gfxTextContextPaint *aContextPaint,
7069 gfxTextRunDrawCallbacks *aCallbacks)
7071 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
7072 NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH ||
7073 !(int(aDrawMode) & int(DrawMode::GLYPH_PATH)),
7074 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
7075 NS_ASSERTION(aDrawMode == DrawMode::GLYPH_PATH || !aCallbacks,
7076 "callback must not be specified unless using GLYPH_PATH");
7078 bool skipDrawing = mSkipDrawing;
7079 if (aDrawMode == DrawMode::GLYPH_FILL) {
7080 gfxRGBA currentColor;
7081 if (aContext->GetDeviceColor(currentColor) && currentColor.a == 0) {
7082 skipDrawing = true;
7086 gfxFloat direction = GetDirection();
7088 if (skipDrawing) {
7089 // We don't need to draw anything;
7090 // but if the caller wants advance width, we need to compute it here
7091 if (aAdvanceWidth) {
7092 gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
7093 gfxFont::LOOSE_INK_EXTENTS,
7094 aContext, aProvider);
7095 *aAdvanceWidth = metrics.mAdvanceWidth * direction;
7098 // return without drawing
7099 return;
7102 // Set up parameters that will be constant across all glyph runs we need
7103 // to draw, regardless of the font used.
7104 TextRunDrawParams params;
7105 params.context = aContext;
7106 params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
7107 params.isRTL = IsRightToLeft();
7108 params.direction = direction;
7109 params.drawMode = aDrawMode;
7110 params.callbacks = aCallbacks;
7111 params.runContextPaint = aContextPaint;
7112 params.paintSVGGlyphs = !aCallbacks || aCallbacks->mShouldPaintSVGGlyphs;
7113 params.dt = aContext->GetDrawTarget();
7115 gfxPoint pt = aPt;
7117 // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
7118 // correctly unless first drawn without alpha
7119 BufferAlphaColor syntheticBoldBuffer(aContext);
7120 gfxRGBA currentColor;
7121 bool needToRestore = false;
7123 if (aDrawMode == DrawMode::GLYPH_FILL &&
7124 HasNonOpaqueColor(aContext, currentColor) &&
7125 HasSyntheticBold(this, aStart, aLength)) {
7126 needToRestore = true;
7127 // measure text, use the bounding box
7128 gfxTextRun::Metrics metrics = MeasureText(aStart, aLength,
7129 gfxFont::LOOSE_INK_EXTENTS,
7130 aContext, aProvider);
7131 metrics.mBoundingBox.MoveBy(aPt);
7132 syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
7133 GetAppUnitsPerDevUnit());
7136 GlyphRunIterator iter(this, aStart, aLength);
7137 while (iter.NextRun()) {
7138 gfxFont *font = iter.GetGlyphRun()->mFont;
7139 uint32_t start = iter.GetStringStart();
7140 uint32_t end = iter.GetStringEnd();
7141 uint32_t ligatureRunStart = start;
7142 uint32_t ligatureRunEnd = end;
7143 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
7145 bool drawPartial = aDrawMode == DrawMode::GLYPH_FILL ||
7146 (aDrawMode == DrawMode::GLYPH_PATH && aCallbacks);
7148 if (drawPartial) {
7149 DrawPartialLigature(font, start, ligatureRunStart, &pt,
7150 aProvider, params);
7153 DrawGlyphs(font, ligatureRunStart, ligatureRunEnd, &pt,
7154 aProvider, ligatureRunStart, ligatureRunEnd, params);
7156 if (drawPartial) {
7157 DrawPartialLigature(font, ligatureRunEnd, end, &pt,
7158 aProvider, params);
7162 // composite result when synthetic bolding used
7163 if (needToRestore) {
7164 syntheticBoldBuffer.PopAlpha();
7167 if (aAdvanceWidth) {
7168 *aAdvanceWidth = (pt.x - aPt.x)*direction;
7172 void
7173 gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont,
7174 uint32_t aStart, uint32_t aEnd,
7175 gfxFont::BoundingBoxType aBoundingBoxType,
7176 gfxContext *aRefContext,
7177 PropertyProvider *aProvider,
7178 uint32_t aSpacingStart, uint32_t aSpacingEnd,
7179 Metrics *aMetrics)
7181 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
7182 bool haveSpacing = GetAdjustedSpacingArray(aStart, aEnd, aProvider,
7183 aSpacingStart, aSpacingEnd, &spacingBuffer);
7184 Metrics metrics = aFont->Measure(this, aStart, aEnd, aBoundingBoxType, aRefContext,
7185 haveSpacing ? spacingBuffer.Elements() : nullptr);
7186 aMetrics->CombineWith(metrics, IsRightToLeft());
7189 void
7190 gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont,
7191 uint32_t aStart, uint32_t aEnd,
7192 gfxFont::BoundingBoxType aBoundingBoxType, gfxContext *aRefContext,
7193 PropertyProvider *aProvider, Metrics *aMetrics)
7195 if (aStart >= aEnd)
7196 return;
7198 // Measure partial ligature. We hack this by clipping the metrics in the
7199 // same way we clip the drawing.
7200 LigatureData data = ComputeLigatureData(aStart, aEnd, aProvider);
7202 // First measure the complete ligature
7203 Metrics metrics;
7204 AccumulateMetricsForRun(aFont, data.mLigatureStart, data.mLigatureEnd,
7205 aBoundingBoxType, aRefContext,
7206 aProvider, aStart, aEnd, &metrics);
7208 // Clip the bounding box to the ligature part
7209 gfxFloat bboxLeft = metrics.mBoundingBox.X();
7210 gfxFloat bboxRight = metrics.mBoundingBox.XMost();
7211 // Where we are going to start "drawing" relative to our left baseline origin
7212 gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
7213 ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
7214 metrics.mBoundingBox.x = bboxLeft;
7215 metrics.mBoundingBox.width = bboxRight - bboxLeft;
7217 // mBoundingBox is now relative to the left baseline origin for the entire
7218 // ligature. Shift it left.
7219 metrics.mBoundingBox.x -=
7220 IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth)
7221 : data.mPartAdvance;
7222 metrics.mAdvanceWidth = data.mPartWidth;
7224 aMetrics->CombineWith(metrics, IsRightToLeft());
7227 gfxTextRun::Metrics
7228 gfxTextRun::MeasureText(uint32_t aStart, uint32_t aLength,
7229 gfxFont::BoundingBoxType aBoundingBoxType,
7230 gfxContext *aRefContext,
7231 PropertyProvider *aProvider)
7233 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
7235 Metrics accumulatedMetrics;
7236 GlyphRunIterator iter(this, aStart, aLength);
7237 while (iter.NextRun()) {
7238 gfxFont *font = iter.GetGlyphRun()->mFont;
7239 uint32_t start = iter.GetStringStart();
7240 uint32_t end = iter.GetStringEnd();
7241 uint32_t ligatureRunStart = start;
7242 uint32_t ligatureRunEnd = end;
7243 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
7245 AccumulatePartialLigatureMetrics(font, start, ligatureRunStart,
7246 aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
7248 // XXX This sucks. We have to get glyph extents just so we can detect
7249 // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
7250 // even though in almost all cases we could get correct results just
7251 // by getting some ascent/descent from the font and using our stored
7252 // advance widths.
7253 AccumulateMetricsForRun(font,
7254 ligatureRunStart, ligatureRunEnd, aBoundingBoxType,
7255 aRefContext, aProvider, ligatureRunStart, ligatureRunEnd,
7256 &accumulatedMetrics);
7258 AccumulatePartialLigatureMetrics(font, ligatureRunEnd, end,
7259 aBoundingBoxType, aRefContext, aProvider, &accumulatedMetrics);
7262 return accumulatedMetrics;
7265 #define MEASUREMENT_BUFFER_SIZE 100
7267 uint32_t
7268 gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
7269 bool aLineBreakBefore, gfxFloat aWidth,
7270 PropertyProvider *aProvider,
7271 bool aSuppressInitialBreak,
7272 gfxFloat *aTrimWhitespace,
7273 Metrics *aMetrics,
7274 gfxFont::BoundingBoxType aBoundingBoxType,
7275 gfxContext *aRefContext,
7276 bool *aUsedHyphenation,
7277 uint32_t *aLastBreak,
7278 bool aCanWordWrap,
7279 gfxBreakPriority *aBreakPriority)
7281 aMaxLength = std::min(aMaxLength, GetLength() - aStart);
7283 NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
7285 uint32_t bufferStart = aStart;
7286 uint32_t bufferLength = std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE);
7287 PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
7288 bool haveSpacing = aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING) != 0;
7289 if (haveSpacing) {
7290 GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
7291 spacingBuffer);
7293 bool hyphenBuffer[MEASUREMENT_BUFFER_SIZE];
7294 bool haveHyphenation = aProvider &&
7295 (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_AUTO ||
7296 (aProvider->GetHyphensOption() == NS_STYLE_HYPHENS_MANUAL &&
7297 (mFlags & gfxTextRunFactory::TEXT_ENABLE_HYPHEN_BREAKS) != 0));
7298 if (haveHyphenation) {
7299 aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
7300 hyphenBuffer);
7303 gfxFloat width = 0;
7304 gfxFloat advance = 0;
7305 // The number of space characters that can be trimmed
7306 uint32_t trimmableChars = 0;
7307 // The amount of space removed by ignoring trimmableChars
7308 gfxFloat trimmableAdvance = 0;
7309 int32_t lastBreak = -1;
7310 int32_t lastBreakTrimmableChars = -1;
7311 gfxFloat lastBreakTrimmableAdvance = -1;
7312 bool aborted = false;
7313 uint32_t end = aStart + aMaxLength;
7314 bool lastBreakUsedHyphenation = false;
7316 uint32_t ligatureRunStart = aStart;
7317 uint32_t ligatureRunEnd = end;
7318 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
7320 uint32_t i;
7321 for (i = aStart; i < end; ++i) {
7322 if (i >= bufferStart + bufferLength) {
7323 // Fetch more spacing and hyphenation data
7324 bufferStart = i;
7325 bufferLength = std::min(aStart + aMaxLength, i + MEASUREMENT_BUFFER_SIZE) - i;
7326 if (haveSpacing) {
7327 GetAdjustedSpacing(this, bufferStart, bufferStart + bufferLength, aProvider,
7328 spacingBuffer);
7330 if (haveHyphenation) {
7331 aProvider->GetHyphenationBreaks(bufferStart, bufferLength,
7332 hyphenBuffer);
7336 // There can't be a word-wrap break opportunity at the beginning of the
7337 // line: if the width is too small for even one character to fit, it
7338 // could be the first and last break opportunity on the line, and that
7339 // would trigger an infinite loop.
7340 if (!aSuppressInitialBreak || i > aStart) {
7341 bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
7342 bool atHyphenationBreak =
7343 !atNaturalBreak && haveHyphenation && hyphenBuffer[i - bufferStart];
7344 bool atBreak = atNaturalBreak || atHyphenationBreak;
7345 bool wordWrapping =
7346 aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
7347 *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
7349 if (atBreak || wordWrapping) {
7350 gfxFloat hyphenatedAdvance = advance;
7351 if (atHyphenationBreak) {
7352 hyphenatedAdvance += aProvider->GetHyphenWidth();
7355 if (lastBreak < 0 || width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
7356 // We can break here.
7357 lastBreak = i;
7358 lastBreakTrimmableChars = trimmableChars;
7359 lastBreakTrimmableAdvance = trimmableAdvance;
7360 lastBreakUsedHyphenation = atHyphenationBreak;
7361 *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
7362 : gfxBreakPriority::eWordWrapBreak;
7365 width += advance;
7366 advance = 0;
7367 if (width - trimmableAdvance > aWidth) {
7368 // No more text fits. Abort
7369 aborted = true;
7370 break;
7375 gfxFloat charAdvance;
7376 if (i >= ligatureRunStart && i < ligatureRunEnd) {
7377 charAdvance = GetAdvanceForGlyphs(i, i + 1);
7378 if (haveSpacing) {
7379 PropertyProvider::Spacing *space = &spacingBuffer[i - bufferStart];
7380 charAdvance += space->mBefore + space->mAfter;
7382 } else {
7383 charAdvance = ComputePartialLigatureWidth(i, i + 1, aProvider);
7386 advance += charAdvance;
7387 if (aTrimWhitespace) {
7388 if (mCharacterGlyphs[i].CharIsSpace()) {
7389 ++trimmableChars;
7390 trimmableAdvance += charAdvance;
7391 } else {
7392 trimmableAdvance = 0;
7393 trimmableChars = 0;
7398 if (!aborted) {
7399 width += advance;
7402 // There are three possibilities:
7403 // 1) all the text fit (width <= aWidth)
7404 // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
7405 // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
7406 uint32_t charsFit;
7407 bool usedHyphenation = false;
7408 if (width - trimmableAdvance <= aWidth) {
7409 charsFit = aMaxLength;
7410 } else if (lastBreak >= 0) {
7411 charsFit = lastBreak - aStart;
7412 trimmableChars = lastBreakTrimmableChars;
7413 trimmableAdvance = lastBreakTrimmableAdvance;
7414 usedHyphenation = lastBreakUsedHyphenation;
7415 } else {
7416 charsFit = aMaxLength;
7419 if (aMetrics) {
7420 *aMetrics = MeasureText(aStart, charsFit - trimmableChars,
7421 aBoundingBoxType, aRefContext, aProvider);
7423 if (aTrimWhitespace) {
7424 *aTrimWhitespace = trimmableAdvance;
7426 if (aUsedHyphenation) {
7427 *aUsedHyphenation = usedHyphenation;
7429 if (aLastBreak && charsFit == aMaxLength) {
7430 if (lastBreak < 0) {
7431 *aLastBreak = UINT32_MAX;
7432 } else {
7433 *aLastBreak = lastBreak - aStart;
7437 return charsFit;
7440 gfxFloat
7441 gfxTextRun::GetAdvanceWidth(uint32_t aStart, uint32_t aLength,
7442 PropertyProvider *aProvider)
7444 NS_ASSERTION(aStart + aLength <= GetLength(), "Substring out of range");
7446 uint32_t ligatureRunStart = aStart;
7447 uint32_t ligatureRunEnd = aStart + aLength;
7448 ShrinkToLigatureBoundaries(&ligatureRunStart, &ligatureRunEnd);
7450 gfxFloat result = ComputePartialLigatureWidth(aStart, ligatureRunStart, aProvider) +
7451 ComputePartialLigatureWidth(ligatureRunEnd, aStart + aLength, aProvider);
7453 // Account for all remaining spacing here. This is more efficient than
7454 // processing it along with the glyphs.
7455 if (aProvider && (mFlags & gfxTextRunFactory::TEXT_ENABLE_SPACING)) {
7456 uint32_t i;
7457 nsAutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
7458 if (spacingBuffer.AppendElements(aLength)) {
7459 GetAdjustedSpacing(this, ligatureRunStart, ligatureRunEnd, aProvider,
7460 spacingBuffer.Elements());
7461 for (i = 0; i < ligatureRunEnd - ligatureRunStart; ++i) {
7462 PropertyProvider::Spacing *space = &spacingBuffer[i];
7463 result += space->mBefore + space->mAfter;
7468 return result + GetAdvanceForGlyphs(ligatureRunStart, ligatureRunEnd);
7471 bool
7472 gfxTextRun::SetLineBreaks(uint32_t aStart, uint32_t aLength,
7473 bool aLineBreakBefore, bool aLineBreakAfter,
7474 gfxFloat *aAdvanceWidthDelta,
7475 gfxContext *aRefContext)
7477 // Do nothing because our shaping does not currently take linebreaks into
7478 // account. There is no change in advance width.
7479 if (aAdvanceWidthDelta) {
7480 *aAdvanceWidthDelta = 0;
7482 return false;
7485 uint32_t
7486 gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset)
7488 NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
7489 NS_ASSERTION(GetLength() == 0 || mGlyphRuns.Length() > 0,
7490 "non-empty text but no glyph runs present!");
7491 if (aOffset == GetLength())
7492 return mGlyphRuns.Length();
7493 uint32_t start = 0;
7494 uint32_t end = mGlyphRuns.Length();
7495 while (end - start > 1) {
7496 uint32_t mid = (start + end)/2;
7497 if (mGlyphRuns[mid].mCharacterOffset <= aOffset) {
7498 start = mid;
7499 } else {
7500 end = mid;
7503 NS_ASSERTION(mGlyphRuns[start].mCharacterOffset <= aOffset,
7504 "Hmm, something went wrong, aOffset should have been found");
7505 return start;
7508 nsresult
7509 gfxTextRun::AddGlyphRun(gfxFont *aFont, uint8_t aMatchType,
7510 uint32_t aUTF16Offset, bool aForceNewRun)
7512 NS_ASSERTION(aFont, "adding glyph run for null font!");
7513 if (!aFont) {
7514 return NS_OK;
7516 uint32_t numGlyphRuns = mGlyphRuns.Length();
7517 if (!aForceNewRun && numGlyphRuns > 0) {
7518 GlyphRun *lastGlyphRun = &mGlyphRuns[numGlyphRuns - 1];
7520 NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
7521 "Glyph runs out of order (and run not forced)");
7523 // Don't append a run if the font is already the one we want
7524 if (lastGlyphRun->mFont == aFont &&
7525 lastGlyphRun->mMatchType == aMatchType)
7527 return NS_OK;
7530 // If the offset has not changed, avoid leaving a zero-length run
7531 // by overwriting the last entry instead of appending...
7532 if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
7534 // ...except that if the run before the last entry had the same
7535 // font as the new one wants, merge with it instead of creating
7536 // adjacent runs with the same font
7537 if (numGlyphRuns > 1 &&
7538 mGlyphRuns[numGlyphRuns - 2].mFont == aFont &&
7539 mGlyphRuns[numGlyphRuns - 2].mMatchType == aMatchType)
7541 mGlyphRuns.TruncateLength(numGlyphRuns - 1);
7542 return NS_OK;
7545 lastGlyphRun->mFont = aFont;
7546 lastGlyphRun->mMatchType = aMatchType;
7547 return NS_OK;
7551 NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
7552 "First run doesn't cover the first character (and run not forced)?");
7554 GlyphRun *glyphRun = mGlyphRuns.AppendElement();
7555 if (!glyphRun)
7556 return NS_ERROR_OUT_OF_MEMORY;
7557 glyphRun->mFont = aFont;
7558 glyphRun->mCharacterOffset = aUTF16Offset;
7559 glyphRun->mMatchType = aMatchType;
7560 return NS_OK;
7563 void
7564 gfxTextRun::SortGlyphRuns()
7566 if (mGlyphRuns.Length() <= 1)
7567 return;
7569 nsTArray<GlyphRun> runs(mGlyphRuns);
7570 GlyphRunOffsetComparator comp;
7571 runs.Sort(comp);
7573 // Now copy back, coalescing adjacent glyph runs that have the same font
7574 mGlyphRuns.Clear();
7575 uint32_t i, count = runs.Length();
7576 for (i = 0; i < count; ++i) {
7577 // a GlyphRun with the same font as the previous GlyphRun can just
7578 // be skipped; the last GlyphRun will cover its character range.
7579 if (i == 0 || runs[i].mFont != runs[i - 1].mFont) {
7580 mGlyphRuns.AppendElement(runs[i]);
7581 // If two fonts have the same character offset, Sort() will have
7582 // randomized the order.
7583 NS_ASSERTION(i == 0 ||
7584 runs[i].mCharacterOffset !=
7585 runs[i - 1].mCharacterOffset,
7586 "Two fonts for the same run, glyph indices may not match the font");
7591 // Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
7592 // therefore we only call it once, at the end of textrun construction,
7593 // NOT incrementally as each glyph run is added (bug 680402).
7594 void
7595 gfxTextRun::SanitizeGlyphRuns()
7597 if (mGlyphRuns.Length() <= 1)
7598 return;
7600 // If any glyph run starts with ligature-continuation characters, we need to advance it
7601 // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
7602 // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
7603 // it appear as if a ligature has been formed)
7604 int32_t i, lastRunIndex = mGlyphRuns.Length() - 1;
7605 const CompressedGlyph *charGlyphs = mCharacterGlyphs;
7606 for (i = lastRunIndex; i >= 0; --i) {
7607 GlyphRun& run = mGlyphRuns[i];
7608 while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
7609 run.mCharacterOffset < GetLength()) {
7610 run.mCharacterOffset++;
7612 // if the run has become empty, eliminate it
7613 if ((i < lastRunIndex &&
7614 run.mCharacterOffset >= mGlyphRuns[i+1].mCharacterOffset) ||
7615 (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
7616 mGlyphRuns.RemoveElementAt(i);
7617 --lastRunIndex;
7622 uint32_t
7623 gfxTextRun::CountMissingGlyphs()
7625 uint32_t i;
7626 uint32_t count = 0;
7627 for (i = 0; i < GetLength(); ++i) {
7628 if (mCharacterGlyphs[i].IsMissing()) {
7629 ++count;
7632 return count;
7635 void
7636 gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
7638 uint32_t wordLen = aShapedWord->GetLength();
7639 NS_ASSERTION(aOffset + wordLen <= GetLength(),
7640 "word overruns end of textrun!");
7642 CompressedGlyph *charGlyphs = GetCharacterGlyphs();
7643 const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
7644 if (aShapedWord->HasDetailedGlyphs()) {
7645 for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
7646 const CompressedGlyph& g = wordGlyphs[i];
7647 if (g.IsSimpleGlyph()) {
7648 charGlyphs[aOffset] = g;
7649 } else {
7650 const DetailedGlyph *details =
7651 g.GetGlyphCount() > 0 ?
7652 aShapedWord->GetDetailedGlyphs(i) : nullptr;
7653 SetGlyphs(aOffset, g, details);
7656 } else {
7657 memcpy(charGlyphs + aOffset, wordGlyphs,
7658 wordLen * sizeof(CompressedGlyph));
7662 void
7663 gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, uint32_t aStart,
7664 uint32_t aLength, uint32_t aDest)
7666 NS_ASSERTION(aStart + aLength <= aSource->GetLength(),
7667 "Source substring out of range");
7668 NS_ASSERTION(aDest + aLength <= GetLength(),
7669 "Destination substring out of range");
7671 if (aSource->mSkipDrawing) {
7672 mSkipDrawing = true;
7675 // Copy base glyph data, and DetailedGlyph data where present
7676 const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aStart;
7677 CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
7678 for (uint32_t i = 0; i < aLength; ++i) {
7679 CompressedGlyph g = srcGlyphs[i];
7680 g.SetCanBreakBefore(!g.IsClusterStart() ?
7681 CompressedGlyph::FLAG_BREAK_TYPE_NONE :
7682 dstGlyphs[i].CanBreakBefore());
7683 if (!g.IsSimpleGlyph()) {
7684 uint32_t count = g.GetGlyphCount();
7685 if (count > 0) {
7686 DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
7687 if (dst) {
7688 DetailedGlyph *src = aSource->GetDetailedGlyphs(i + aStart);
7689 if (src) {
7690 ::memcpy(dst, src, count * sizeof(DetailedGlyph));
7691 } else {
7692 g.SetMissing(0);
7694 } else {
7695 g.SetMissing(0);
7699 dstGlyphs[i] = g;
7702 // Copy glyph runs
7703 GlyphRunIterator iter(aSource, aStart, aLength);
7704 #ifdef DEBUG
7705 gfxFont *lastFont = nullptr;
7706 #endif
7707 while (iter.NextRun()) {
7708 gfxFont *font = iter.GetGlyphRun()->mFont;
7709 NS_ASSERTION(font != lastFont, "Glyphruns not coalesced?");
7710 #ifdef DEBUG
7711 lastFont = font;
7712 uint32_t end = iter.GetStringEnd();
7713 #endif
7714 uint32_t start = iter.GetStringStart();
7716 // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
7717 // Although it's unusual (and not desirable), it's possible for us to assign
7718 // different fonts to a base character and a following diacritic.
7719 // Example on OSX 10.5/10.6 with default fonts installed:
7720 // data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
7721 // &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
7722 // This means the rendering of the cluster will probably not be very good,
7723 // but it's the best we can do for now if the specified font only covered the
7724 // initial base character and not its applied marks.
7725 NS_WARN_IF_FALSE(aSource->IsClusterStart(start),
7726 "Started font run in the middle of a cluster");
7727 NS_WARN_IF_FALSE(end == aSource->GetLength() || aSource->IsClusterStart(end),
7728 "Ended font run in the middle of a cluster");
7730 nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
7731 start - aStart + aDest, false);
7732 if (NS_FAILED(rv))
7733 return;
7737 void
7738 gfxTextRun::ClearGlyphsAndCharacters()
7740 ResetGlyphRuns();
7741 memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
7742 mLength * sizeof(CompressedGlyph));
7743 mDetailedGlyphs = nullptr;
7746 void
7747 gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
7748 uint32_t aCharIndex)
7750 if (SetSpaceGlyphIfSimple(aFont, aContext, aCharIndex, ' ')) {
7751 return;
7754 aFont->InitWordCache();
7755 static const uint8_t space = ' ';
7756 gfxShapedWord *sw = aFont->GetShapedWord(aContext,
7757 &space, 1,
7758 HashMix(0, ' '),
7759 MOZ_SCRIPT_LATIN,
7760 mAppUnitsPerDevUnit,
7761 gfxTextRunFactory::TEXT_IS_8BIT |
7762 gfxTextRunFactory::TEXT_IS_ASCII |
7763 gfxTextRunFactory::TEXT_IS_PERSISTENT,
7764 nullptr);
7765 if (sw) {
7766 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
7767 CopyGlyphDataFrom(sw, aCharIndex);
7771 bool
7772 gfxTextRun::SetSpaceGlyphIfSimple(gfxFont *aFont, gfxContext *aContext,
7773 uint32_t aCharIndex, char16_t aSpaceChar)
7775 uint32_t spaceGlyph = aFont->GetSpaceGlyph();
7776 if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
7777 return false;
7780 uint32_t spaceWidthAppUnits =
7781 NS_lroundf(aFont->GetMetrics().spaceWidth * mAppUnitsPerDevUnit);
7782 if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
7783 return false;
7786 AddGlyphRun(aFont, gfxTextRange::kFontGroup, aCharIndex, false);
7787 CompressedGlyph g;
7788 g.SetSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
7789 if (aSpaceChar == ' ') {
7790 g.SetIsSpace();
7792 GetCharacterGlyphs()[aCharIndex] = g;
7793 return true;
7796 void
7797 gfxTextRun::FetchGlyphExtents(gfxContext *aRefContext)
7799 bool needsGlyphExtents = NeedsGlyphExtents(this);
7800 if (!needsGlyphExtents && !mDetailedGlyphs)
7801 return;
7803 uint32_t i, runCount = mGlyphRuns.Length();
7804 CompressedGlyph *charGlyphs = mCharacterGlyphs;
7805 for (i = 0; i < runCount; ++i) {
7806 const GlyphRun& run = mGlyphRuns[i];
7807 gfxFont *font = run.mFont;
7808 if (MOZ_UNLIKELY(font->GetStyle()->size == 0)) {
7809 continue;
7812 uint32_t start = run.mCharacterOffset;
7813 uint32_t end = i + 1 < runCount ?
7814 mGlyphRuns[i + 1].mCharacterOffset : GetLength();
7815 bool fontIsSetup = false;
7816 uint32_t j;
7817 gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
7819 for (j = start; j < end; ++j) {
7820 const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
7821 if (glyphData->IsSimpleGlyph()) {
7822 // If we're in speed mode, don't set up glyph extents here; we'll
7823 // just return "optimistic" glyph bounds later
7824 if (needsGlyphExtents) {
7825 uint32_t glyphIndex = glyphData->GetSimpleGlyph();
7826 if (!extents->IsGlyphKnown(glyphIndex)) {
7827 if (!fontIsSetup) {
7828 if (!font->SetupCairoFont(aRefContext)) {
7829 NS_WARNING("failed to set up font for glyph extents");
7830 break;
7832 fontIsSetup = true;
7834 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
7835 ++gGlyphExtentsSetupEagerSimple;
7836 #endif
7837 font->SetupGlyphExtents(aRefContext, glyphIndex, false, extents);
7840 } else if (!glyphData->IsMissing()) {
7841 uint32_t glyphCount = glyphData->GetGlyphCount();
7842 if (glyphCount == 0) {
7843 continue;
7845 const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
7846 if (!details) {
7847 continue;
7849 for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
7850 uint32_t glyphIndex = details->mGlyphID;
7851 if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
7852 if (!fontIsSetup) {
7853 if (!font->SetupCairoFont(aRefContext)) {
7854 NS_WARNING("failed to set up font for glyph extents");
7855 break;
7857 fontIsSetup = true;
7859 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
7860 ++gGlyphExtentsSetupEagerTight;
7861 #endif
7862 font->SetupGlyphExtents(aRefContext, glyphIndex, true, extents);
7871 gfxTextRun::ClusterIterator::ClusterIterator(gfxTextRun *aTextRun)
7872 : mTextRun(aTextRun), mCurrentChar(uint32_t(-1))
7876 void
7877 gfxTextRun::ClusterIterator::Reset()
7879 mCurrentChar = uint32_t(-1);
7882 bool
7883 gfxTextRun::ClusterIterator::NextCluster()
7885 uint32_t len = mTextRun->GetLength();
7886 while (++mCurrentChar < len) {
7887 if (mTextRun->IsClusterStart(mCurrentChar)) {
7888 return true;
7892 mCurrentChar = uint32_t(-1);
7893 return false;
7896 uint32_t
7897 gfxTextRun::ClusterIterator::ClusterLength() const
7899 if (mCurrentChar == uint32_t(-1)) {
7900 return 0;
7903 uint32_t i = mCurrentChar,
7904 len = mTextRun->GetLength();
7905 while (++i < len) {
7906 if (mTextRun->IsClusterStart(i)) {
7907 break;
7911 return i - mCurrentChar;
7914 gfxFloat
7915 gfxTextRun::ClusterIterator::ClusterAdvance(PropertyProvider *aProvider) const
7917 if (mCurrentChar == uint32_t(-1)) {
7918 return 0;
7921 return mTextRun->GetAdvanceWidth(mCurrentChar, ClusterLength(), aProvider);
7924 size_t
7925 gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
7927 // The second arg is how much gfxTextRun::AllocateStorage would have
7928 // allocated.
7929 size_t total = mGlyphRuns.SizeOfExcludingThis(aMallocSizeOf);
7931 if (mDetailedGlyphs) {
7932 total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
7935 return total;
7938 size_t
7939 gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
7941 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
7945 #ifdef DEBUG
7946 void
7947 gfxTextRun::Dump(FILE* aOutput) {
7948 if (!aOutput) {
7949 aOutput = stdout;
7952 uint32_t i;
7953 fputc('[', aOutput);
7954 for (i = 0; i < mGlyphRuns.Length(); ++i) {
7955 if (i > 0) {
7956 fputc(',', aOutput);
7958 gfxFont* font = mGlyphRuns[i].mFont;
7959 const gfxFontStyle* style = font->GetStyle();
7960 NS_ConvertUTF16toUTF8 fontName(font->GetName());
7961 nsAutoCString lang;
7962 style->language->ToUTF8String(lang);
7963 fprintf(aOutput, "%d: %s %f/%d/%d/%s", mGlyphRuns[i].mCharacterOffset,
7964 fontName.get(), style->size,
7965 style->weight, style->style, lang.get());
7967 fputc(']', aOutput);
7969 #endif