1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
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/. */
5 
6 #include "gfxFont.h"
7 
8 #include "mozilla/BinarySearch.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/FontPropertyTypes.h"
11 #include "mozilla/gfx/2D.h"
12 #include "mozilla/IntegerRange.h"
13 #include "mozilla/MathAlgorithms.h"
14 #include "mozilla/StaticPrefs_gfx.h"
15 #include "mozilla/SVGContextPaint.h"
16 
17 #include "mozilla/Logging.h"
18 
19 #include "nsITimer.h"
20 
21 #include "gfxGlyphExtents.h"
22 #include "gfxPlatform.h"
23 #include "gfxTextRun.h"
24 #include "nsGkAtoms.h"
25 
26 #include "gfxTypes.h"
27 #include "gfxContext.h"
28 #include "gfxFontMissingGlyphs.h"
29 #include "gfxGraphiteShaper.h"
30 #include "gfxHarfBuzzShaper.h"
31 #include "gfxUserFontSet.h"
32 #include "nsCRT.h"
33 #include "nsSpecialCasingData.h"
34 #include "nsTextRunTransformations.h"
35 #include "nsUGenCategory.h"
36 #include "nsUnicodeProperties.h"
37 #include "nsStyleConsts.h"
38 #include "mozilla/AppUnits.h"
39 #include "mozilla/Likely.h"
40 #include "mozilla/MemoryReporting.h"
41 #include "mozilla/Preferences.h"
42 #include "mozilla/Services.h"
43 #include "mozilla/Telemetry.h"
44 #include "gfxMathTable.h"
45 #include "gfxSVGGlyphs.h"
46 #include "gfx2DGlue.h"
47 #include "TextDrawTarget.h"
48 
49 #include "ThebesRLBox.h"
50 
51 #include "GreekCasing.h"
52 
53 #include "cairo.h"
54 #ifdef XP_WIN
55 #  include "cairo-win32.h"
56 #  include "gfxWindowsPlatform.h"
57 #endif
58 
59 #include "harfbuzz/hb.h"
60 #include "harfbuzz/hb-ot.h"
61 
62 #include <algorithm>
63 #include <limits>
64 #include <cmath>
65 
66 using namespace mozilla;
67 using namespace mozilla::gfx;
68 using namespace mozilla::unicode;
69 using mozilla::services::GetObserverService;
70 
71 gfxFontCache* gfxFontCache::gGlobalCache = nullptr;
72 
73 #ifdef DEBUG_roc
74 #  define DEBUG_TEXT_RUN_STORAGE_METRICS
75 #endif
76 
77 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
78 uint32_t gTextRunStorageHighWaterMark = 0;
79 uint32_t gTextRunStorage = 0;
80 uint32_t gFontCount = 0;
81 uint32_t gGlyphExtentsCount = 0;
82 uint32_t gGlyphExtentsWidthsTotalSize = 0;
83 uint32_t gGlyphExtentsSetupEagerSimple = 0;
84 uint32_t gGlyphExtentsSetupEagerTight = 0;
85 uint32_t gGlyphExtentsSetupLazyTight = 0;
86 uint32_t gGlyphExtentsSetupFallBackToTight = 0;
87 #endif
88 
89 #define LOG_FONTINIT(args) \
90   MOZ_LOG(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug, args)
91 #define LOG_FONTINIT_ENABLED() \
92   MOZ_LOG_TEST(gfxPlatform::GetLog(eGfxLog_fontinit), LogLevel::Debug)
93 
94 /*
95  * gfxFontCache - global cache of gfxFont instances.
96  * Expires unused fonts after a short interval;
97  * notifies fonts to age their cached shaped-word records;
98  * observes memory-pressure notification and tells fonts to clear their
99  * shaped-word caches to free up memory.
100  */
101 
102 MOZ_DEFINE_MALLOC_SIZE_OF(FontCacheMallocSizeOf)
103 
NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter,nsIMemoryReporter)104 NS_IMPL_ISUPPORTS(gfxFontCache::MemoryReporter, nsIMemoryReporter)
105 
106 /*virtual*/
107 gfxTextRunFactory::~gfxTextRunFactory() {
108   // Should not be dropped by stylo
109   MOZ_ASSERT(NS_IsMainThread());
110 }
111 
112 NS_IMETHODIMP
CollectReports(nsIHandleReportCallback * aHandleReport,nsISupports * aData,bool aAnonymize)113 gfxFontCache::MemoryReporter::CollectReports(
114     nsIHandleReportCallback* aHandleReport, nsISupports* aData,
115     bool aAnonymize) {
116   FontCacheSizes sizes;
117 
118   gfxFontCache::GetCache()->AddSizeOfIncludingThis(&FontCacheMallocSizeOf,
119                                                    &sizes);
120 
121   MOZ_COLLECT_REPORT("explicit/gfx/font-cache", KIND_HEAP, UNITS_BYTES,
122                      sizes.mFontInstances,
123                      "Memory used for active font instances.");
124 
125   MOZ_COLLECT_REPORT("explicit/gfx/font-shaped-words", KIND_HEAP, UNITS_BYTES,
126                      sizes.mShapedWords,
127                      "Memory used to cache shaped glyph data.");
128 
129   return NS_OK;
130 }
131 
NS_IMPL_ISUPPORTS(gfxFontCache::Observer,nsIObserver)132 NS_IMPL_ISUPPORTS(gfxFontCache::Observer, nsIObserver)
133 
134 NS_IMETHODIMP
135 gfxFontCache::Observer::Observe(nsISupports* aSubject, const char* aTopic,
136                                 const char16_t* someData) {
137   if (!nsCRT::strcmp(aTopic, "memory-pressure")) {
138     gfxFontCache* fontCache = gfxFontCache::GetCache();
139     if (fontCache) {
140       fontCache->FlushShapedWordCaches();
141     }
142   } else {
143     MOZ_ASSERT_UNREACHABLE("unexpected notification topic");
144   }
145   return NS_OK;
146 }
147 
Init()148 nsresult gfxFontCache::Init() {
149   NS_ASSERTION(!gGlobalCache, "Where did this come from?");
150   gGlobalCache = new gfxFontCache(GetMainThreadSerialEventTarget());
151   if (!gGlobalCache) {
152     return NS_ERROR_OUT_OF_MEMORY;
153   }
154   RegisterStrongMemoryReporter(new MemoryReporter());
155   return NS_OK;
156 }
157 
Shutdown()158 void gfxFontCache::Shutdown() {
159   delete gGlobalCache;
160   gGlobalCache = nullptr;
161 
162 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
163   printf("Textrun storage high water mark=%d\n", gTextRunStorageHighWaterMark);
164   printf("Total number of fonts=%d\n", gFontCount);
165   printf("Total glyph extents allocated=%d (size %d)\n", gGlyphExtentsCount,
166          int(gGlyphExtentsCount * sizeof(gfxGlyphExtents)));
167   printf("Total glyph extents width-storage size allocated=%d\n",
168          gGlyphExtentsWidthsTotalSize);
169   printf("Number of simple glyph extents eagerly requested=%d\n",
170          gGlyphExtentsSetupEagerSimple);
171   printf("Number of tight glyph extents eagerly requested=%d\n",
172          gGlyphExtentsSetupEagerTight);
173   printf("Number of tight glyph extents lazily requested=%d\n",
174          gGlyphExtentsSetupLazyTight);
175   printf("Number of simple glyph extent setups that fell back to tight=%d\n",
176          gGlyphExtentsSetupFallBackToTight);
177 #endif
178 }
179 
gfxFontCache(nsIEventTarget * aEventTarget)180 gfxFontCache::gfxFontCache(nsIEventTarget* aEventTarget)
181     : gfxFontCacheExpirationTracker(aEventTarget) {
182   nsCOMPtr<nsIObserverService> obs = GetObserverService();
183   if (obs) {
184     obs->AddObserver(new Observer, "memory-pressure", false);
185   }
186 
187   nsIEventTarget* target = nullptr;
188   if (XRE_IsContentProcess() && NS_IsMainThread()) {
189     target = aEventTarget;
190   }
191   NS_NewTimerWithFuncCallback(getter_AddRefs(mWordCacheExpirationTimer),
192                               WordCacheExpirationTimerCallback, this,
193                               SHAPED_WORD_TIMEOUT_SECONDS * 1000,
194                               nsITimer::TYPE_REPEATING_SLACK,
195                               "gfxFontCache::gfxFontCache", target);
196 }
197 
~gfxFontCache()198 gfxFontCache::~gfxFontCache() {
199   // Ensure the user font cache releases its references to font entries,
200   // so they aren't kept alive after the font instances and font-list
201   // have been shut down.
202   gfxUserFontSet::UserFontCache::Shutdown();
203 
204   if (mWordCacheExpirationTimer) {
205     mWordCacheExpirationTimer->Cancel();
206     mWordCacheExpirationTimer = nullptr;
207   }
208 
209   // Expire everything that has a zero refcount, so we don't leak them.
210   AgeAllGenerations();
211   // All fonts should be gone.
212   NS_WARNING_ASSERTION(mFonts.Count() == 0,
213                        "Fonts still alive while shutting down gfxFontCache");
214   // Note that we have to delete everything through the expiration
215   // tracker, since there might be fonts not in the hashtable but in
216   // the tracker.
217 }
218 
KeyEquals(const KeyTypePointer aKey) const219 bool gfxFontCache::HashEntry::KeyEquals(const KeyTypePointer aKey) const {
220   const gfxCharacterMap* fontUnicodeRangeMap = mFont->GetUnicodeRangeMap();
221   return aKey->mFontEntry == mFont->GetFontEntry() &&
222          aKey->mStyle->Equals(*mFont->GetStyle()) &&
223          ((!aKey->mUnicodeRangeMap && !fontUnicodeRangeMap) ||
224           (aKey->mUnicodeRangeMap && fontUnicodeRangeMap &&
225            aKey->mUnicodeRangeMap->Equals(fontUnicodeRangeMap)));
226 }
227 
Lookup(const gfxFontEntry * aFontEntry,const gfxFontStyle * aStyle,const gfxCharacterMap * aUnicodeRangeMap)228 gfxFont* gfxFontCache::Lookup(const gfxFontEntry* aFontEntry,
229                               const gfxFontStyle* aStyle,
230                               const gfxCharacterMap* aUnicodeRangeMap) {
231   Key key(aFontEntry, aStyle, aUnicodeRangeMap);
232   HashEntry* entry = mFonts.GetEntry(key);
233 
234   Telemetry::Accumulate(Telemetry::FONT_CACHE_HIT, entry != nullptr);
235   if (!entry) return nullptr;
236 
237   return entry->mFont;
238 }
239 
AddNew(gfxFont * aFont)240 void gfxFontCache::AddNew(gfxFont* aFont) {
241   Key key(aFont->GetFontEntry(), aFont->GetStyle(),
242           aFont->GetUnicodeRangeMap());
243   HashEntry* entry = mFonts.PutEntry(key);
244   if (!entry) return;
245   gfxFont* oldFont = entry->mFont;
246   entry->mFont = aFont;
247   // Assert that we can find the entry we just put in (this fails if the key
248   // has a NaN float value in it, e.g. 'sizeAdjust').
249   MOZ_ASSERT(entry == mFonts.GetEntry(key));
250   // If someone's asked us to replace an existing font entry, then that's a
251   // bit weird, but let it happen, and expire the old font if it's not used.
252   if (oldFont && oldFont->GetExpirationState()->IsTracked()) {
253     // if oldFont == aFont, recount should be > 0,
254     // so we shouldn't be here.
255     NS_ASSERTION(aFont != oldFont, "new font is tracked for expiry!");
256     NotifyExpired(oldFont);
257   }
258 }
259 
NotifyReleased(gfxFont * aFont)260 void gfxFontCache::NotifyReleased(gfxFont* aFont) {
261   nsresult rv = AddObject(aFont);
262   if (NS_FAILED(rv)) {
263     // We couldn't track it for some reason. Kill it now.
264     DestroyFont(aFont);
265   }
266   // Note that we might have fonts that aren't in the hashtable, perhaps because
267   // of OOM adding to the hashtable or because someone did an AddNew where
268   // we already had a font. These fonts are added to the expiration tracker
269   // anyway, even though Lookup can't resurrect them. Eventually they will
270   // expire and be deleted.
271 }
272 
NotifyExpired(gfxFont * aFont)273 void gfxFontCache::NotifyExpired(gfxFont* aFont) {
274   aFont->ClearCachedWords();
275   RemoveObject(aFont);
276   DestroyFont(aFont);
277 }
278 
DestroyFont(gfxFont * aFont)279 void gfxFontCache::DestroyFont(gfxFont* aFont) {
280   Key key(aFont->GetFontEntry(), aFont->GetStyle(),
281           aFont->GetUnicodeRangeMap());
282   HashEntry* entry = mFonts.GetEntry(key);
283   if (entry && entry->mFont == aFont) {
284     mFonts.RemoveEntry(entry);
285   }
286   NS_ASSERTION(aFont->GetRefCount() == 0,
287                "Destroying with non-zero ref count!");
288   delete aFont;
289 }
290 
291 /*static*/
WordCacheExpirationTimerCallback(nsITimer * aTimer,void * aCache)292 void gfxFontCache::WordCacheExpirationTimerCallback(nsITimer* aTimer,
293                                                     void* aCache) {
294   gfxFontCache* cache = static_cast<gfxFontCache*>(aCache);
295   for (const auto& entry : cache->mFonts) {
296     entry.mFont->AgeCachedWords();
297   }
298 }
299 
FlushShapedWordCaches()300 void gfxFontCache::FlushShapedWordCaches() {
301   for (const auto& entry : mFonts) {
302     entry.mFont->ClearCachedWords();
303   }
304 }
305 
NotifyGlyphsChanged()306 void gfxFontCache::NotifyGlyphsChanged() {
307   for (const auto& entry : mFonts) {
308     entry.mFont->NotifyGlyphsChanged();
309   }
310 }
311 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const312 void gfxFontCache::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
313                                           FontCacheSizes* aSizes) const {
314   // TODO: add the overhead of the expiration tracker (generation arrays)
315 
316   aSizes->mFontInstances += mFonts.ShallowSizeOfExcludingThis(aMallocSizeOf);
317   for (const auto& entry : mFonts) {
318     entry.mFont->AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
319   }
320 }
321 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const322 void gfxFontCache::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
323                                           FontCacheSizes* aSizes) const {
324   aSizes->mFontInstances += aMallocSizeOf(this);
325   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
326 }
327 
328 #define MAX_SSXX_VALUE 99
329 #define MAX_CVXX_VALUE 99
330 
LookupAlternateValues(const gfxFontFeatureValueSet & aFeatureLookup,const nsACString & aFamily,const StyleVariantAlternates & aAlternates,nsTArray<gfxFontFeature> & aFontFeatures)331 static void LookupAlternateValues(const gfxFontFeatureValueSet& aFeatureLookup,
332                                   const nsACString& aFamily,
333                                   const StyleVariantAlternates& aAlternates,
334                                   nsTArray<gfxFontFeature>& aFontFeatures) {
335   using Tag = StyleVariantAlternates::Tag;
336 
337   // historical-forms gets handled in nsFont::AddFontFeaturesToStyle.
338   if (aAlternates.IsHistoricalForms()) {
339     return;
340   }
341 
342   gfxFontFeature feature;
343   if (aAlternates.IsCharacterVariant()) {
344     for (auto& ident : aAlternates.AsCharacterVariant().AsSpan()) {
345       Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
346           aFamily, NS_FONT_VARIANT_ALTERNATES_CHARACTER_VARIANT,
347           ident.AsAtom());
348       // nothing defined, skip
349       if (values.IsEmpty()) {
350         continue;
351       }
352       NS_ASSERTION(values.Length() <= 2,
353                    "too many values allowed for character-variant");
354       // character-variant(12 3) ==> 'cv12' = 3
355       uint32_t nn = values[0];
356       // ignore values greater than 99
357       if (nn == 0 || nn > MAX_CVXX_VALUE) {
358         continue;
359       }
360       feature.mValue = values.Length() > 1 ? values[1] : 1;
361       feature.mTag = HB_TAG('c', 'v', ('0' + nn / 10), ('0' + nn % 10));
362       aFontFeatures.AppendElement(feature);
363     }
364     return;
365   }
366 
367   if (aAlternates.IsStyleset()) {
368     for (auto& ident : aAlternates.AsStyleset().AsSpan()) {
369       Span<const uint32_t> values = aFeatureLookup.GetFontFeatureValuesFor(
370           aFamily, NS_FONT_VARIANT_ALTERNATES_STYLESET, ident.AsAtom());
371 
372       // styleset(1 2 7) ==> 'ss01' = 1, 'ss02' = 1, 'ss07' = 1
373       feature.mValue = 1;
374       for (uint32_t nn : values) {
375         if (nn == 0 || nn > MAX_SSXX_VALUE) {
376           continue;
377         }
378         feature.mTag = HB_TAG('s', 's', ('0' + nn / 10), ('0' + nn % 10));
379         aFontFeatures.AppendElement(feature);
380       }
381     }
382     return;
383   }
384 
385   uint32_t constant = 0;
386   nsAtom* name = nullptr;
387   switch (aAlternates.tag) {
388     case Tag::Swash:
389       constant = NS_FONT_VARIANT_ALTERNATES_SWASH;
390       name = aAlternates.AsSwash().AsAtom();
391       break;
392     case Tag::Stylistic:
393       constant = NS_FONT_VARIANT_ALTERNATES_STYLISTIC;
394       name = aAlternates.AsStylistic().AsAtom();
395       break;
396     case Tag::Ornaments:
397       constant = NS_FONT_VARIANT_ALTERNATES_ORNAMENTS;
398       name = aAlternates.AsOrnaments().AsAtom();
399       break;
400     case Tag::Annotation:
401       constant = NS_FONT_VARIANT_ALTERNATES_ANNOTATION;
402       name = aAlternates.AsAnnotation().AsAtom();
403       break;
404     default:
405       MOZ_ASSERT_UNREACHABLE("Unknown font-variant-alternates value!");
406       return;
407   }
408 
409   Span<const uint32_t> values =
410       aFeatureLookup.GetFontFeatureValuesFor(aFamily, constant, name);
411   if (values.IsEmpty()) {
412     return;
413   }
414   MOZ_ASSERT(values.Length() == 1,
415              "too many values for font-specific font-variant-alternates");
416 
417   feature.mValue = values[0];
418   switch (aAlternates.tag) {
419     case Tag::Swash:  // swsh, cswh
420       feature.mTag = HB_TAG('s', 'w', 's', 'h');
421       aFontFeatures.AppendElement(feature);
422       feature.mTag = HB_TAG('c', 's', 'w', 'h');
423       break;
424     case Tag::Stylistic:  // salt
425       feature.mTag = HB_TAG('s', 'a', 'l', 't');
426       break;
427     case Tag::Ornaments:  // ornm
428       feature.mTag = HB_TAG('o', 'r', 'n', 'm');
429       break;
430     case Tag::Annotation:  // nalt
431       feature.mTag = HB_TAG('n', 'a', 'l', 't');
432       break;
433     default:
434       MOZ_ASSERT_UNREACHABLE("how?");
435       return;
436   }
437   aFontFeatures.AppendElement(feature);
438 }
439 
440 /* static */
MergeFontFeatures(const gfxFontStyle * aStyle,const nsTArray<gfxFontFeature> & aFontFeatures,bool aDisableLigatures,const nsACString & aFamilyName,bool aAddSmallCaps,void (* aHandleFeature)(const uint32_t &,uint32_t &,void *),void * aHandleFeatureData)441 void gfxFontShaper::MergeFontFeatures(
442     const gfxFontStyle* aStyle, const nsTArray<gfxFontFeature>& aFontFeatures,
443     bool aDisableLigatures, const nsACString& aFamilyName, bool aAddSmallCaps,
444     void (*aHandleFeature)(const uint32_t&, uint32_t&, void*),
445     void* aHandleFeatureData) {
446   const nsTArray<gfxFontFeature>& styleRuleFeatures = aStyle->featureSettings;
447 
448   // Bail immediately if nothing to do, which is the common case.
449   if (styleRuleFeatures.IsEmpty() && aFontFeatures.IsEmpty() &&
450       !aDisableLigatures &&
451       aStyle->variantCaps == NS_FONT_VARIANT_CAPS_NORMAL &&
452       aStyle->variantSubSuper == NS_FONT_VARIANT_POSITION_NORMAL &&
453       aStyle->variantAlternates.IsEmpty()) {
454     return;
455   }
456 
457   nsTHashMap<nsUint32HashKey, uint32_t> mergedFeatures;
458 
459   // add feature values from font
460   for (const gfxFontFeature& feature : aFontFeatures) {
461     mergedFeatures.InsertOrUpdate(feature.mTag, feature.mValue);
462   }
463 
464   // font-variant-caps - handled here due to the need for fallback handling
465   // petite caps cases can fallback to appropriate smallcaps
466   uint32_t variantCaps = aStyle->variantCaps;
467   switch (variantCaps) {
468     case NS_FONT_VARIANT_CAPS_NORMAL:
469       break;
470 
471     case NS_FONT_VARIANT_CAPS_ALLSMALL:
472       mergedFeatures.InsertOrUpdate(HB_TAG('c', '2', 's', 'c'), 1);
473       // fall through to the small-caps case
474       [[fallthrough]];
475 
476     case NS_FONT_VARIANT_CAPS_SMALLCAPS:
477       mergedFeatures.InsertOrUpdate(HB_TAG('s', 'm', 'c', 'p'), 1);
478       break;
479 
480     case NS_FONT_VARIANT_CAPS_ALLPETITE:
481       mergedFeatures.InsertOrUpdate(aAddSmallCaps ? HB_TAG('c', '2', 's', 'c')
482                                                   : HB_TAG('c', '2', 'p', 'c'),
483                                     1);
484       // fall through to the petite-caps case
485       [[fallthrough]];
486 
487     case NS_FONT_VARIANT_CAPS_PETITECAPS:
488       mergedFeatures.InsertOrUpdate(aAddSmallCaps ? HB_TAG('s', 'm', 'c', 'p')
489                                                   : HB_TAG('p', 'c', 'a', 'p'),
490                                     1);
491       break;
492 
493     case NS_FONT_VARIANT_CAPS_TITLING:
494       mergedFeatures.InsertOrUpdate(HB_TAG('t', 'i', 't', 'l'), 1);
495       break;
496 
497     case NS_FONT_VARIANT_CAPS_UNICASE:
498       mergedFeatures.InsertOrUpdate(HB_TAG('u', 'n', 'i', 'c'), 1);
499       break;
500 
501     default:
502       MOZ_ASSERT_UNREACHABLE("Unexpected variantCaps");
503       break;
504   }
505 
506   // font-variant-position - handled here due to the need for fallback
507   switch (aStyle->variantSubSuper) {
508     case NS_FONT_VARIANT_POSITION_NORMAL:
509       break;
510     case NS_FONT_VARIANT_POSITION_SUPER:
511       mergedFeatures.InsertOrUpdate(HB_TAG('s', 'u', 'p', 's'), 1);
512       break;
513     case NS_FONT_VARIANT_POSITION_SUB:
514       mergedFeatures.InsertOrUpdate(HB_TAG('s', 'u', 'b', 's'), 1);
515       break;
516     default:
517       MOZ_ASSERT_UNREACHABLE("Unexpected variantSubSuper");
518       break;
519   }
520 
521   // add font-specific feature values from style rules
522   if (aStyle->featureValueLookup && !aStyle->variantAlternates.IsEmpty()) {
523     AutoTArray<gfxFontFeature, 4> featureList;
524 
525     // insert list of alternate feature settings
526     for (auto& alternate : aStyle->variantAlternates.AsSpan()) {
527       LookupAlternateValues(*aStyle->featureValueLookup, aFamilyName, alternate,
528                             featureList);
529     }
530 
531     for (const gfxFontFeature& feature : featureList) {
532       mergedFeatures.InsertOrUpdate(feature.mTag, feature.mValue);
533     }
534   }
535 
536   // Add features that are already resolved to tags & values in the style.
537   if (styleRuleFeatures.IsEmpty()) {
538     // Disable common ligatures if non-zero letter-spacing is in effect.
539     if (aDisableLigatures) {
540       mergedFeatures.InsertOrUpdate(HB_TAG('l', 'i', 'g', 'a'), 0);
541       mergedFeatures.InsertOrUpdate(HB_TAG('c', 'l', 'i', 'g'), 0);
542     }
543   } else {
544     for (const gfxFontFeature& feature : styleRuleFeatures) {
545       // A dummy feature (0,0) is used as a sentinel to separate features
546       // originating from font-variant-* or other high-level properties from
547       // those directly specified as font-feature-settings. The high-level
548       // features may be overridden by aDisableLigatures, while low-level
549       // features specified directly as tags will come last and therefore
550       // take precedence over everything else.
551       if (feature.mTag) {
552         mergedFeatures.InsertOrUpdate(feature.mTag, feature.mValue);
553       } else if (aDisableLigatures) {
554         // Handle ligature-disabling setting at the boundary between high-
555         // and low-level features.
556         mergedFeatures.InsertOrUpdate(HB_TAG('l', 'i', 'g', 'a'), 0);
557         mergedFeatures.InsertOrUpdate(HB_TAG('c', 'l', 'i', 'g'), 0);
558       }
559     }
560   }
561 
562   if (mergedFeatures.Count() != 0) {
563     for (auto iter = mergedFeatures.Iter(); !iter.Done(); iter.Next()) {
564       aHandleFeature(iter.Key(), iter.Data(), aHandleFeatureData);
565     }
566   }
567 }
568 
SetupClusterBoundaries(uint32_t aOffset,const char16_t * aString,uint32_t aLength)569 void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
570                                            const char16_t* aString,
571                                            uint32_t aLength) {
572   CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
573 
574   CompressedGlyph extendCluster = CompressedGlyph::MakeComplex(false, true);
575 
576   ClusterIterator iter(aString, aLength);
577 
578   // the ClusterIterator won't be able to tell us if the string
579   // _begins_ with a cluster-extender, so we handle that here
580   if (aLength) {
581     uint32_t ch = *aString;
582     if (aLength > 1 && NS_IS_SURROGATE_PAIR(ch, aString[1])) {
583       ch = SURROGATE_TO_UCS4(ch, aString[1]);
584     }
585     if (IsClusterExtender(ch)) {
586       *glyphs = extendCluster;
587     }
588   }
589 
590   const char16_t kIdeographicSpace = 0x3000;
591   while (!iter.AtEnd()) {
592     if (*iter == char16_t(' ') || *iter == kIdeographicSpace) {
593       glyphs->SetIsSpace();
594     }
595     // advance iter to the next cluster-start (or end of text)
596     iter.Next();
597     // step past the first char of the cluster
598     aString++;
599     glyphs++;
600     // mark all the rest as cluster-continuations
601     while (aString < iter) {
602       *glyphs = extendCluster;
603       glyphs++;
604       aString++;
605     }
606   }
607 }
608 
SetupClusterBoundaries(uint32_t aOffset,const uint8_t * aString,uint32_t aLength)609 void gfxShapedText::SetupClusterBoundaries(uint32_t aOffset,
610                                            const uint8_t* aString,
611                                            uint32_t aLength) {
612   CompressedGlyph* glyphs = GetCharacterGlyphs() + aOffset;
613   const uint8_t* limit = aString + aLength;
614 
615   while (aString < limit) {
616     if (*aString == uint8_t(' ')) {
617       glyphs->SetIsSpace();
618     }
619     aString++;
620     glyphs++;
621   }
622 }
623 
AllocateDetailedGlyphs(uint32_t aIndex,uint32_t aCount)624 gfxShapedText::DetailedGlyph* gfxShapedText::AllocateDetailedGlyphs(
625     uint32_t aIndex, uint32_t aCount) {
626   NS_ASSERTION(aIndex < GetLength(), "Index out of range");
627 
628   if (!mDetailedGlyphs) {
629     mDetailedGlyphs = MakeUnique<DetailedGlyphStore>();
630   }
631 
632   return mDetailedGlyphs->Allocate(aIndex, aCount);
633 }
634 
SetDetailedGlyphs(uint32_t aIndex,uint32_t aGlyphCount,const DetailedGlyph * aGlyphs)635 void gfxShapedText::SetDetailedGlyphs(uint32_t aIndex, uint32_t aGlyphCount,
636                                       const DetailedGlyph* aGlyphs) {
637   CompressedGlyph& g = GetCharacterGlyphs()[aIndex];
638 
639   MOZ_ASSERT(aIndex > 0 || g.IsLigatureGroupStart(),
640              "First character can't be a ligature continuation!");
641 
642   if (aGlyphCount > 0) {
643     DetailedGlyph* details = AllocateDetailedGlyphs(aIndex, aGlyphCount);
644     memcpy(details, aGlyphs, sizeof(DetailedGlyph) * aGlyphCount);
645   }
646 
647   g.SetGlyphCount(aGlyphCount);
648 }
649 
650 #define ZWNJ 0x200C
651 #define ZWJ 0x200D
IsIgnorable(uint32_t aChar)652 static inline bool IsIgnorable(uint32_t aChar) {
653   return (IsDefaultIgnorable(aChar)) || aChar == ZWNJ || aChar == ZWJ;
654 }
655 
SetMissingGlyph(uint32_t aIndex,uint32_t aChar,gfxFont * aFont)656 void gfxShapedText::SetMissingGlyph(uint32_t aIndex, uint32_t aChar,
657                                     gfxFont* aFont) {
658   CompressedGlyph& g = GetCharacterGlyphs()[aIndex];
659   uint8_t category = GetGeneralCategory(aChar);
660   if (category >= HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK &&
661       category <= HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
662     g.SetComplex(false, true);
663   }
664 
665   // Leaving advance as zero will prevent drawing the hexbox for ignorables.
666   int32_t advance = 0;
667   if (!IsIgnorable(aChar)) {
668     gfxFloat width =
669         std::max(aFont->GetMetrics(nsFontMetrics::eHorizontal).aveCharWidth,
670                  gfxFloat(gfxFontMissingGlyphs::GetDesiredMinWidth(
671                      aChar, mAppUnitsPerDevUnit)));
672     advance = int32_t(width * mAppUnitsPerDevUnit);
673   }
674   DetailedGlyph detail = {aChar, advance, gfx::Point()};
675   SetDetailedGlyphs(aIndex, 1, &detail);
676   g.SetMissing();
677 }
678 
FilterIfIgnorable(uint32_t aIndex,uint32_t aCh)679 bool gfxShapedText::FilterIfIgnorable(uint32_t aIndex, uint32_t aCh) {
680   if (IsIgnorable(aCh)) {
681     // There are a few default-ignorables of Letter category (currently,
682     // just the Hangul filler characters) that we'd better not discard
683     // if they're followed by additional characters in the same cluster.
684     // Some fonts use them to carry the width of a whole cluster of
685     // combining jamos; see bug 1238243.
686     auto* charGlyphs = GetCharacterGlyphs();
687     if (GetGenCategory(aCh) == nsUGenCategory::kLetter &&
688         aIndex + 1 < GetLength() && !charGlyphs[aIndex + 1].IsClusterStart()) {
689       return false;
690     }
691     // A compressedGlyph that is set to MISSING but has no DetailedGlyphs list
692     // will be zero-width/invisible, which is what we want here.
693     CompressedGlyph& g = charGlyphs[aIndex];
694     g.SetComplex(g.IsClusterStart(), g.IsLigatureGroupStart()).SetMissing();
695     return true;
696   }
697   return false;
698 }
699 
AdjustAdvancesForSyntheticBold(float aSynBoldOffset,uint32_t aOffset,uint32_t aLength)700 void gfxShapedText::AdjustAdvancesForSyntheticBold(float aSynBoldOffset,
701                                                    uint32_t aOffset,
702                                                    uint32_t aLength) {
703   uint32_t synAppUnitOffset = aSynBoldOffset * mAppUnitsPerDevUnit;
704   CompressedGlyph* charGlyphs = GetCharacterGlyphs();
705   for (uint32_t i = aOffset; i < aOffset + aLength; ++i) {
706     CompressedGlyph* glyphData = charGlyphs + i;
707     if (glyphData->IsSimpleGlyph()) {
708       // simple glyphs ==> just add the advance
709       int32_t advance = glyphData->GetSimpleAdvance();
710       if (advance > 0) {
711         advance += synAppUnitOffset;
712         if (CompressedGlyph::IsSimpleAdvance(advance)) {
713           glyphData->SetSimpleGlyph(advance, glyphData->GetSimpleGlyph());
714         } else {
715           // rare case, tested by making this the default
716           uint32_t glyphIndex = glyphData->GetSimpleGlyph();
717           // convert the simple CompressedGlyph to an empty complex record
718           glyphData->SetComplex(true, true);
719           // then set its details (glyph ID with its new advance)
720           DetailedGlyph detail = {glyphIndex, advance, gfx::Point()};
721           SetDetailedGlyphs(i, 1, &detail);
722         }
723       }
724     } else {
725       // complex glyphs ==> add offset at cluster/ligature boundaries
726       uint32_t detailedLength = glyphData->GetGlyphCount();
727       if (detailedLength) {
728         DetailedGlyph* details = GetDetailedGlyphs(i);
729         if (!details) {
730           continue;
731         }
732         if (IsRightToLeft()) {
733           if (details[0].mAdvance > 0) {
734             details[0].mAdvance += synAppUnitOffset;
735           }
736         } else {
737           if (details[detailedLength - 1].mAdvance > 0) {
738             details[detailedLength - 1].mAdvance += synAppUnitOffset;
739           }
740         }
741       }
742     }
743   }
744 }
745 
AngleForSyntheticOblique() const746 float gfxFont::AngleForSyntheticOblique() const {
747   // If the style doesn't call for italic/oblique, or if the face already
748   // provides it, no synthetic style should be added.
749   if (mStyle.style == FontSlantStyle::Normal() || !mStyle.allowSyntheticStyle ||
750       !mFontEntry->IsUpright()) {
751     return 0.0f;
752   }
753 
754   // If style calls for italic, and face doesn't support it, use default
755   // oblique angle as a simulation.
756   if (mStyle.style.IsItalic()) {
757     return mFontEntry->SupportsItalic() ? 0.0f : FontSlantStyle::kDefaultAngle;
758   }
759 
760   // Default or custom oblique angle
761   return mStyle.style.ObliqueAngle();
762 }
763 
SkewForSyntheticOblique() const764 float gfxFont::SkewForSyntheticOblique() const {
765   // Precomputed value of tan(kDefaultAngle), the default italic/oblique slant;
766   // avoids calling tan() at runtime except for custom oblique values.
767   static const float kTanDefaultAngle =
768       tan(FontSlantStyle::kDefaultAngle * (M_PI / 180.0));
769 
770   float angle = AngleForSyntheticOblique();
771   if (angle == 0.0f) {
772     return 0.0f;
773   } else if (angle == FontSlantStyle::kDefaultAngle) {
774     return kTanDefaultAngle;
775   } else {
776     return tan(angle * (M_PI / 180.0));
777   }
778 }
779 
CombineWith(const RunMetrics & aOther,bool aOtherIsOnLeft)780 void gfxFont::RunMetrics::CombineWith(const RunMetrics& aOther,
781                                       bool aOtherIsOnLeft) {
782   mAscent = std::max(mAscent, aOther.mAscent);
783   mDescent = std::max(mDescent, aOther.mDescent);
784   if (aOtherIsOnLeft) {
785     mBoundingBox = (mBoundingBox + gfxPoint(aOther.mAdvanceWidth, 0))
786                        .Union(aOther.mBoundingBox);
787   } else {
788     mBoundingBox =
789         mBoundingBox.Union(aOther.mBoundingBox + gfxPoint(mAdvanceWidth, 0));
790   }
791   mAdvanceWidth += aOther.mAdvanceWidth;
792 }
793 
gfxFont(const RefPtr<UnscaledFont> & aUnscaledFont,gfxFontEntry * aFontEntry,const gfxFontStyle * aFontStyle,AntialiasOption anAAOption)794 gfxFont::gfxFont(const RefPtr<UnscaledFont>& aUnscaledFont,
795                  gfxFontEntry* aFontEntry, const gfxFontStyle* aFontStyle,
796                  AntialiasOption anAAOption)
797     : mFontEntry(aFontEntry),
798       mUnscaledFont(aUnscaledFont),
799       mStyle(*aFontStyle),
800       mAdjustedSize(-1.0),       // negative to indicate "not yet initialized"
801       mFUnitsConvFactor(-1.0f),  // negative to indicate "not yet initialized"
802       mAntialiasOption(anAAOption),
803       mIsValid(true),
804       mApplySyntheticBold(false),
805       mKerningEnabled(false),
806       mMathInitialized(false) {
807 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
808   ++gFontCount;
809 #endif
810 
811   if (MOZ_UNLIKELY(StaticPrefs::gfx_text_disable_aa_AtStartup())) {
812     mAntialiasOption = kAntialiasNone;
813   }
814 
815   // Turn off AA for Ahem for testing purposes when requested.
816   if (MOZ_UNLIKELY(StaticPrefs::gfx_font_rendering_ahem_antialias_none() &&
817                    mFontEntry->FamilyName().EqualsLiteral("Ahem"))) {
818     mAntialiasOption = kAntialiasNone;
819   }
820 
821   mKerningSet = HasFeatureSet(HB_TAG('k', 'e', 'r', 'n'), mKerningEnabled);
822 }
823 
~gfxFont()824 gfxFont::~gfxFont() {
825   mFontEntry->NotifyFontDestroyed(this);
826 
827   if (mGlyphChangeObservers) {
828     for (const auto& key : *mGlyphChangeObservers) {
829       key->ForgetFont();
830     }
831   }
832 }
833 
834 // Work out whether cairo will snap inter-glyph spacing to pixels.
835 //
836 // Layout does not align text to pixel boundaries, so, with font drawing
837 // backends that snap glyph positions to pixels, it is important that
838 // inter-glyph spacing within words is always an integer number of pixels.
839 // This ensures that the drawing backend snaps all of the word's glyphs in the
840 // same direction and so inter-glyph spacing remains the same.
841 //
GetRoundOffsetsToPixels(DrawTarget * aDrawTarget)842 gfxFont::RoundingFlags gfxFont::GetRoundOffsetsToPixels(
843     DrawTarget* aDrawTarget) {
844   // Could do something fancy here for ScaleFactors of
845   // AxisAlignedTransforms, but we leave things simple.
846   // Not much point rounding if a matrix will mess things up anyway.
847   // Also check if the font already knows hint metrics is off...
848   if (aDrawTarget->GetTransform().HasNonTranslation() || !ShouldHintMetrics()) {
849     return RoundingFlags(0);
850   }
851 
852   cairo_t* cr = static_cast<cairo_t*>(
853       aDrawTarget->GetNativeSurface(NativeSurfaceType::CAIRO_CONTEXT));
854   if (cr) {
855     cairo_surface_t* target = cairo_get_target(cr);
856 
857     // Check whether the cairo surface's font options hint metrics.
858     cairo_font_options_t* fontOptions = cairo_font_options_create();
859     cairo_surface_get_font_options(target, fontOptions);
860     cairo_hint_metrics_t hintMetrics =
861         cairo_font_options_get_hint_metrics(fontOptions);
862     cairo_font_options_destroy(fontOptions);
863 
864     switch (hintMetrics) {
865       case CAIRO_HINT_METRICS_OFF:
866         return RoundingFlags(0);
867       case CAIRO_HINT_METRICS_ON:
868         return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
869       default:
870         break;
871     }
872   }
873 
874   if (ShouldRoundXOffset(cr)) {
875     return RoundingFlags::kRoundX | RoundingFlags::kRoundY;
876   } else {
877     return RoundingFlags::kRoundY;
878   }
879 }
880 
GetGlyphAdvance(uint16_t aGID,bool aVertical)881 gfxFloat gfxFont::GetGlyphAdvance(uint16_t aGID, bool aVertical) {
882   if (!aVertical && ProvidesGlyphWidths()) {
883     return GetGlyphWidth(aGID) / 65536.0;
884   }
885   if (mFUnitsConvFactor < 0.0f) {
886     GetMetrics(nsFontMetrics::eHorizontal);
887   }
888   NS_ASSERTION(mFUnitsConvFactor >= 0.0f,
889                "missing font unit conversion factor");
890   if (!mHarfBuzzShaper) {
891     mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
892   }
893   gfxHarfBuzzShaper* shaper =
894       static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
895   if (!shaper->Initialize()) {
896     return 0;
897   }
898   return (aVertical ? shaper->GetGlyphVAdvance(aGID)
899                     : shaper->GetGlyphHAdvance(aGID)) /
900          65536.0;
901 }
902 
GetCharAdvance(uint32_t aUnicode,bool aVertical)903 gfxFloat gfxFont::GetCharAdvance(uint32_t aUnicode, bool aVertical) {
904   uint32_t gid = 0;
905   if (ProvidesGetGlyph()) {
906     gid = GetGlyph(aUnicode, 0);
907   } else {
908     if (!mHarfBuzzShaper) {
909       mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
910     }
911     gfxHarfBuzzShaper* shaper =
912         static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
913     if (!shaper->Initialize()) {
914       return -1.0;
915     }
916     gid = shaper->GetNominalGlyph(aUnicode);
917   }
918   if (!gid) {
919     return -1.0;
920   }
921   return GetGlyphAdvance(gid, aVertical);
922 }
923 
CollectLookupsByFeature(hb_face_t * aFace,hb_tag_t aTableTag,uint32_t aFeatureIndex,hb_set_t * aLookups)924 static void CollectLookupsByFeature(hb_face_t* aFace, hb_tag_t aTableTag,
925                                     uint32_t aFeatureIndex,
926                                     hb_set_t* aLookups) {
927   uint32_t lookups[32];
928   uint32_t i, len, offset;
929 
930   offset = 0;
931   do {
932     len = ArrayLength(lookups);
933     hb_ot_layout_feature_get_lookups(aFace, aTableTag, aFeatureIndex, offset,
934                                      &len, lookups);
935     for (i = 0; i < len; i++) {
936       hb_set_add(aLookups, lookups[i]);
937     }
938     offset += len;
939   } while (len == ArrayLength(lookups));
940 }
941 
CollectLookupsByLanguage(hb_face_t * aFace,hb_tag_t aTableTag,const nsTHashSet<uint32_t> & aSpecificFeatures,hb_set_t * aOtherLookups,hb_set_t * aSpecificFeatureLookups,uint32_t aScriptIndex,uint32_t aLangIndex)942 static void CollectLookupsByLanguage(
943     hb_face_t* aFace, hb_tag_t aTableTag,
944     const nsTHashSet<uint32_t>& aSpecificFeatures, hb_set_t* aOtherLookups,
945     hb_set_t* aSpecificFeatureLookups, uint32_t aScriptIndex,
946     uint32_t aLangIndex) {
947   uint32_t reqFeatureIndex;
948   if (hb_ot_layout_language_get_required_feature_index(
949           aFace, aTableTag, aScriptIndex, aLangIndex, &reqFeatureIndex)) {
950     CollectLookupsByFeature(aFace, aTableTag, reqFeatureIndex, aOtherLookups);
951   }
952 
953   uint32_t featureIndexes[32];
954   uint32_t i, len, offset;
955 
956   offset = 0;
957   do {
958     len = ArrayLength(featureIndexes);
959     hb_ot_layout_language_get_feature_indexes(aFace, aTableTag, aScriptIndex,
960                                               aLangIndex, offset, &len,
961                                               featureIndexes);
962 
963     for (i = 0; i < len; i++) {
964       uint32_t featureIndex = featureIndexes[i];
965 
966       // get the feature tag
967       hb_tag_t featureTag;
968       uint32_t tagLen = 1;
969       hb_ot_layout_language_get_feature_tags(aFace, aTableTag, aScriptIndex,
970                                              aLangIndex, offset + i, &tagLen,
971                                              &featureTag);
972 
973       // collect lookups
974       hb_set_t* lookups = aSpecificFeatures.Contains(featureTag)
975                               ? aSpecificFeatureLookups
976                               : aOtherLookups;
977       CollectLookupsByFeature(aFace, aTableTag, featureIndex, lookups);
978     }
979     offset += len;
980   } while (len == ArrayLength(featureIndexes));
981 }
982 
HasLookupRuleWithGlyphByScript(hb_face_t * aFace,hb_tag_t aTableTag,hb_tag_t aScriptTag,uint32_t aScriptIndex,uint16_t aGlyph,const nsTHashSet<uint32_t> & aDefaultFeatures,bool & aHasDefaultFeatureWithGlyph)983 static bool HasLookupRuleWithGlyphByScript(
984     hb_face_t* aFace, hb_tag_t aTableTag, hb_tag_t aScriptTag,
985     uint32_t aScriptIndex, uint16_t aGlyph,
986     const nsTHashSet<uint32_t>& aDefaultFeatures,
987     bool& aHasDefaultFeatureWithGlyph) {
988   uint32_t numLangs, lang;
989   hb_set_t* defaultFeatureLookups = hb_set_create();
990   hb_set_t* nonDefaultFeatureLookups = hb_set_create();
991 
992   // default lang
993   CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
994                            nonDefaultFeatureLookups, defaultFeatureLookups,
995                            aScriptIndex, HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
996 
997   // iterate over langs
998   numLangs = hb_ot_layout_script_get_language_tags(
999       aFace, aTableTag, aScriptIndex, 0, nullptr, nullptr);
1000   for (lang = 0; lang < numLangs; lang++) {
1001     CollectLookupsByLanguage(aFace, aTableTag, aDefaultFeatures,
1002                              nonDefaultFeatureLookups, defaultFeatureLookups,
1003                              aScriptIndex, lang);
1004   }
1005 
1006   // look for the glyph among default feature lookups
1007   aHasDefaultFeatureWithGlyph = false;
1008   hb_set_t* glyphs = hb_set_create();
1009   hb_codepoint_t index = -1;
1010   while (hb_set_next(defaultFeatureLookups, &index)) {
1011     hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
1012                                        glyphs, nullptr);
1013     if (hb_set_has(glyphs, aGlyph)) {
1014       aHasDefaultFeatureWithGlyph = true;
1015       break;
1016     }
1017   }
1018 
1019   // look for the glyph among non-default feature lookups
1020   // if no default feature lookups contained spaces
1021   bool hasNonDefaultFeatureWithGlyph = false;
1022   if (!aHasDefaultFeatureWithGlyph) {
1023     hb_set_clear(glyphs);
1024     index = -1;
1025     while (hb_set_next(nonDefaultFeatureLookups, &index)) {
1026       hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs,
1027                                          glyphs, glyphs, nullptr);
1028       if (hb_set_has(glyphs, aGlyph)) {
1029         hasNonDefaultFeatureWithGlyph = true;
1030         break;
1031       }
1032     }
1033   }
1034 
1035   hb_set_destroy(glyphs);
1036   hb_set_destroy(defaultFeatureLookups);
1037   hb_set_destroy(nonDefaultFeatureLookups);
1038 
1039   return aHasDefaultFeatureWithGlyph || hasNonDefaultFeatureWithGlyph;
1040 }
1041 
HasLookupRuleWithGlyph(hb_face_t * aFace,hb_tag_t aTableTag,bool & aHasGlyph,hb_tag_t aSpecificFeature,bool & aHasGlyphSpecific,uint16_t aGlyph)1042 static void HasLookupRuleWithGlyph(hb_face_t* aFace, hb_tag_t aTableTag,
1043                                    bool& aHasGlyph, hb_tag_t aSpecificFeature,
1044                                    bool& aHasGlyphSpecific, uint16_t aGlyph) {
1045   // iterate over the scripts in the font
1046   uint32_t numScripts, numLangs, script, lang;
1047   hb_set_t* otherLookups = hb_set_create();
1048   hb_set_t* specificFeatureLookups = hb_set_create();
1049   nsTHashSet<uint32_t> specificFeature(1);
1050 
1051   specificFeature.Insert(aSpecificFeature);
1052 
1053   numScripts =
1054       hb_ot_layout_table_get_script_tags(aFace, aTableTag, 0, nullptr, nullptr);
1055 
1056   for (script = 0; script < numScripts; script++) {
1057     // default lang
1058     CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
1059                              specificFeatureLookups, script,
1060                              HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
1061 
1062     // iterate over langs
1063     numLangs = hb_ot_layout_script_get_language_tags(
1064         aFace, HB_OT_TAG_GPOS, script, 0, nullptr, nullptr);
1065     for (lang = 0; lang < numLangs; lang++) {
1066       CollectLookupsByLanguage(aFace, aTableTag, specificFeature, otherLookups,
1067                                specificFeatureLookups, script, lang);
1068     }
1069   }
1070 
1071   // look for the glyph among non-specific feature lookups
1072   hb_set_t* glyphs = hb_set_create();
1073   hb_codepoint_t index = -1;
1074   while (hb_set_next(otherLookups, &index)) {
1075     hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
1076                                        glyphs, nullptr);
1077     if (hb_set_has(glyphs, aGlyph)) {
1078       aHasGlyph = true;
1079       break;
1080     }
1081   }
1082 
1083   // look for the glyph among specific feature lookups
1084   hb_set_clear(glyphs);
1085   index = -1;
1086   while (hb_set_next(specificFeatureLookups, &index)) {
1087     hb_ot_layout_lookup_collect_glyphs(aFace, aTableTag, index, glyphs, glyphs,
1088                                        glyphs, nullptr);
1089     if (hb_set_has(glyphs, aGlyph)) {
1090       aHasGlyphSpecific = true;
1091       break;
1092     }
1093   }
1094 
1095   hb_set_destroy(glyphs);
1096   hb_set_destroy(specificFeatureLookups);
1097   hb_set_destroy(otherLookups);
1098 }
1099 
1100 nsTHashMap<nsUint32HashKey, Script>* gfxFont::sScriptTagToCode = nullptr;
1101 nsTHashSet<uint32_t>* gfxFont::sDefaultFeatures = nullptr;
1102 
HasSubstitution(uint32_t * aBitVector,Script aScript)1103 static inline bool HasSubstitution(uint32_t* aBitVector, Script aScript) {
1104   return (aBitVector[static_cast<uint32_t>(aScript) >> 5] &
1105           (1 << (static_cast<uint32_t>(aScript) & 0x1f))) != 0;
1106 }
1107 
1108 // union of all default substitution features across scripts
1109 static const hb_tag_t defaultFeatures[] = {
1110     HB_TAG('a', 'b', 'v', 'f'), HB_TAG('a', 'b', 'v', 's'),
1111     HB_TAG('a', 'k', 'h', 'n'), HB_TAG('b', 'l', 'w', 'f'),
1112     HB_TAG('b', 'l', 'w', 's'), HB_TAG('c', 'a', 'l', 't'),
1113     HB_TAG('c', 'c', 'm', 'p'), HB_TAG('c', 'f', 'a', 'r'),
1114     HB_TAG('c', 'j', 'c', 't'), HB_TAG('c', 'l', 'i', 'g'),
1115     HB_TAG('f', 'i', 'n', '2'), HB_TAG('f', 'i', 'n', '3'),
1116     HB_TAG('f', 'i', 'n', 'a'), HB_TAG('h', 'a', 'l', 'f'),
1117     HB_TAG('h', 'a', 'l', 'n'), HB_TAG('i', 'n', 'i', 't'),
1118     HB_TAG('i', 's', 'o', 'l'), HB_TAG('l', 'i', 'g', 'a'),
1119     HB_TAG('l', 'j', 'm', 'o'), HB_TAG('l', 'o', 'c', 'l'),
1120     HB_TAG('l', 't', 'r', 'a'), HB_TAG('l', 't', 'r', 'm'),
1121     HB_TAG('m', 'e', 'd', '2'), HB_TAG('m', 'e', 'd', 'i'),
1122     HB_TAG('m', 's', 'e', 't'), HB_TAG('n', 'u', 'k', 't'),
1123     HB_TAG('p', 'r', 'e', 'f'), HB_TAG('p', 'r', 'e', 's'),
1124     HB_TAG('p', 's', 't', 'f'), HB_TAG('p', 's', 't', 's'),
1125     HB_TAG('r', 'c', 'l', 't'), HB_TAG('r', 'l', 'i', 'g'),
1126     HB_TAG('r', 'k', 'r', 'f'), HB_TAG('r', 'p', 'h', 'f'),
1127     HB_TAG('r', 't', 'l', 'a'), HB_TAG('r', 't', 'l', 'm'),
1128     HB_TAG('t', 'j', 'm', 'o'), HB_TAG('v', 'a', 't', 'u'),
1129     HB_TAG('v', 'e', 'r', 't'), HB_TAG('v', 'j', 'm', 'o')};
1130 
CheckForFeaturesInvolvingSpace()1131 void gfxFont::CheckForFeaturesInvolvingSpace() {
1132   mFontEntry->mHasSpaceFeaturesInitialized = true;
1133 
1134   bool log = LOG_FONTINIT_ENABLED();
1135   TimeStamp start;
1136   if (MOZ_UNLIKELY(log)) {
1137     start = TimeStamp::Now();
1138   }
1139 
1140   bool result = false;
1141 
1142   uint32_t spaceGlyph = GetSpaceGlyph();
1143   if (!spaceGlyph) {
1144     return;
1145   }
1146 
1147   hb_face_t* face = GetFontEntry()->GetHBFace();
1148 
1149   // GSUB lookups - examine per script
1150   if (hb_ot_layout_has_substitution(face)) {
1151     // set up the script ==> code hashtable if needed
1152     if (!sScriptTagToCode) {
1153       sScriptTagToCode = new nsTHashMap<nsUint32HashKey, Script>(
1154           size_t(Script::NUM_SCRIPT_CODES));
1155       sScriptTagToCode->InsertOrUpdate(HB_TAG('D', 'F', 'L', 'T'),
1156                                        Script::COMMON);
1157       // Ensure that we don't try to look at script codes beyond what the
1158       // current version of ICU (at runtime -- in case of system ICU)
1159       // knows about.
1160       Script scriptCount =
1161           Script(std::min<int>(u_getIntPropertyMaxValue(UCHAR_SCRIPT) + 1,
1162                                int(Script::NUM_SCRIPT_CODES)));
1163       for (Script s = Script::ARABIC; s < scriptCount;
1164            s = Script(static_cast<int>(s) + 1)) {
1165         hb_script_t script = hb_script_t(GetScriptTagForCode(s));
1166         unsigned int scriptCount = 4;
1167         hb_tag_t scriptTags[4];
1168         hb_ot_tags_from_script_and_language(script, HB_LANGUAGE_INVALID,
1169                                             &scriptCount, scriptTags, nullptr,
1170                                             nullptr);
1171         for (unsigned int i = 0; i < scriptCount; i++) {
1172           sScriptTagToCode->InsertOrUpdate(scriptTags[i], s);
1173         }
1174       }
1175 
1176       uint32_t numDefaultFeatures = ArrayLength(defaultFeatures);
1177       sDefaultFeatures = new nsTHashSet<uint32_t>(numDefaultFeatures);
1178       for (uint32_t i = 0; i < numDefaultFeatures; i++) {
1179         sDefaultFeatures->Insert(defaultFeatures[i]);
1180       }
1181     }
1182 
1183     // iterate over the scripts in the font
1184     hb_tag_t scriptTags[8];
1185 
1186     uint32_t len, offset = 0;
1187     do {
1188       len = ArrayLength(scriptTags);
1189       hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, offset, &len,
1190                                          scriptTags);
1191       for (uint32_t i = 0; i < len; i++) {
1192         bool isDefaultFeature = false;
1193         Script s;
1194         if (!HasLookupRuleWithGlyphByScript(
1195                 face, HB_OT_TAG_GSUB, scriptTags[i], offset + i, spaceGlyph,
1196                 *sDefaultFeatures, isDefaultFeature) ||
1197             !sScriptTagToCode->Get(scriptTags[i], &s)) {
1198           continue;
1199         }
1200         result = true;
1201         uint32_t index = static_cast<uint32_t>(s) >> 5;
1202         uint32_t bit = static_cast<uint32_t>(s) & 0x1f;
1203         if (isDefaultFeature) {
1204           mFontEntry->mDefaultSubSpaceFeatures[index] |= (1 << bit);
1205         } else {
1206           mFontEntry->mNonDefaultSubSpaceFeatures[index] |= (1 << bit);
1207         }
1208       }
1209       offset += len;
1210     } while (len == ArrayLength(scriptTags));
1211   }
1212 
1213   // spaces in default features of default script?
1214   // ==> can't use word cache, skip GPOS analysis
1215   bool canUseWordCache = true;
1216   if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON)) {
1217     canUseWordCache = false;
1218   }
1219 
1220   // GPOS lookups - distinguish kerning from non-kerning features
1221   mFontEntry->mHasSpaceFeaturesKerning = false;
1222   mFontEntry->mHasSpaceFeaturesNonKerning = false;
1223 
1224   if (canUseWordCache && hb_ot_layout_has_positioning(face)) {
1225     bool hasKerning = false, hasNonKerning = false;
1226     HasLookupRuleWithGlyph(face, HB_OT_TAG_GPOS, hasNonKerning,
1227                            HB_TAG('k', 'e', 'r', 'n'), hasKerning, spaceGlyph);
1228     if (hasKerning || hasNonKerning) {
1229       result = true;
1230     }
1231     mFontEntry->mHasSpaceFeaturesKerning = hasKerning;
1232     mFontEntry->mHasSpaceFeaturesNonKerning = hasNonKerning;
1233   }
1234 
1235   hb_face_destroy(face);
1236   mFontEntry->mHasSpaceFeatures = result;
1237 
1238   if (MOZ_UNLIKELY(log)) {
1239     TimeDuration elapsed = TimeStamp::Now() - start;
1240     LOG_FONTINIT(
1241         ("(fontinit-spacelookups) font: %s - "
1242          "subst default: %8.8x %8.8x %8.8x %8.8x "
1243          "subst non-default: %8.8x %8.8x %8.8x %8.8x "
1244          "kerning: %s non-kerning: %s time: %6.3f\n",
1245          mFontEntry->Name().get(), mFontEntry->mDefaultSubSpaceFeatures[3],
1246          mFontEntry->mDefaultSubSpaceFeatures[2],
1247          mFontEntry->mDefaultSubSpaceFeatures[1],
1248          mFontEntry->mDefaultSubSpaceFeatures[0],
1249          mFontEntry->mNonDefaultSubSpaceFeatures[3],
1250          mFontEntry->mNonDefaultSubSpaceFeatures[2],
1251          mFontEntry->mNonDefaultSubSpaceFeatures[1],
1252          mFontEntry->mNonDefaultSubSpaceFeatures[0],
1253          (mFontEntry->mHasSpaceFeaturesKerning ? "true" : "false"),
1254          (mFontEntry->mHasSpaceFeaturesNonKerning ? "true" : "false"),
1255          elapsed.ToMilliseconds()));
1256   }
1257 }
1258 
HasSubstitutionRulesWithSpaceLookups(Script aRunScript)1259 bool gfxFont::HasSubstitutionRulesWithSpaceLookups(Script aRunScript) {
1260   NS_ASSERTION(GetFontEntry()->mHasSpaceFeaturesInitialized,
1261                "need to initialize space lookup flags");
1262   NS_ASSERTION(aRunScript < Script::NUM_SCRIPT_CODES, "weird script code");
1263   if (aRunScript == Script::INVALID || aRunScript >= Script::NUM_SCRIPT_CODES) {
1264     return false;
1265   }
1266 
1267   // default features have space lookups ==> true
1268   if (HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, Script::COMMON) ||
1269       HasSubstitution(mFontEntry->mDefaultSubSpaceFeatures, aRunScript)) {
1270     return true;
1271   }
1272 
1273   // non-default features have space lookups and some type of
1274   // font feature, in font or style is specified ==> true
1275   if ((HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures,
1276                        Script::COMMON) ||
1277        HasSubstitution(mFontEntry->mNonDefaultSubSpaceFeatures, aRunScript)) &&
1278       (!mStyle.featureSettings.IsEmpty() ||
1279        !mFontEntry->mFeatureSettings.IsEmpty())) {
1280     return true;
1281   }
1282 
1283   return false;
1284 }
1285 
SpaceMayParticipateInShaping(Script aRunScript)1286 tainted_boolean_hint gfxFont::SpaceMayParticipateInShaping(Script aRunScript) {
1287   // avoid checking fonts known not to include default space-dependent features
1288   if (MOZ_UNLIKELY(mFontEntry->mSkipDefaultFeatureSpaceCheck)) {
1289     if (!mKerningSet && mStyle.featureSettings.IsEmpty() &&
1290         mFontEntry->mFeatureSettings.IsEmpty()) {
1291       return false;
1292     }
1293   }
1294 
1295   if (FontCanSupportGraphite()) {
1296     if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1297       return mFontEntry->HasGraphiteSpaceContextuals();
1298     }
1299   }
1300 
1301   // We record the presence of space-dependent features in the font entry
1302   // so that subsequent instantiations for the same font face won't
1303   // require us to re-check the tables; however, the actual check is done
1304   // by gfxFont because not all font entry subclasses know how to create
1305   // a harfbuzz face for introspection.
1306   if (!mFontEntry->mHasSpaceFeaturesInitialized) {
1307     CheckForFeaturesInvolvingSpace();
1308   }
1309 
1310   if (!mFontEntry->mHasSpaceFeatures) {
1311     return false;
1312   }
1313 
1314   // if font has substitution rules or non-kerning positioning rules
1315   // that involve spaces, bypass
1316   if (HasSubstitutionRulesWithSpaceLookups(aRunScript) ||
1317       mFontEntry->mHasSpaceFeaturesNonKerning) {
1318     return true;
1319   }
1320 
1321   // if kerning explicitly enabled/disabled via font-feature-settings or
1322   // font-kerning and kerning rules use spaces, only bypass when enabled
1323   if (mKerningSet && mFontEntry->mHasSpaceFeaturesKerning) {
1324     return mKerningEnabled;
1325   }
1326 
1327   return false;
1328 }
1329 
SupportsFeature(Script aScript,uint32_t aFeatureTag)1330 bool gfxFont::SupportsFeature(Script aScript, uint32_t aFeatureTag) {
1331   if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1332     return GetFontEntry()->SupportsGraphiteFeature(aFeatureTag);
1333   }
1334   return GetFontEntry()->SupportsOpenTypeFeature(aScript, aFeatureTag);
1335 }
1336 
SupportsVariantCaps(Script aScript,uint32_t aVariantCaps,bool & aFallbackToSmallCaps,bool & aSyntheticLowerToSmallCaps,bool & aSyntheticUpperToSmallCaps)1337 bool gfxFont::SupportsVariantCaps(Script aScript, uint32_t aVariantCaps,
1338                                   bool& aFallbackToSmallCaps,
1339                                   bool& aSyntheticLowerToSmallCaps,
1340                                   bool& aSyntheticUpperToSmallCaps) {
1341   bool ok = true;  // cases without fallback are fine
1342   aFallbackToSmallCaps = false;
1343   aSyntheticLowerToSmallCaps = false;
1344   aSyntheticUpperToSmallCaps = false;
1345   switch (aVariantCaps) {
1346     case NS_FONT_VARIANT_CAPS_SMALLCAPS:
1347       ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
1348       if (!ok) {
1349         aSyntheticLowerToSmallCaps = true;
1350       }
1351       break;
1352     case NS_FONT_VARIANT_CAPS_ALLSMALL:
1353       ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
1354            SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
1355       if (!ok) {
1356         aSyntheticLowerToSmallCaps = true;
1357         aSyntheticUpperToSmallCaps = true;
1358       }
1359       break;
1360     case NS_FONT_VARIANT_CAPS_PETITECAPS:
1361       ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p'));
1362       if (!ok) {
1363         ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p'));
1364         aFallbackToSmallCaps = ok;
1365       }
1366       if (!ok) {
1367         aSyntheticLowerToSmallCaps = true;
1368       }
1369       break;
1370     case NS_FONT_VARIANT_CAPS_ALLPETITE:
1371       ok = SupportsFeature(aScript, HB_TAG('p', 'c', 'a', 'p')) &&
1372            SupportsFeature(aScript, HB_TAG('c', '2', 'p', 'c'));
1373       if (!ok) {
1374         ok = SupportsFeature(aScript, HB_TAG('s', 'm', 'c', 'p')) &&
1375              SupportsFeature(aScript, HB_TAG('c', '2', 's', 'c'));
1376         aFallbackToSmallCaps = ok;
1377       }
1378       if (!ok) {
1379         aSyntheticLowerToSmallCaps = true;
1380         aSyntheticUpperToSmallCaps = true;
1381       }
1382       break;
1383     default:
1384       break;
1385   }
1386 
1387   NS_ASSERTION(
1388       !(ok && (aSyntheticLowerToSmallCaps || aSyntheticUpperToSmallCaps)),
1389       "shouldn't use synthetic features if we found real ones");
1390 
1391   NS_ASSERTION(!(!ok && aFallbackToSmallCaps),
1392                "if we found a usable fallback, that counts as ok");
1393 
1394   return ok;
1395 }
1396 
SupportsSubSuperscript(uint32_t aSubSuperscript,const uint8_t * aString,uint32_t aLength,Script aRunScript)1397 bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
1398                                      const uint8_t* aString, uint32_t aLength,
1399                                      Script aRunScript) {
1400   NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aString),
1401                                        aLength);
1402   return SupportsSubSuperscript(aSubSuperscript, unicodeString.get(), aLength,
1403                                 aRunScript);
1404 }
1405 
SupportsSubSuperscript(uint32_t aSubSuperscript,const char16_t * aString,uint32_t aLength,Script aRunScript)1406 bool gfxFont::SupportsSubSuperscript(uint32_t aSubSuperscript,
1407                                      const char16_t* aString, uint32_t aLength,
1408                                      Script aRunScript) {
1409   NS_ASSERTION(aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER ||
1410                    aSubSuperscript == NS_FONT_VARIANT_POSITION_SUB,
1411                "unknown value of font-variant-position");
1412 
1413   uint32_t feature = aSubSuperscript == NS_FONT_VARIANT_POSITION_SUPER
1414                          ? HB_TAG('s', 'u', 'p', 's')
1415                          : HB_TAG('s', 'u', 'b', 's');
1416 
1417   if (!SupportsFeature(aRunScript, feature)) {
1418     return false;
1419   }
1420 
1421   // xxx - for graphite, don't really know how to sniff lookups so bail
1422   if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1423     return true;
1424   }
1425 
1426   if (!mHarfBuzzShaper) {
1427     mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
1428   }
1429   gfxHarfBuzzShaper* shaper =
1430       static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
1431   if (!shaper->Initialize()) {
1432     return false;
1433   }
1434 
1435   // get the hbset containing input glyphs for the feature
1436   const hb_set_t* inputGlyphs =
1437       mFontEntry->InputsForOpenTypeFeature(aRunScript, feature);
1438 
1439   // create an hbset containing default glyphs for the script run
1440   hb_set_t* defaultGlyphsInRun = hb_set_create();
1441 
1442   // for each character, get the glyph id
1443   for (uint32_t i = 0; i < aLength; i++) {
1444     uint32_t ch = aString[i];
1445 
1446     if (i + 1 < aLength && NS_IS_SURROGATE_PAIR(ch, aString[i + 1])) {
1447       i++;
1448       ch = SURROGATE_TO_UCS4(ch, aString[i]);
1449     }
1450 
1451     if (ch == 0xa0) {
1452       ch = ' ';
1453     }
1454 
1455     hb_codepoint_t gid = shaper->GetNominalGlyph(ch);
1456     hb_set_add(defaultGlyphsInRun, gid);
1457   }
1458 
1459   // intersect with input glyphs, if size is not the same ==> fallback
1460   uint32_t origSize = hb_set_get_population(defaultGlyphsInRun);
1461   hb_set_intersect(defaultGlyphsInRun, inputGlyphs);
1462   uint32_t intersectionSize = hb_set_get_population(defaultGlyphsInRun);
1463   hb_set_destroy(defaultGlyphsInRun);
1464 
1465   return origSize == intersectionSize;
1466 }
1467 
FeatureWillHandleChar(Script aRunScript,uint32_t aFeature,uint32_t aUnicode)1468 bool gfxFont::FeatureWillHandleChar(Script aRunScript, uint32_t aFeature,
1469                                     uint32_t aUnicode) {
1470   if (!SupportsFeature(aRunScript, aFeature)) {
1471     return false;
1472   }
1473 
1474   // xxx - for graphite, don't really know how to sniff lookups so bail
1475   if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
1476     return true;
1477   }
1478 
1479   if (!mHarfBuzzShaper) {
1480     mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
1481   }
1482   gfxHarfBuzzShaper* shaper =
1483       static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
1484   if (!shaper->Initialize()) {
1485     return false;
1486   }
1487 
1488   // get the hbset containing input glyphs for the feature
1489   const hb_set_t* inputGlyphs =
1490       mFontEntry->InputsForOpenTypeFeature(aRunScript, aFeature);
1491 
1492   if (aUnicode == 0xa0) {
1493     aUnicode = ' ';
1494   }
1495 
1496   hb_codepoint_t gid = shaper->GetNominalGlyph(aUnicode);
1497   return hb_set_has(inputGlyphs, gid);
1498 }
1499 
HasFeatureSet(uint32_t aFeature,bool & aFeatureOn)1500 bool gfxFont::HasFeatureSet(uint32_t aFeature, bool& aFeatureOn) {
1501   aFeatureOn = false;
1502 
1503   if (mStyle.featureSettings.IsEmpty() &&
1504       GetFontEntry()->mFeatureSettings.IsEmpty()) {
1505     return false;
1506   }
1507 
1508   // add feature values from font
1509   bool featureSet = false;
1510   uint32_t i, count;
1511 
1512   nsTArray<gfxFontFeature>& fontFeatures = GetFontEntry()->mFeatureSettings;
1513   count = fontFeatures.Length();
1514   for (i = 0; i < count; i++) {
1515     const gfxFontFeature& feature = fontFeatures.ElementAt(i);
1516     if (feature.mTag == aFeature) {
1517       featureSet = true;
1518       aFeatureOn = (feature.mValue != 0);
1519     }
1520   }
1521 
1522   // add feature values from style rules
1523   nsTArray<gfxFontFeature>& styleFeatures = mStyle.featureSettings;
1524   count = styleFeatures.Length();
1525   for (i = 0; i < count; i++) {
1526     const gfxFontFeature& feature = styleFeatures.ElementAt(i);
1527     if (feature.mTag == aFeature) {
1528       featureSet = true;
1529       aFeatureOn = (feature.mValue != 0);
1530     }
1531   }
1532 
1533   return featureSet;
1534 }
1535 
InitializeScaledFont()1536 void gfxFont::InitializeScaledFont() {
1537   if (!mAzureScaledFont) {
1538     return;
1539   }
1540 
1541   float angle = AngleForSyntheticOblique();
1542   if (angle != 0.0f) {
1543     mAzureScaledFont->SetSyntheticObliqueAngle(angle);
1544   }
1545 }
1546 
1547 /**
1548  * A helper function in case we need to do any rounding or other
1549  * processing here.
1550  */
1551 #define ToDeviceUnits(aAppUnits, aDevUnitsPerAppUnit) \
1552   (double(aAppUnits) * double(aDevUnitsPerAppUnit))
1553 
Get2DAAMode(gfxFont::AntialiasOption aAAOption)1554 static AntialiasMode Get2DAAMode(gfxFont::AntialiasOption aAAOption) {
1555   switch (aAAOption) {
1556     case gfxFont::kAntialiasSubpixel:
1557       return AntialiasMode::SUBPIXEL;
1558     case gfxFont::kAntialiasGrayscale:
1559       return AntialiasMode::GRAY;
1560     case gfxFont::kAntialiasNone:
1561       return AntialiasMode::NONE;
1562     default:
1563       return AntialiasMode::DEFAULT;
1564   }
1565 }
1566 
1567 class GlyphBufferAzure {
1568 #define AUTO_BUFFER_SIZE (2048 / sizeof(Glyph))
1569 
1570   typedef mozilla::image::imgDrawingParams imgDrawingParams;
1571 
1572  public:
GlyphBufferAzure(const TextRunDrawParams & aRunParams,const FontDrawParams & aFontParams)1573   GlyphBufferAzure(const TextRunDrawParams& aRunParams,
1574                    const FontDrawParams& aFontParams)
1575       : mRunParams(aRunParams),
1576         mFontParams(aFontParams),
1577         mBuffer(*mAutoBuffer.addr()),
1578         mBufSize(AUTO_BUFFER_SIZE),
1579         mCapacity(0),
1580         mNumGlyphs(0) {}
1581 
~GlyphBufferAzure()1582   ~GlyphBufferAzure() {
1583     if (mNumGlyphs > 0) {
1584       FlushGlyphs();
1585     }
1586 
1587     if (mBuffer != *mAutoBuffer.addr()) {
1588       free(mBuffer);
1589     }
1590   }
1591 
1592   // Ensure the buffer has enough space for aGlyphCount glyphs to be added,
1593   // considering the supplied strike multipler aStrikeCount.
1594   // This MUST be called before OutputGlyph is used to actually store glyph
1595   // records in the buffer. It may be called repeated to add further capacity
1596   // in case we don't know up-front exactly what will be needed.
AddCapacity(uint32_t aGlyphCount,uint32_t aStrikeCount)1597   void AddCapacity(uint32_t aGlyphCount, uint32_t aStrikeCount) {
1598     // Calculate the new capacity and ensure it will fit within the maximum
1599     // allowed capacity.
1600     static const uint64_t kMaxCapacity = 64 * 1024;
1601     mCapacity = uint32_t(std::min(
1602         kMaxCapacity,
1603         uint64_t(mCapacity) + uint64_t(aGlyphCount) * uint64_t(aStrikeCount)));
1604     // See if the required capacity fits within the already-allocated space
1605     if (mCapacity <= mBufSize) {
1606       return;
1607     }
1608     // We need to grow the buffer: determine a new size, allocate, and
1609     // copy the existing data over if we didn't use realloc (which would
1610     // do it automatically).
1611     mBufSize = std::max(mCapacity, mBufSize * 2);
1612     if (mBuffer == *mAutoBuffer.addr()) {
1613       // switching from autobuffer to malloc, so we need to copy
1614       mBuffer = reinterpret_cast<Glyph*>(moz_xmalloc(mBufSize * sizeof(Glyph)));
1615       std::memcpy(mBuffer, *mAutoBuffer.addr(), mNumGlyphs * sizeof(Glyph));
1616     } else {
1617       mBuffer = reinterpret_cast<Glyph*>(
1618           moz_xrealloc(mBuffer, mBufSize * sizeof(Glyph)));
1619     }
1620   }
1621 
OutputGlyph(uint32_t aGlyphID,const gfx::Point & aPt)1622   void OutputGlyph(uint32_t aGlyphID, const gfx::Point& aPt) {
1623     // If the buffer is full, flush to make room for the new glyph.
1624     if (mNumGlyphs >= mCapacity) {
1625       // Check that AddCapacity has been used appropriately!
1626       MOZ_ASSERT(mCapacity > 0 && mNumGlyphs == mCapacity);
1627       Flush();
1628     }
1629     Glyph* glyph = mBuffer + mNumGlyphs++;
1630     glyph->mIndex = aGlyphID;
1631     glyph->mPosition = aPt;
1632   }
1633 
Flush()1634   void Flush() {
1635     if (mNumGlyphs > 0) {
1636       FlushGlyphs();
1637       mNumGlyphs = 0;
1638     }
1639   }
1640 
1641   const TextRunDrawParams& mRunParams;
1642   const FontDrawParams& mFontParams;
1643 
1644  private:
GetStrokeMode(DrawMode aMode)1645   static DrawMode GetStrokeMode(DrawMode aMode) {
1646     return aMode & (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH);
1647   }
1648 
1649   // Render the buffered glyphs to the draw target.
FlushGlyphs()1650   void FlushGlyphs() {
1651     gfx::GlyphBuffer buf;
1652     buf.mGlyphs = mBuffer;
1653     buf.mNumGlyphs = mNumGlyphs;
1654 
1655     const gfxContext::AzureState& state = mRunParams.context->CurrentState();
1656 
1657     // Draw stroke first if the UNDERNEATH flag is set in drawMode.
1658     if (mRunParams.strokeOpts &&
1659         GetStrokeMode(mRunParams.drawMode) ==
1660             (DrawMode::GLYPH_STROKE | DrawMode::GLYPH_STROKE_UNDERNEATH)) {
1661       DrawStroke(state, buf);
1662     }
1663 
1664     if (mRunParams.drawMode & DrawMode::GLYPH_FILL) {
1665       if (state.pattern || mFontParams.contextPaint) {
1666         Pattern* pat;
1667 
1668         RefPtr<gfxPattern> fillPattern;
1669         if (mFontParams.contextPaint) {
1670           imgDrawingParams imgParams;
1671           fillPattern = mFontParams.contextPaint->GetFillPattern(
1672               mRunParams.context->GetDrawTarget(),
1673               mRunParams.context->CurrentMatrixDouble(), imgParams);
1674         }
1675         if (!fillPattern) {
1676           if (state.pattern) {
1677             RefPtr<gfxPattern> statePattern =
1678                 mRunParams.context->CurrentState().pattern;
1679             pat = statePattern->GetPattern(mRunParams.dt,
1680                                            state.patternTransformChanged
1681                                                ? &state.patternTransform
1682                                                : nullptr);
1683           } else {
1684             pat = nullptr;
1685           }
1686         } else {
1687           pat = fillPattern->GetPattern(mRunParams.dt);
1688         }
1689 
1690         if (pat) {
1691           mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf, *pat,
1692                                     mFontParams.drawOptions);
1693         }
1694       } else {
1695         mRunParams.dt->FillGlyphs(mFontParams.scaledFont, buf,
1696                                   ColorPattern(state.color),
1697                                   mFontParams.drawOptions);
1698       }
1699     }
1700 
1701     // Draw stroke if the UNDERNEATH flag is not set.
1702     if (mRunParams.strokeOpts &&
1703         GetStrokeMode(mRunParams.drawMode) == DrawMode::GLYPH_STROKE) {
1704       DrawStroke(state, buf);
1705     }
1706 
1707     if (mRunParams.drawMode & DrawMode::GLYPH_PATH) {
1708       mRunParams.context->EnsurePathBuilder();
1709       Matrix mat = mRunParams.dt->GetTransform();
1710       mFontParams.scaledFont->CopyGlyphsToBuilder(
1711           buf, mRunParams.context->mPathBuilder, &mat);
1712     }
1713   }
1714 
DrawStroke(const gfxContext::AzureState & aState,gfx::GlyphBuffer & aBuffer)1715   void DrawStroke(const gfxContext::AzureState& aState,
1716                   gfx::GlyphBuffer& aBuffer) {
1717     if (mRunParams.textStrokePattern) {
1718       Pattern* pat = mRunParams.textStrokePattern->GetPattern(
1719           mRunParams.dt,
1720           aState.patternTransformChanged ? &aState.patternTransform : nullptr);
1721 
1722       if (pat) {
1723         FlushStroke(aBuffer, *pat);
1724       }
1725     } else {
1726       FlushStroke(aBuffer,
1727                   ColorPattern(ToDeviceColor(mRunParams.textStrokeColor)));
1728     }
1729   }
1730 
FlushStroke(gfx::GlyphBuffer & aBuf,const Pattern & aPattern)1731   void FlushStroke(gfx::GlyphBuffer& aBuf, const Pattern& aPattern) {
1732     mRunParams.dt->StrokeGlyphs(mFontParams.scaledFont, aBuf, aPattern,
1733                                 *mRunParams.strokeOpts,
1734                                 mFontParams.drawOptions);
1735   }
1736 
1737   // We use an "inline" buffer automatically allocated (on the stack) as part
1738   // of the GlyphBufferAzure object to hold the glyphs in most cases, falling
1739   // back to a separately-allocated heap buffer if the count of buffered
1740   // glyphs gets too big.
1741   //
1742   // This is basically a rudimentary AutoTArray; so why not use AutoTArray
1743   // itself?
1744   //
1745   // If we used an AutoTArray, we'd want to avoid using SetLength or
1746   // AppendElements to allocate the space we actually need, because those
1747   // methods would default-construct the new elements.
1748   //
1749   // Could we use SetCapacity to reserve the necessary buffer space without
1750   // default-constructing all the Glyph records? No, because of a failure
1751   // that could occur when we need to grow the buffer, which happens when we
1752   // encounter a DetailedGlyph in the textrun that refers to a sequence of
1753   // several real glyphs. At that point, we need to add some extra capacity
1754   // to the buffer we initially allocated based on the length of the textrun
1755   // range we're rendering.
1756   //
1757   // This buffer growth would work fine as long as it still fits within the
1758   // array's inline buffer (we just use a bit more of it), or if the buffer
1759   // was already heap-allocated (in which case AutoTArray will use realloc(),
1760   // preserving its contents). But a problem will arise when the initial
1761   // capacity we allocated (based on the length of the run) fits within the
1762   // array's inline buffer, but subsequently we need to extend the buffer
1763   // beyond the inline buffer size, so we reallocate to the heap. Because we
1764   // haven't "officially" filled the array with SetLength or AppendElements,
1765   // its mLength is still zero; as far as it's concerned the buffer is just
1766   // uninitialized space, and when it switches to use a malloc'd buffer it
1767   // won't copy the existing contents.
1768 
1769   // Allocate space for a buffer of Glyph records, without initializing them.
1770   AlignedStorage2<Glyph[AUTO_BUFFER_SIZE]> mAutoBuffer;
1771 
1772   // Pointer to the buffer we're currently using -- initially mAutoBuffer,
1773   // but may be changed to a malloc'd buffer, in which case that buffer must
1774   // be free'd on destruction.
1775   Glyph* mBuffer;
1776 
1777   uint32_t mBufSize;    // size of allocated buffer; capacity can grow to
1778                         // this before reallocation is needed
1779   uint32_t mCapacity;   // amount of buffer size reserved
1780   uint32_t mNumGlyphs;  // number of glyphs actually present in the buffer
1781 
1782 #undef AUTO_BUFFER_SIZE
1783 };
1784 
1785 // Bug 674909. When synthetic bolding text by drawing twice, need to
1786 // render using a pixel offset in device pixels, otherwise text
1787 // doesn't appear bolded, it appears as if a bad text shadow exists
1788 // when a non-identity transform exists.  Use an offset factor so that
1789 // the second draw occurs at a constant offset in device pixels.
1790 
CalcXScale(DrawTarget * aDrawTarget)1791 gfx::Float gfxFont::CalcXScale(DrawTarget* aDrawTarget) {
1792   // determine magnitude of a 1px x offset in device space
1793   Size t = aDrawTarget->GetTransform().TransformSize(Size(1.0, 0.0));
1794   if (t.width == 1.0 && t.height == 0.0) {
1795     // short-circuit the most common case to avoid sqrt() and division
1796     return 1.0;
1797   }
1798 
1799   gfx::Float m = sqrtf(t.width * t.width + t.height * t.height);
1800 
1801   NS_ASSERTION(m != 0.0, "degenerate transform while synthetic bolding");
1802   if (m == 0.0) {
1803     return 0.0;  // effectively disables offset
1804   }
1805 
1806   // scale factor so that offsets are 1px in device pixels
1807   return 1.0 / m;
1808 }
1809 
1810 // Draw a run of CharacterGlyph records from the given offset in aShapedText.
1811 // Returns true if glyph paths were actually emitted.
1812 template <gfxFont::FontComplexityT FC, gfxFont::SpacingT S>
DrawGlyphs(const gfxShapedText * aShapedText,uint32_t aOffset,uint32_t aCount,gfx::Point * aPt,const gfx::Matrix * aOffsetMatrix,GlyphBufferAzure & aBuffer)1813 bool gfxFont::DrawGlyphs(const gfxShapedText* aShapedText,
1814                          uint32_t aOffset,  // offset in the textrun
1815                          uint32_t aCount,   // length of run to draw
1816                          gfx::Point* aPt,
1817                          const gfx::Matrix* aOffsetMatrix,  // may be null
1818                          GlyphBufferAzure& aBuffer) {
1819   float& inlineCoord = aBuffer.mFontParams.isVerticalFont ? aPt->y : aPt->x;
1820 
1821   const gfxShapedText::CompressedGlyph* glyphData =
1822       &aShapedText->GetCharacterGlyphs()[aOffset];
1823 
1824   if (S == SpacingT::HasSpacing) {
1825     float space = aBuffer.mRunParams.spacing[0].mBefore *
1826                   aBuffer.mFontParams.advanceDirection;
1827     inlineCoord += space;
1828   }
1829 
1830   // Allocate buffer space for the run, assuming all simple glyphs.
1831   uint32_t capacityMult = 1 + aBuffer.mFontParams.extraStrikes;
1832   aBuffer.AddCapacity(aCount, capacityMult);
1833 
1834   bool emittedGlyphs = false;
1835 
1836   for (uint32_t i = 0; i < aCount; ++i, ++glyphData) {
1837     if (glyphData->IsSimpleGlyph()) {
1838       float advance =
1839           glyphData->GetSimpleAdvance() * aBuffer.mFontParams.advanceDirection;
1840       if (aBuffer.mRunParams.isRTL) {
1841         inlineCoord += advance;
1842       }
1843       DrawOneGlyph<FC>(glyphData->GetSimpleGlyph(), *aPt, aBuffer,
1844                        &emittedGlyphs);
1845       if (!aBuffer.mRunParams.isRTL) {
1846         inlineCoord += advance;
1847       }
1848     } else {
1849       uint32_t glyphCount = glyphData->GetGlyphCount();
1850       if (glyphCount > 0) {
1851         // Add extra buffer capacity to allow for multiple-glyph entry.
1852         aBuffer.AddCapacity(glyphCount - 1, capacityMult);
1853         const gfxShapedText::DetailedGlyph* details =
1854             aShapedText->GetDetailedGlyphs(aOffset + i);
1855         MOZ_ASSERT(details, "missing DetailedGlyph!");
1856         for (uint32_t j = 0; j < glyphCount; ++j, ++details) {
1857           float advance =
1858               details->mAdvance * aBuffer.mFontParams.advanceDirection;
1859           if (aBuffer.mRunParams.isRTL) {
1860             inlineCoord += advance;
1861           }
1862           if (glyphData->IsMissing()) {
1863             if (!DrawMissingGlyph(aBuffer.mRunParams, aBuffer.mFontParams,
1864                                   details, *aPt)) {
1865               return false;
1866             }
1867           } else {
1868             gfx::Point glyphPt(
1869                 *aPt + (aOffsetMatrix
1870                             ? aOffsetMatrix->TransformPoint(details->mOffset)
1871                             : details->mOffset));
1872             DrawOneGlyph<FC>(details->mGlyphID, glyphPt, aBuffer,
1873                              &emittedGlyphs);
1874           }
1875           if (!aBuffer.mRunParams.isRTL) {
1876             inlineCoord += advance;
1877           }
1878         }
1879       }
1880     }
1881 
1882     if (S == SpacingT::HasSpacing) {
1883       float space = aBuffer.mRunParams.spacing[i].mAfter;
1884       if (i + 1 < aCount) {
1885         space += aBuffer.mRunParams.spacing[i + 1].mBefore;
1886       }
1887       space *= aBuffer.mFontParams.advanceDirection;
1888       inlineCoord += space;
1889     }
1890   }
1891 
1892   return emittedGlyphs;
1893 }
1894 
1895 // Draw an individual glyph at a specific location.
1896 // *aPt is the glyph position in appUnits; it is converted to device
1897 // coordinates (devPt) here.
1898 template <gfxFont::FontComplexityT FC>
DrawOneGlyph(uint32_t aGlyphID,const gfx::Point & aPt,GlyphBufferAzure & aBuffer,bool * aEmittedGlyphs)1899 void gfxFont::DrawOneGlyph(uint32_t aGlyphID, const gfx::Point& aPt,
1900                            GlyphBufferAzure& aBuffer, bool* aEmittedGlyphs) {
1901   const TextRunDrawParams& runParams(aBuffer.mRunParams);
1902 
1903   gfx::Point devPt(ToDeviceUnits(aPt.x, runParams.devPerApp),
1904                    ToDeviceUnits(aPt.y, runParams.devPerApp));
1905 
1906   if (FC == FontComplexityT::ComplexFont) {
1907     const FontDrawParams& fontParams(aBuffer.mFontParams);
1908 
1909     auto* textDrawer = runParams.context->GetTextDrawer();
1910 
1911     gfxContextMatrixAutoSaveRestore matrixRestore;
1912 
1913     if (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
1914         !textDrawer) {
1915       // We have to flush each glyph individually when doing
1916       // synthetic-oblique for vertical-upright text, because
1917       // the skew transform needs to be applied to a separate
1918       // origin for each glyph, not once for the whole run.
1919       aBuffer.Flush();
1920       matrixRestore.SetContext(runParams.context);
1921       gfx::Point skewPt(
1922           devPt.x + GetMetrics(nsFontMetrics::eVertical).emHeight / 2, devPt.y);
1923       gfx::Matrix mat =
1924           runParams.context->CurrentMatrix()
1925               .PreTranslate(skewPt)
1926               .PreMultiply(gfx::Matrix(1, fontParams.obliqueSkew, 0, 1, 0, 0))
1927               .PreTranslate(-skewPt);
1928       runParams.context->SetMatrix(mat);
1929     }
1930 
1931     if (fontParams.haveSVGGlyphs) {
1932       if (!runParams.paintSVGGlyphs) {
1933         return;
1934       }
1935       NS_WARNING_ASSERTION(
1936           runParams.drawMode != DrawMode::GLYPH_PATH,
1937           "Rendering SVG glyph despite request for glyph path");
1938       if (RenderSVGGlyph(runParams.context, textDrawer, devPt, aGlyphID,
1939                          fontParams.contextPaint, runParams.callbacks,
1940                          *aEmittedGlyphs)) {
1941         return;
1942       }
1943     }
1944 
1945     if (fontParams.haveColorGlyphs &&
1946         !gfxPlatform::GetPlatform()->HasNativeColrFontSupport() &&
1947         RenderColorGlyph(runParams.dt, runParams.context, textDrawer,
1948                          fontParams.scaledFont, fontParams.drawOptions, devPt,
1949                          aGlyphID)) {
1950       return;
1951     }
1952 
1953     aBuffer.OutputGlyph(aGlyphID, devPt);
1954 
1955     // Synthetic bolding (if required) by multi-striking.
1956     for (int32_t i = 0; i < fontParams.extraStrikes; ++i) {
1957       if (fontParams.isVerticalFont) {
1958         devPt.y += fontParams.synBoldOnePixelOffset;
1959       } else {
1960         devPt.x += fontParams.synBoldOnePixelOffset;
1961       }
1962       aBuffer.OutputGlyph(aGlyphID, devPt);
1963     }
1964 
1965     if (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
1966         !textDrawer) {
1967       aBuffer.Flush();
1968     }
1969   } else {
1970     aBuffer.OutputGlyph(aGlyphID, devPt);
1971   }
1972 
1973   *aEmittedGlyphs = true;
1974 }
1975 
DrawMissingGlyph(const TextRunDrawParams & aRunParams,const FontDrawParams & aFontParams,const gfxShapedText::DetailedGlyph * aDetails,const gfx::Point & aPt)1976 bool gfxFont::DrawMissingGlyph(const TextRunDrawParams& aRunParams,
1977                                const FontDrawParams& aFontParams,
1978                                const gfxShapedText::DetailedGlyph* aDetails,
1979                                const gfx::Point& aPt) {
1980   // Default-ignorable chars will have zero advance width;
1981   // we don't have to draw the hexbox for them.
1982   float advance = aDetails->mAdvance;
1983   if (aRunParams.drawMode != DrawMode::GLYPH_PATH && advance > 0) {
1984     auto* textDrawer = aRunParams.context->GetTextDrawer();
1985     const Matrix* matPtr = nullptr;
1986     Matrix mat;
1987     if (textDrawer) {
1988       // Generate an orientation matrix for the current writing mode
1989       wr::FontInstanceFlags flags = textDrawer->GetWRGlyphFlags();
1990       if (flags & wr::FontInstanceFlags::TRANSPOSE) {
1991         std::swap(mat._11, mat._12);
1992         std::swap(mat._21, mat._22);
1993       }
1994       mat.PostScale(flags & wr::FontInstanceFlags::FLIP_X ? -1.0f : 1.0f,
1995                     flags & wr::FontInstanceFlags::FLIP_Y ? -1.0f : 1.0f);
1996       matPtr = &mat;
1997     }
1998 
1999     Point pt(Float(ToDeviceUnits(aPt.x, aRunParams.devPerApp)),
2000              Float(ToDeviceUnits(aPt.y, aRunParams.devPerApp)));
2001     Float advanceDevUnits = Float(ToDeviceUnits(advance, aRunParams.devPerApp));
2002     Float height = GetMetrics(nsFontMetrics::eHorizontal).maxAscent;
2003     // Horizontally center if drawing vertically upright with no sideways
2004     // transform.
2005     Rect glyphRect =
2006         aFontParams.isVerticalFont && !mat.HasNonAxisAlignedTransform()
2007             ? Rect(pt.x - height / 2, pt.y, height, advanceDevUnits)
2008             : Rect(pt.x, pt.y - height, advanceDevUnits, height);
2009 
2010     // If there's a fake-italic skew in effect as part
2011     // of the drawTarget's transform, we need to undo
2012     // this before drawing the hexbox. (Bug 983985)
2013     gfxContextMatrixAutoSaveRestore matrixRestore;
2014     if (aFontParams.obliqueSkew != 0.0f && !aFontParams.isVerticalFont &&
2015         !textDrawer) {
2016       matrixRestore.SetContext(aRunParams.context);
2017       gfx::Matrix mat =
2018           aRunParams.context->CurrentMatrix()
2019               .PreTranslate(pt)
2020               .PreMultiply(gfx::Matrix(1, 0, aFontParams.obliqueSkew, 1, 0, 0))
2021               .PreTranslate(-pt);
2022       aRunParams.context->SetMatrix(mat);
2023     }
2024 
2025     gfxFontMissingGlyphs::DrawMissingGlyph(aDetails->mGlyphID, glyphRect,
2026                                            *aRunParams.dt,
2027                                            PatternFromState(aRunParams.context),
2028                                            1.0 / aRunParams.devPerApp, matPtr);
2029   }
2030   return true;
2031 }
2032 
2033 // This method is mostly parallel to DrawGlyphs.
DrawEmphasisMarks(const gfxTextRun * aShapedText,gfx::Point * aPt,uint32_t aOffset,uint32_t aCount,const EmphasisMarkDrawParams & aParams)2034 void gfxFont::DrawEmphasisMarks(const gfxTextRun* aShapedText, gfx::Point* aPt,
2035                                 uint32_t aOffset, uint32_t aCount,
2036                                 const EmphasisMarkDrawParams& aParams) {
2037   float& inlineCoord = aParams.isVertical ? aPt->y : aPt->x;
2038   gfxTextRun::Range markRange(aParams.mark);
2039   gfxTextRun::DrawParams params(aParams.context);
2040 
2041   float clusterStart = -std::numeric_limits<float>::infinity();
2042   bool shouldDrawEmphasisMark = false;
2043   for (uint32_t i = 0, idx = aOffset; i < aCount; ++i, ++idx) {
2044     if (aParams.spacing) {
2045       inlineCoord += aParams.direction * aParams.spacing[i].mBefore;
2046     }
2047     if (aShapedText->IsClusterStart(idx) ||
2048         clusterStart == -std::numeric_limits<float>::infinity()) {
2049       clusterStart = inlineCoord;
2050     }
2051     if (aShapedText->CharMayHaveEmphasisMark(idx)) {
2052       shouldDrawEmphasisMark = true;
2053     }
2054     inlineCoord += aParams.direction * aShapedText->GetAdvanceForGlyph(idx);
2055     if (shouldDrawEmphasisMark &&
2056         (i + 1 == aCount || aShapedText->IsClusterStart(idx + 1))) {
2057       float clusterAdvance = inlineCoord - clusterStart;
2058       // Move the coord backward to get the needed start point.
2059       float delta = (clusterAdvance + aParams.advance) / 2;
2060       inlineCoord -= delta;
2061       aParams.mark->Draw(markRange, *aPt, params);
2062       inlineCoord += delta;
2063       shouldDrawEmphasisMark = false;
2064     }
2065     if (aParams.spacing) {
2066       inlineCoord += aParams.direction * aParams.spacing[i].mAfter;
2067     }
2068   }
2069 }
2070 
Draw(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,gfx::Point * aPt,const TextRunDrawParams & aRunParams,gfx::ShapedTextFlags aOrientation)2071 void gfxFont::Draw(const gfxTextRun* aTextRun, uint32_t aStart, uint32_t aEnd,
2072                    gfx::Point* aPt, const TextRunDrawParams& aRunParams,
2073                    gfx::ShapedTextFlags aOrientation) {
2074   NS_ASSERTION(aRunParams.drawMode == DrawMode::GLYPH_PATH ||
2075                    !(int(aRunParams.drawMode) & int(DrawMode::GLYPH_PATH)),
2076                "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or "
2077                "GLYPH_STROKE_UNDERNEATH");
2078 
2079   if (aStart >= aEnd) {
2080     return;
2081   }
2082 
2083   FontDrawParams fontParams;
2084 
2085   if (aRunParams.drawOpts) {
2086     fontParams.drawOptions = *aRunParams.drawOpts;
2087   }
2088 
2089   fontParams.scaledFont = GetScaledFont(aRunParams.dt);
2090   if (!fontParams.scaledFont) {
2091     return;
2092   }
2093 
2094   auto* textDrawer = aRunParams.context->GetTextDrawer();
2095 
2096   fontParams.obliqueSkew = SkewForSyntheticOblique();
2097   fontParams.haveSVGGlyphs = GetFontEntry()->TryGetSVGData(this);
2098   fontParams.haveColorGlyphs = GetFontEntry()->TryGetColorGlyphs();
2099   fontParams.contextPaint = aRunParams.runContextPaint;
2100 
2101   if (textDrawer) {
2102     fontParams.isVerticalFont = aRunParams.isVerticalRun;
2103   } else {
2104     fontParams.isVerticalFont =
2105         aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
2106   }
2107 
2108   gfxContextMatrixAutoSaveRestore matrixRestore;
2109   layout::TextDrawTarget::AutoRestoreWRGlyphFlags glyphFlagsRestore;
2110 
2111   // Save the current baseline offset for restoring later, in case it is
2112   // modified.
2113   float& baseline = fontParams.isVerticalFont ? aPt->x : aPt->y;
2114   float origBaseline = baseline;
2115 
2116   // The point may be advanced in local-space, while the resulting point on
2117   // return must be advanced in transformed space. So save the original point so
2118   // we can properly transform the advance later.
2119   gfx::Point origPt = *aPt;
2120   const gfx::Matrix* offsetMatrix = nullptr;
2121 
2122   // Default to advancing along the +X direction (-X if RTL).
2123   fontParams.advanceDirection = aRunParams.isRTL ? -1.0f : 1.0f;
2124   // Default to offsetting baseline downward along the +Y direction.
2125   float baselineDir = 1.0f;
2126   // The direction of sideways rotation, if applicable.
2127   // -1 for rotating left/counter-clockwise
2128   // 1 for rotating right/clockwise
2129   // 0 for no rotation
2130   float sidewaysDir =
2131       (aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
2132            ? -1.0f
2133            : (aOrientation ==
2134                       gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT
2135                   ? 1.0f
2136                   : 0.0f));
2137   // If we're rendering a sideways run, we need to push a rotation transform to
2138   // the context.
2139   if (sidewaysDir != 0.0f) {
2140     if (textDrawer) {
2141       // For WebRender, we can't use a DrawTarget transform and must instead use
2142       // flags that locally transform the glyph, without affecting the glyph
2143       // origin. The glyph origins must thus be offset in the transformed
2144       // directions (instead of local-space directions). Modify the advance and
2145       // baseline directions to account for the indicated transform.
2146 
2147       // The default text orientation is down being +Y and right being +X.
2148       // Rotating 90 degrees left/CCW makes down be +X and right be -Y.
2149       // Rotating 90 degrees right/CW makes down be -X and right be +Y.
2150       // Thus the advance direction (moving right) is just sidewaysDir,
2151       // i.e. negative along Y axis if rotated left and positive if
2152       // rotated right.
2153       fontParams.advanceDirection *= sidewaysDir;
2154       // The baseline direction (moving down) is negated relative to the
2155       // advance direction for sideways transforms.
2156       baselineDir *= -sidewaysDir;
2157 
2158       glyphFlagsRestore.Save(textDrawer);
2159       // Set the transform flags accordingly. Both sideways rotations transpose
2160       // X and Y, while left rotation flips the resulting Y axis, and right
2161       // rotation flips the resulting X axis.
2162       textDrawer->SetWRGlyphFlags(
2163           textDrawer->GetWRGlyphFlags() | wr::FontInstanceFlags::TRANSPOSE |
2164           (aOrientation ==
2165                    gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT
2166                ? wr::FontInstanceFlags::FLIP_Y
2167                : wr::FontInstanceFlags::FLIP_X));
2168       // We also need to set up a transform for the glyph offset vector that
2169       // may be present in DetailedGlyph records.
2170       static const gfx::Matrix kSidewaysLeft = {0, -1, 1, 0, 0, 0};
2171       static const gfx::Matrix kSidewaysRight = {0, 1, -1, 0, 0, 0};
2172       offsetMatrix =
2173           (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_LEFT)
2174               ? &kSidewaysLeft
2175               : &kSidewaysRight;
2176     } else {
2177       // For non-WebRender targets, just push a rotation transform.
2178       matrixRestore.SetContext(aRunParams.context);
2179       gfxPoint p(aPt->x * aRunParams.devPerApp, aPt->y * aRunParams.devPerApp);
2180       // Get a matrix we can use to draw the (horizontally-shaped) textrun
2181       // with 90-degree CW rotation.
2182       const gfxFloat rotation = sidewaysDir * M_PI / 2.0f;
2183       gfxMatrix mat = aRunParams.context->CurrentMatrixDouble()
2184                           .PreTranslate(p)
2185                           .  // translate origin for rotation
2186                       PreRotate(rotation)
2187                           .  // turn 90deg CCW (sideways-left) or CW (*-right)
2188                       PreTranslate(-p);  // undo the translation
2189 
2190       aRunParams.context->SetMatrixDouble(mat);
2191     }
2192 
2193     // If we're drawing rotated horizontal text for an element styled
2194     // text-orientation:mixed, the dominant baseline will be vertical-
2195     // centered. So in this case, we need to adjust the position so that
2196     // the rotated horizontal text (which uses an alphabetic baseline) will
2197     // look OK when juxtaposed with upright glyphs (rendered on a centered
2198     // vertical baseline). The adjustment here is somewhat ad hoc; we
2199     // should eventually look for baseline tables[1] in the fonts and use
2200     // those if available.
2201     // [1] See http://www.microsoft.com/typography/otspec/base.htm
2202     if (aTextRun->UseCenterBaseline()) {
2203       const Metrics& metrics = GetMetrics(nsFontMetrics::eHorizontal);
2204       float baseAdj = (metrics.emAscent - metrics.emDescent) / 2;
2205       baseline += baseAdj * aTextRun->GetAppUnitsPerDevUnit() * baselineDir;
2206     }
2207   } else if (textDrawer &&
2208              aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) {
2209     glyphFlagsRestore.Save(textDrawer);
2210     textDrawer->SetWRGlyphFlags(textDrawer->GetWRGlyphFlags() |
2211                                 wr::FontInstanceFlags::VERTICAL);
2212   }
2213 
2214   if (fontParams.obliqueSkew != 0.0f && !fontParams.isVerticalFont &&
2215       !textDrawer) {
2216     // Adjust matrix for synthetic-oblique, except if we're doing vertical-
2217     // upright text, in which case this will be handled for each glyph
2218     // individually in DrawOneGlyph.
2219     if (!matrixRestore.HasMatrix()) {
2220       matrixRestore.SetContext(aRunParams.context);
2221     }
2222     gfx::Point p(aPt->x * aRunParams.devPerApp, aPt->y * aRunParams.devPerApp);
2223     gfx::Matrix mat =
2224         aRunParams.context->CurrentMatrix()
2225             .PreTranslate(p)
2226             .PreMultiply(gfx::Matrix(1, 0, -fontParams.obliqueSkew, 1, 0, 0))
2227             .PreTranslate(-p);
2228     aRunParams.context->SetMatrix(mat);
2229   }
2230 
2231   RefPtr<SVGContextPaint> contextPaint;
2232   if (fontParams.haveSVGGlyphs && !fontParams.contextPaint) {
2233     // If no pattern is specified for fill, use the current pattern
2234     NS_ASSERTION((int(aRunParams.drawMode) & int(DrawMode::GLYPH_STROKE)) == 0,
2235                  "no pattern supplied for stroking text");
2236     RefPtr<gfxPattern> fillPattern = aRunParams.context->GetPattern();
2237     contextPaint = new SimpleTextContextPaint(
2238         fillPattern, nullptr, aRunParams.context->CurrentMatrixDouble());
2239     fontParams.contextPaint = contextPaint.get();
2240   }
2241 
2242   // Synthetic-bold strikes are each offset one device pixel in run direction.
2243   // (these values are only needed if IsSyntheticBold() is true)
2244   // WebRender handles synthetic bold independently via FontInstanceFlags,
2245   // so just ignore requests in that case.
2246   if (IsSyntheticBold() && !textDrawer) {
2247     gfx::Float xscale = CalcXScale(aRunParams.context->GetDrawTarget());
2248     fontParams.synBoldOnePixelOffset = aRunParams.direction * xscale;
2249     if (xscale != 0.0) {
2250       static const int32_t kMaxExtraStrikes = 128;
2251       gfxFloat extraStrikes = GetSyntheticBoldOffset() / xscale;
2252       if (extraStrikes > kMaxExtraStrikes) {
2253         // if too many strikes are required, limit them and increase the step
2254         // size to compensate
2255         fontParams.extraStrikes = kMaxExtraStrikes;
2256         fontParams.synBoldOnePixelOffset = aRunParams.direction *
2257                                            GetSyntheticBoldOffset() /
2258                                            fontParams.extraStrikes;
2259       } else {
2260         // use as many strikes as needed for the increased advance
2261         fontParams.extraStrikes = NS_lroundf(std::max(1.0, extraStrikes));
2262       }
2263     }
2264   } else {
2265     fontParams.synBoldOnePixelOffset = 0;
2266     fontParams.extraStrikes = 0;
2267   }
2268 
2269   bool oldSubpixelAA = aRunParams.dt->GetPermitSubpixelAA();
2270   if (!AllowSubpixelAA()) {
2271     aRunParams.dt->SetPermitSubpixelAA(false);
2272   }
2273 
2274   Matrix mat;
2275   Matrix oldMat = aRunParams.dt->GetTransform();
2276 
2277   fontParams.drawOptions.mAntialiasMode = Get2DAAMode(mAntialiasOption);
2278 
2279   if (mStyle.baselineOffset != 0.0) {
2280     baseline +=
2281         mStyle.baselineOffset * aTextRun->GetAppUnitsPerDevUnit() * baselineDir;
2282   }
2283 
2284   bool emittedGlyphs;
2285   {
2286     // Select appropriate version of the templated DrawGlyphs method
2287     // to output glyphs to the buffer, depending on complexity needed
2288     // for the type of font, and whether added inter-glyph spacing
2289     // is specified.
2290     GlyphBufferAzure buffer(aRunParams, fontParams);
2291     if (fontParams.haveSVGGlyphs || fontParams.haveColorGlyphs ||
2292         fontParams.extraStrikes ||
2293         (fontParams.obliqueSkew != 0.0f && fontParams.isVerticalFont &&
2294          !textDrawer)) {
2295       if (aRunParams.spacing) {
2296         emittedGlyphs =
2297             DrawGlyphs<FontComplexityT::ComplexFont, SpacingT::HasSpacing>(
2298                 aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
2299       } else {
2300         emittedGlyphs =
2301             DrawGlyphs<FontComplexityT::ComplexFont, SpacingT::NoSpacing>(
2302                 aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
2303       }
2304     } else {
2305       if (aRunParams.spacing) {
2306         emittedGlyphs =
2307             DrawGlyphs<FontComplexityT::SimpleFont, SpacingT::HasSpacing>(
2308                 aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
2309       } else {
2310         emittedGlyphs =
2311             DrawGlyphs<FontComplexityT::SimpleFont, SpacingT::NoSpacing>(
2312                 aTextRun, aStart, aEnd - aStart, aPt, offsetMatrix, buffer);
2313       }
2314     }
2315   }
2316 
2317   baseline = origBaseline;
2318 
2319   if (aRunParams.callbacks && emittedGlyphs) {
2320     aRunParams.callbacks->NotifyGlyphPathEmitted();
2321   }
2322 
2323   aRunParams.dt->SetTransform(oldMat);
2324   aRunParams.dt->SetPermitSubpixelAA(oldSubpixelAA);
2325 
2326   if (sidewaysDir != 0.0f && !textDrawer) {
2327     // Adjust updated aPt to account for the transform we were using.
2328     // The advance happened horizontally in local-space, but the transformed
2329     // sideways advance is actually vertical, with sign depending on the
2330     // direction of rotation.
2331     float advance = aPt->x - origPt.x;
2332     *aPt = gfx::Point(origPt.x, origPt.y + advance * sidewaysDir);
2333   }
2334 }
2335 
RenderSVGGlyph(gfxContext * aContext,layout::TextDrawTarget * aTextDrawer,gfx::Point aPoint,uint32_t aGlyphId,SVGContextPaint * aContextPaint) const2336 bool gfxFont::RenderSVGGlyph(gfxContext* aContext,
2337                              layout::TextDrawTarget* aTextDrawer,
2338                              gfx::Point aPoint, uint32_t aGlyphId,
2339                              SVGContextPaint* aContextPaint) const {
2340   if (!GetFontEntry()->HasSVGGlyph(aGlyphId)) {
2341     return false;
2342   }
2343 
2344   if (aTextDrawer) {
2345     // WebRender doesn't support SVG Glyphs.
2346     // (pretend to succeed, output doesn't matter, we will emit a blob)
2347     aTextDrawer->FoundUnsupportedFeature();
2348     return true;
2349   }
2350 
2351   const gfxFloat devUnitsPerSVGUnit =
2352       GetAdjustedSize() / GetFontEntry()->UnitsPerEm();
2353   gfxContextMatrixAutoSaveRestore matrixRestore(aContext);
2354 
2355   aContext->SetMatrix(aContext->CurrentMatrix()
2356                           .PreTranslate(aPoint.x, aPoint.y)
2357                           .PreScale(devUnitsPerSVGUnit, devUnitsPerSVGUnit));
2358 
2359   aContextPaint->InitStrokeGeometry(aContext, devUnitsPerSVGUnit);
2360 
2361   GetFontEntry()->RenderSVGGlyph(aContext, aGlyphId, aContextPaint);
2362   aContext->NewPath();
2363   return true;
2364 }
2365 
RenderSVGGlyph(gfxContext * aContext,layout::TextDrawTarget * aTextDrawer,gfx::Point aPoint,uint32_t aGlyphId,SVGContextPaint * aContextPaint,gfxTextRunDrawCallbacks * aCallbacks,bool & aEmittedGlyphs) const2366 bool gfxFont::RenderSVGGlyph(gfxContext* aContext,
2367                              layout::TextDrawTarget* aTextDrawer,
2368                              gfx::Point aPoint, uint32_t aGlyphId,
2369                              SVGContextPaint* aContextPaint,
2370                              gfxTextRunDrawCallbacks* aCallbacks,
2371                              bool& aEmittedGlyphs) const {
2372   if (aCallbacks && aEmittedGlyphs) {
2373     aCallbacks->NotifyGlyphPathEmitted();
2374     aEmittedGlyphs = false;
2375   }
2376   return RenderSVGGlyph(aContext, aTextDrawer, aPoint, aGlyphId, aContextPaint);
2377 }
2378 
RenderColorGlyph(DrawTarget * aDrawTarget,gfxContext * aContext,layout::TextDrawTarget * aTextDrawer,mozilla::gfx::ScaledFont * scaledFont,mozilla::gfx::DrawOptions aDrawOptions,const mozilla::gfx::Point & aPoint,uint32_t aGlyphId) const2379 bool gfxFont::RenderColorGlyph(DrawTarget* aDrawTarget, gfxContext* aContext,
2380                                layout::TextDrawTarget* aTextDrawer,
2381                                mozilla::gfx::ScaledFont* scaledFont,
2382                                mozilla::gfx::DrawOptions aDrawOptions,
2383                                const mozilla::gfx::Point& aPoint,
2384                                uint32_t aGlyphId) const {
2385   AutoTArray<uint16_t, 8> layerGlyphs;
2386   AutoTArray<mozilla::gfx::DeviceColor, 8> layerColors;
2387 
2388   mozilla::gfx::DeviceColor defaultColor;
2389   if (!aContext->GetDeviceColor(defaultColor)) {
2390     defaultColor = ToDeviceColor(mozilla::gfx::sRGBColor::OpaqueBlack());
2391   }
2392   if (!GetFontEntry()->GetColorLayersInfo(aGlyphId, defaultColor, layerGlyphs,
2393                                           layerColors)) {
2394     return false;
2395   }
2396 
2397   // Default to opaque rendering (non-webrender applies alpha with a layer)
2398   float alpha = 1.0;
2399   if (aTextDrawer) {
2400     // defaultColor is the one that comes from CSS, so it has transparency info.
2401     bool hasComplexTransparency = 0.f < defaultColor.a && defaultColor.a < 1.f;
2402     if (hasComplexTransparency && layerGlyphs.Length() > 1) {
2403       // WebRender doesn't support drawing multi-layer transparent color-glyphs,
2404       // as it requires compositing all the layers before applying transparency.
2405       // (pretend to succeed, output doesn't matter, we will emit a blob)
2406       aTextDrawer->FoundUnsupportedFeature();
2407       return true;
2408     }
2409 
2410     // If we get here, then either alpha is 0 or 1, or there's only one layer
2411     // which shouldn't have composition issues. In all of these cases, applying
2412     // transparency directly to the glyph should work perfectly fine.
2413     //
2414     // Note that we must still emit completely transparent emoji, because they
2415     // might be wrapped in a shadow that uses the text run's glyphs.
2416     alpha = defaultColor.a;
2417   }
2418 
2419   for (uint32_t layerIndex = 0; layerIndex < layerGlyphs.Length();
2420        layerIndex++) {
2421     Glyph glyph;
2422     glyph.mIndex = layerGlyphs[layerIndex];
2423     glyph.mPosition = aPoint;
2424 
2425     mozilla::gfx::GlyphBuffer buffer;
2426     buffer.mGlyphs = &glyph;
2427     buffer.mNumGlyphs = 1;
2428 
2429     mozilla::gfx::DeviceColor layerColor = layerColors[layerIndex];
2430     layerColor.a *= alpha;
2431     aDrawTarget->FillGlyphs(scaledFont, buffer, ColorPattern(layerColor),
2432                             aDrawOptions);
2433   }
2434   return true;
2435 }
2436 
HasColorGlyphFor(uint32_t aCh,uint32_t aNextCh)2437 bool gfxFont::HasColorGlyphFor(uint32_t aCh, uint32_t aNextCh) {
2438   // Bitmap fonts are assumed to provide "color" glyphs for all supported chars.
2439   gfxFontEntry* fe = GetFontEntry();
2440   if (fe->HasColorBitmapTable()) {
2441     return true;
2442   }
2443   // Use harfbuzz shaper to look up the default glyph ID for the character.
2444   if (!mHarfBuzzShaper) {
2445     mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
2446   }
2447   auto* shaper = static_cast<gfxHarfBuzzShaper*>(mHarfBuzzShaper.get());
2448   if (!shaper->Initialize()) {
2449     return false;
2450   }
2451   uint32_t gid = 0;
2452   if (gfxFontUtils::IsVarSelector(aNextCh)) {
2453     gid = shaper->GetVariationGlyph(aCh, aNextCh);
2454   }
2455   if (!gid) {
2456     gid = shaper->GetNominalGlyph(aCh);
2457   }
2458   if (!gid) {
2459     return false;
2460   }
2461   // Check if there is a COLR/CPAL or SVG glyph for this ID.
2462   if (fe->TryGetColorGlyphs() && fe->HasColorLayersForGlyph(gid)) {
2463     return true;
2464   }
2465   if (fe->TryGetSVGData(this) && fe->HasSVGGlyph(gid)) {
2466     return true;
2467   }
2468   return false;
2469 }
2470 
UnionRange(gfxFloat aX,gfxFloat * aDestMin,gfxFloat * aDestMax)2471 static void UnionRange(gfxFloat aX, gfxFloat* aDestMin, gfxFloat* aDestMax) {
2472   *aDestMin = std::min(*aDestMin, aX);
2473   *aDestMax = std::max(*aDestMax, aX);
2474 }
2475 
2476 // We get precise glyph extents if the textrun creator requested them, or
2477 // if the font is a user font --- in which case the author may be relying
2478 // on overflowing glyphs.
NeedsGlyphExtents(gfxFont * aFont,const gfxTextRun * aTextRun)2479 static bool NeedsGlyphExtents(gfxFont* aFont, const gfxTextRun* aTextRun) {
2480   return (aTextRun->GetFlags() &
2481           gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX) ||
2482          aFont->GetFontEntry()->IsUserFont();
2483 }
2484 
IsSpaceGlyphInvisible(DrawTarget * aRefDrawTarget,const gfxTextRun * aTextRun)2485 bool gfxFont::IsSpaceGlyphInvisible(DrawTarget* aRefDrawTarget,
2486                                     const gfxTextRun* aTextRun) {
2487   if (!mFontEntry->mSpaceGlyphIsInvisibleInitialized &&
2488       GetAdjustedSize() >= 1.0) {
2489     gfxGlyphExtents* extents =
2490         GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
2491     gfxRect glyphExtents;
2492     mFontEntry->mSpaceGlyphIsInvisible =
2493         extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
2494                                               GetSpaceGlyph(), &glyphExtents) &&
2495         glyphExtents.IsEmpty();
2496     mFontEntry->mSpaceGlyphIsInvisibleInitialized = true;
2497   }
2498   return mFontEntry->mSpaceGlyphIsInvisible;
2499 }
2500 
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,gfx::ShapedTextFlags aOrientation)2501 gfxFont::RunMetrics gfxFont::Measure(const gfxTextRun* aTextRun,
2502                                      uint32_t aStart, uint32_t aEnd,
2503                                      BoundingBoxType aBoundingBoxType,
2504                                      DrawTarget* aRefDrawTarget,
2505                                      Spacing* aSpacing,
2506                                      gfx::ShapedTextFlags aOrientation) {
2507   // If aBoundingBoxType is TIGHT_HINTED_OUTLINE_EXTENTS
2508   // and the underlying cairo font may be antialiased,
2509   // we need to create a copy in order to avoid getting cached extents.
2510   // This is only used by MathML layout at present.
2511   if (aBoundingBoxType == TIGHT_HINTED_OUTLINE_EXTENTS &&
2512       mAntialiasOption != kAntialiasNone) {
2513     if (!mNonAAFont) {
2514       mNonAAFont = CopyWithAntialiasOption(kAntialiasNone);
2515     }
2516     // if font subclass doesn't implement CopyWithAntialiasOption(),
2517     // it will return null and we'll proceed to use the existing font
2518     if (mNonAAFont) {
2519       return mNonAAFont->Measure(aTextRun, aStart, aEnd,
2520                                  TIGHT_HINTED_OUTLINE_EXTENTS, aRefDrawTarget,
2521                                  aSpacing, aOrientation);
2522     }
2523   }
2524 
2525   const int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
2526   // Current position in appunits
2527   Orientation orientation =
2528       aOrientation == gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
2529           ? nsFontMetrics::eVertical
2530           : nsFontMetrics::eHorizontal;
2531   const gfxFont::Metrics& fontMetrics = GetMetrics(orientation);
2532 
2533   gfxFloat baselineOffset = 0;
2534   if (aTextRun->UseCenterBaseline() &&
2535       orientation == nsFontMetrics::eHorizontal) {
2536     // For a horizontal font being used in vertical writing mode with
2537     // text-orientation:mixed, the overall metrics we're accumulating
2538     // will be aimed at a center baseline. But this font's metrics were
2539     // based on the alphabetic baseline. So we compute a baseline offset
2540     // that will be applied to ascent/descent values and glyph rects
2541     // to effectively shift them relative to the baseline.
2542     // XXX Eventually we should probably use the BASE table, if present.
2543     // But it usually isn't, so we need an ad hoc adjustment for now.
2544     baselineOffset =
2545         appUnitsPerDevUnit * (fontMetrics.emAscent - fontMetrics.emDescent) / 2;
2546   }
2547 
2548   RunMetrics metrics;
2549   metrics.mAscent = fontMetrics.maxAscent * appUnitsPerDevUnit;
2550   metrics.mDescent = fontMetrics.maxDescent * appUnitsPerDevUnit;
2551 
2552   if (aStart == aEnd) {
2553     // exit now before we look at aSpacing[0], which is undefined
2554     metrics.mAscent -= baselineOffset;
2555     metrics.mDescent += baselineOffset;
2556     metrics.mBoundingBox =
2557         gfxRect(0, -metrics.mAscent, 0, metrics.mAscent + metrics.mDescent);
2558     return metrics;
2559   }
2560 
2561   gfxFloat advanceMin = 0, advanceMax = 0;
2562   const gfxTextRun::CompressedGlyph* charGlyphs =
2563       aTextRun->GetCharacterGlyphs();
2564   bool isRTL = aTextRun->IsRightToLeft();
2565   bool needsGlyphExtents = NeedsGlyphExtents(this, aTextRun);
2566   gfxGlyphExtents* extents =
2567       ((aBoundingBoxType == LOOSE_INK_EXTENTS && !needsGlyphExtents &&
2568         !aTextRun->HasDetailedGlyphs()) ||
2569        MOZ_UNLIKELY(GetStyle()->AdjustedSizeMustBeZero()))
2570           ? nullptr
2571           : GetOrCreateGlyphExtents(aTextRun->GetAppUnitsPerDevUnit());
2572   double x = 0;
2573   if (aSpacing) {
2574     x += aSpacing[0].mBefore;
2575   }
2576   uint32_t spaceGlyph = GetSpaceGlyph();
2577   bool allGlyphsInvisible = true;
2578   uint32_t i;
2579   for (i = aStart; i < aEnd; ++i) {
2580     const gfxTextRun::CompressedGlyph* glyphData = &charGlyphs[i];
2581     if (glyphData->IsSimpleGlyph()) {
2582       double advance = glyphData->GetSimpleAdvance();
2583       uint32_t glyphIndex = glyphData->GetSimpleGlyph();
2584       if (glyphIndex != spaceGlyph ||
2585           !IsSpaceGlyphInvisible(aRefDrawTarget, aTextRun)) {
2586         allGlyphsInvisible = false;
2587       }
2588       // Only get the real glyph horizontal extent if we were asked
2589       // for the tight bounding box or we're in quality mode
2590       if ((aBoundingBoxType != LOOSE_INK_EXTENTS || needsGlyphExtents) &&
2591           extents) {
2592         uint16_t extentsWidth =
2593             extents->GetContainedGlyphWidthAppUnits(glyphIndex);
2594         if (extentsWidth != gfxGlyphExtents::INVALID_WIDTH &&
2595             aBoundingBoxType == LOOSE_INK_EXTENTS) {
2596           UnionRange(x, &advanceMin, &advanceMax);
2597           UnionRange(x + extentsWidth, &advanceMin, &advanceMax);
2598         } else {
2599           gfxRect glyphRect;
2600           if (!extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
2601                                                      glyphIndex, &glyphRect)) {
2602             glyphRect = gfxRect(0, metrics.mBoundingBox.Y(), advance,
2603                                 metrics.mBoundingBox.Height());
2604           }
2605           if (isRTL) {
2606             // In effect, swap left and right sidebearings of the glyph, for
2607             // proper accumulation of potentially-overlapping glyph rects.
2608             glyphRect.MoveToX(advance - glyphRect.XMost());
2609           }
2610           glyphRect.MoveByX(x);
2611           metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
2612         }
2613       }
2614       x += advance;
2615     } else {
2616       allGlyphsInvisible = false;
2617       uint32_t glyphCount = glyphData->GetGlyphCount();
2618       if (glyphCount > 0) {
2619         const gfxTextRun::DetailedGlyph* details =
2620             aTextRun->GetDetailedGlyphs(i);
2621         NS_ASSERTION(details != nullptr,
2622                      "detailedGlyph record should not be missing!");
2623         uint32_t j;
2624         for (j = 0; j < glyphCount; ++j, ++details) {
2625           uint32_t glyphIndex = details->mGlyphID;
2626           double advance = details->mAdvance;
2627           gfxRect glyphRect;
2628           if (glyphData->IsMissing() || !extents ||
2629               !extents->GetTightGlyphExtentsAppUnits(this, aRefDrawTarget,
2630                                                      glyphIndex, &glyphRect)) {
2631             // We might have failed to get glyph extents due to
2632             // OOM or something
2633             glyphRect = gfxRect(0, -metrics.mAscent, advance,
2634                                 metrics.mAscent + metrics.mDescent);
2635           }
2636           if (isRTL) {
2637             // Swap left/right sidebearings of the glyph, because we're doing
2638             // mirrored measurement.
2639             glyphRect.MoveToX(advance - glyphRect.XMost());
2640             // Move to current x position, mirroring any x-offset amount.
2641             glyphRect.MoveByX(x - details->mOffset.x);
2642           } else {
2643             glyphRect.MoveByX(x + details->mOffset.x);
2644           }
2645           glyphRect.MoveByY(details->mOffset.y);
2646           metrics.mBoundingBox = metrics.mBoundingBox.Union(glyphRect);
2647           x += advance;
2648         }
2649       }
2650     }
2651     // Every other glyph type is ignored
2652     if (aSpacing) {
2653       double space = aSpacing[i - aStart].mAfter;
2654       if (i + 1 < aEnd) {
2655         space += aSpacing[i + 1 - aStart].mBefore;
2656       }
2657       x += space;
2658     }
2659   }
2660 
2661   if (allGlyphsInvisible) {
2662     metrics.mBoundingBox.SetEmpty();
2663   } else {
2664     if (aBoundingBoxType == LOOSE_INK_EXTENTS) {
2665       UnionRange(x, &advanceMin, &advanceMax);
2666       gfxRect fontBox(advanceMin, -metrics.mAscent, advanceMax - advanceMin,
2667                       metrics.mAscent + metrics.mDescent);
2668       metrics.mBoundingBox = metrics.mBoundingBox.Union(fontBox);
2669     }
2670   }
2671 
2672   if (isRTL) {
2673     // Reverse the effect of having swapped each glyph's sidebearings, to get
2674     // the correct sidebearings of the merged bounding box.
2675     metrics.mBoundingBox.MoveToX(x - metrics.mBoundingBox.XMost());
2676   }
2677 
2678   // If the font may be rendered with a fake-italic effect, we need to allow
2679   // for the top-right of the glyphs being skewed to the right, and the
2680   // bottom-left being skewed further left.
2681   gfxFloat skew = SkewForSyntheticOblique();
2682   if (skew != 0.0) {
2683     gfxFloat extendLeftEdge, extendRightEdge;
2684     if (orientation == nsFontMetrics::eVertical) {
2685       // The glyph will actually be skewed vertically, but "left" and "right"
2686       // here refer to line-left (physical top) and -right (bottom), so these
2687       // are still the directions in which we need to extend the box.
2688       extendLeftEdge = skew < 0.0 ? ceil(-skew * metrics.mBoundingBox.XMost())
2689                                   : ceil(skew * -metrics.mBoundingBox.X());
2690       extendRightEdge = skew < 0.0 ? ceil(-skew * -metrics.mBoundingBox.X())
2691                                    : ceil(skew * metrics.mBoundingBox.XMost());
2692     } else {
2693       extendLeftEdge = skew < 0.0 ? ceil(-skew * -metrics.mBoundingBox.Y())
2694                                   : ceil(skew * metrics.mBoundingBox.YMost());
2695       extendRightEdge = skew < 0.0 ? ceil(-skew * metrics.mBoundingBox.YMost())
2696                                    : ceil(skew * -metrics.mBoundingBox.Y());
2697     }
2698     metrics.mBoundingBox.SetWidth(metrics.mBoundingBox.Width() +
2699                                   extendLeftEdge + extendRightEdge);
2700     metrics.mBoundingBox.MoveByX(-extendLeftEdge);
2701   }
2702 
2703   if (baselineOffset != 0) {
2704     metrics.mAscent -= baselineOffset;
2705     metrics.mDescent += baselineOffset;
2706     metrics.mBoundingBox.MoveByY(baselineOffset);
2707   }
2708 
2709   metrics.mAdvanceWidth = x;
2710 
2711   return metrics;
2712 }
2713 
AgeCachedWords()2714 void gfxFont::AgeCachedWords() {
2715   if (mWordCache) {
2716     for (auto it = mWordCache->Iter(); !it.Done(); it.Next()) {
2717       CacheHashEntry* entry = it.Get();
2718       if (!entry->mShapedWord) {
2719         NS_ASSERTION(entry->mShapedWord, "cache entry has no gfxShapedWord!");
2720         it.Remove();
2721       } else if (entry->mShapedWord->IncrementAge() == kShapedWordCacheMaxAge) {
2722         it.Remove();
2723       }
2724     }
2725   }
2726 }
2727 
NotifyGlyphsChanged()2728 void gfxFont::NotifyGlyphsChanged() {
2729   uint32_t i, count = mGlyphExtentsArray.Length();
2730   for (i = 0; i < count; ++i) {
2731     // Flush cached extents array
2732     mGlyphExtentsArray[i]->NotifyGlyphsChanged();
2733   }
2734 
2735   if (mGlyphChangeObservers) {
2736     for (const auto& key : *mGlyphChangeObservers) {
2737       key->NotifyGlyphsChanged();
2738     }
2739   }
2740 }
2741 
2742 // If aChar is a "word boundary" for shaped-word caching purposes, return it;
2743 // else return 0.
IsBoundarySpace(char16_t aChar,char16_t aNextChar)2744 static char16_t IsBoundarySpace(char16_t aChar, char16_t aNextChar) {
2745   if ((aChar == ' ' || aChar == 0x00A0) && !IsClusterExtender(aNextChar)) {
2746     return aChar;
2747   }
2748   return 0;
2749 }
2750 
2751 #ifdef __GNUC__
2752 #  define GFX_MAYBE_UNUSED __attribute__((unused))
2753 #else
2754 #  define GFX_MAYBE_UNUSED
2755 #endif
2756 
2757 template <typename T>
GetShapedWord(DrawTarget * aDrawTarget,const T * aText,uint32_t aLength,uint32_t aHash,Script aRunScript,nsAtom * aLanguage,bool aVertical,int32_t aAppUnitsPerDevUnit,gfx::ShapedTextFlags aFlags,RoundingFlags aRounding,gfxTextPerfMetrics * aTextPerf GFX_MAYBE_UNUSED)2758 gfxShapedWord* gfxFont::GetShapedWord(
2759     DrawTarget* aDrawTarget, const T* aText, uint32_t aLength, uint32_t aHash,
2760     Script aRunScript, nsAtom* aLanguage, bool aVertical,
2761     int32_t aAppUnitsPerDevUnit, gfx::ShapedTextFlags aFlags,
2762     RoundingFlags aRounding, gfxTextPerfMetrics* aTextPerf GFX_MAYBE_UNUSED) {
2763   // if the cache is getting too big, flush it and start over
2764   uint32_t wordCacheMaxEntries =
2765       gfxPlatform::GetPlatform()->WordCacheMaxEntries();
2766   if (mWordCache->Count() > wordCacheMaxEntries) {
2767     NS_WARNING("flushing shaped-word cache");
2768     ClearCachedWords();
2769   }
2770 
2771   // if there's a cached entry for this word, just return it
2772   CacheHashKey key(aText, aLength, aHash, aRunScript, aLanguage,
2773                    aAppUnitsPerDevUnit, aFlags, aRounding);
2774 
2775   CacheHashEntry* entry = mWordCache->PutEntry(key, fallible);
2776   if (!entry) {
2777     NS_WARNING("failed to create word cache entry - expect missing text");
2778     return nullptr;
2779   }
2780   gfxShapedWord* sw = entry->mShapedWord.get();
2781 
2782   if (sw) {
2783     sw->ResetAge();
2784 #ifndef RELEASE_OR_BETA
2785     if (aTextPerf) {
2786       aTextPerf->current.wordCacheHit++;
2787     }
2788 #endif
2789     return sw;
2790   }
2791 
2792 #ifndef RELEASE_OR_BETA
2793   if (aTextPerf) {
2794     aTextPerf->current.wordCacheMiss++;
2795   }
2796 #endif
2797 
2798   sw = gfxShapedWord::Create(aText, aLength, aRunScript, aLanguage,
2799                              aAppUnitsPerDevUnit, aFlags, aRounding);
2800   entry->mShapedWord.reset(sw);
2801   if (!sw) {
2802     NS_WARNING("failed to create gfxShapedWord - expect missing text");
2803     return nullptr;
2804   }
2805 
2806   DebugOnly<bool> ok = ShapeText(aDrawTarget, aText, 0, aLength, aRunScript,
2807                                  aLanguage, aVertical, aRounding, sw);
2808 
2809   NS_WARNING_ASSERTION(ok, "failed to shape word - expect garbled text");
2810 
2811   return sw;
2812 }
2813 
2814 template gfxShapedWord* gfxFont::GetShapedWord(
2815     DrawTarget* aDrawTarget, const uint8_t* aText, uint32_t aLength,
2816     uint32_t aHash, Script aRunScript, nsAtom* aLanguage, bool aVertical,
2817     int32_t aAppUnitsPerDevUnit, gfx::ShapedTextFlags aFlags,
2818     RoundingFlags aRounding, gfxTextPerfMetrics* aTextPerf);
2819 
KeyEquals(const KeyTypePointer aKey) const2820 bool gfxFont::CacheHashEntry::KeyEquals(const KeyTypePointer aKey) const {
2821   const gfxShapedWord* sw = mShapedWord.get();
2822   if (!sw) {
2823     return false;
2824   }
2825   if (sw->GetLength() != aKey->mLength || sw->GetFlags() != aKey->mFlags ||
2826       sw->GetRounding() != aKey->mRounding ||
2827       sw->GetAppUnitsPerDevUnit() != aKey->mAppUnitsPerDevUnit ||
2828       sw->GetScript() != aKey->mScript ||
2829       sw->GetLanguage() != aKey->mLanguage) {
2830     return false;
2831   }
2832   if (sw->TextIs8Bit()) {
2833     if (aKey->mTextIs8Bit) {
2834       return (0 == memcmp(sw->Text8Bit(), aKey->mText.mSingle,
2835                           aKey->mLength * sizeof(uint8_t)));
2836     }
2837     // The key has 16-bit text, even though all the characters are < 256,
2838     // so the TEXT_IS_8BIT flag was set and the cached ShapedWord we're
2839     // comparing with will have 8-bit text.
2840     const uint8_t* s1 = sw->Text8Bit();
2841     const char16_t* s2 = aKey->mText.mDouble;
2842     const char16_t* s2end = s2 + aKey->mLength;
2843     while (s2 < s2end) {
2844       if (*s1++ != *s2++) {
2845         return false;
2846       }
2847     }
2848     return true;
2849   }
2850   NS_ASSERTION(!(aKey->mFlags & gfx::ShapedTextFlags::TEXT_IS_8BIT) &&
2851                    !aKey->mTextIs8Bit,
2852                "didn't expect 8-bit text here");
2853   return (0 == memcmp(sw->TextUnicode(), aKey->mText.mDouble,
2854                       aKey->mLength * sizeof(char16_t)));
2855 }
2856 
ShapeText(DrawTarget * aDrawTarget,const uint8_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)2857 bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const uint8_t* aText,
2858                         uint32_t aOffset, uint32_t aLength, Script aScript,
2859                         nsAtom* aLanguage, bool aVertical,
2860                         RoundingFlags aRounding, gfxShapedText* aShapedText) {
2861   nsDependentCSubstring ascii((const char*)aText, aLength);
2862   nsAutoString utf16;
2863   AppendASCIItoUTF16(ascii, utf16);
2864   if (utf16.Length() != aLength) {
2865     return false;
2866   }
2867   return ShapeText(aDrawTarget, utf16.BeginReading(), aOffset, aLength, aScript,
2868                    aLanguage, aVertical, aRounding, aShapedText);
2869 }
2870 
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)2871 bool gfxFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
2872                         uint32_t aOffset, uint32_t aLength, Script aScript,
2873                         nsAtom* aLanguage, bool aVertical,
2874                         RoundingFlags aRounding, gfxShapedText* aShapedText) {
2875   // XXX Currently, we do all vertical shaping through harfbuzz.
2876   // Vertical graphite support may be wanted as a future enhancement.
2877   if (FontCanSupportGraphite() && !aVertical) {
2878     if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
2879       if (!mGraphiteShaper) {
2880         mGraphiteShaper = MakeUnique<gfxGraphiteShaper>(this);
2881         Telemetry::ScalarAdd(Telemetry::ScalarID::BROWSER_USAGE_GRAPHITE, 1);
2882       }
2883       if (mGraphiteShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
2884                                      aScript, aLanguage, aVertical, aRounding,
2885                                      aShapedText)) {
2886         PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
2887                          aShapedText);
2888         return true;
2889       }
2890     }
2891   }
2892 
2893   if (!mHarfBuzzShaper) {
2894     mHarfBuzzShaper = MakeUnique<gfxHarfBuzzShaper>(this);
2895   }
2896   if (mHarfBuzzShaper->ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
2897                                  aLanguage, aVertical, aRounding,
2898                                  aShapedText)) {
2899     PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
2900                      aShapedText);
2901     if (GetFontEntry()->HasTrackingTable()) {
2902       // Convert font size from device pixels back to CSS px
2903       // to use in selecting tracking value
2904       float trackSize = GetAdjustedSize() *
2905                         aShapedText->GetAppUnitsPerDevUnit() /
2906                         AppUnitsPerCSSPixel();
2907       float tracking =
2908           GetFontEntry()->TrackingForCSSPx(trackSize) * mFUnitsConvFactor;
2909       // Applying tracking is a lot like the adjustment we do for
2910       // synthetic bold: we want to apply between clusters, not to
2911       // non-spacing glyphs within a cluster. So we can reuse that
2912       // helper here.
2913       aShapedText->AdjustAdvancesForSyntheticBold(tracking, aOffset, aLength);
2914     }
2915     return true;
2916   }
2917 
2918   NS_WARNING_ASSERTION(false, "shaper failed, expect scrambled/missing text");
2919   return false;
2920 }
2921 
PostShapingFixup(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,bool aVertical,gfxShapedText * aShapedText)2922 void gfxFont::PostShapingFixup(DrawTarget* aDrawTarget, const char16_t* aText,
2923                                uint32_t aOffset, uint32_t aLength,
2924                                bool aVertical, gfxShapedText* aShapedText) {
2925   if (IsSyntheticBold()) {
2926     const Metrics& metrics = GetMetrics(aVertical ? nsFontMetrics::eVertical
2927                                                   : nsFontMetrics::eHorizontal);
2928     if (metrics.maxAdvance > metrics.aveCharWidth) {
2929       float synBoldOffset = GetSyntheticBoldOffset() * CalcXScale(aDrawTarget);
2930       aShapedText->AdjustAdvancesForSyntheticBold(synBoldOffset, aOffset,
2931                                                   aLength);
2932     }
2933   }
2934 }
2935 
2936 #define MAX_SHAPING_LENGTH \
2937   32760  // slightly less than 32K, trying to avoid
2938          // over-stressing platform shapers
2939 #define BACKTRACK_LIMIT \
2940   16  // backtrack this far looking for a good place
2941       // to split into fragments for separate shaping
2942 
2943 template <typename T>
ShapeFragmentWithoutWordCache(DrawTarget * aDrawTarget,const T * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxTextRun * aTextRun)2944 bool gfxFont::ShapeFragmentWithoutWordCache(DrawTarget* aDrawTarget,
2945                                             const T* aText, uint32_t aOffset,
2946                                             uint32_t aLength, Script aScript,
2947                                             nsAtom* aLanguage, bool aVertical,
2948                                             RoundingFlags aRounding,
2949                                             gfxTextRun* aTextRun) {
2950   aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
2951 
2952   bool ok = true;
2953 
2954   while (ok && aLength > 0) {
2955     uint32_t fragLen = aLength;
2956 
2957     // limit the length of text we pass to shapers in a single call
2958     if (fragLen > MAX_SHAPING_LENGTH) {
2959       fragLen = MAX_SHAPING_LENGTH;
2960 
2961       // in the 8-bit case, there are no multi-char clusters,
2962       // so we don't need to do this check
2963       if (sizeof(T) == sizeof(char16_t)) {
2964         uint32_t i;
2965         for (i = 0; i < BACKTRACK_LIMIT; ++i) {
2966           if (aTextRun->IsClusterStart(aOffset + fragLen - i)) {
2967             fragLen -= i;
2968             break;
2969           }
2970         }
2971         if (i == BACKTRACK_LIMIT) {
2972           // if we didn't find any cluster start while backtracking,
2973           // just check that we're not in the middle of a surrogate
2974           // pair; back up by one code unit if we are.
2975           if (NS_IS_SURROGATE_PAIR(aText[fragLen - 1], aText[fragLen])) {
2976             --fragLen;
2977           }
2978         }
2979       }
2980     }
2981 
2982     ok = ShapeText(aDrawTarget, aText, aOffset, fragLen, aScript, aLanguage,
2983                    aVertical, aRounding, aTextRun);
2984 
2985     aText += fragLen;
2986     aOffset += fragLen;
2987     aLength -= fragLen;
2988   }
2989 
2990   return ok;
2991 }
2992 
2993 // Check if aCh is an unhandled control character that should be displayed
2994 // as a hexbox rather than rendered by some random font on the system.
2995 // We exclude \r as stray &#13;s are rather common (bug 941940).
2996 // Note that \n and \t don't come through here, as they have specific
2997 // meanings that have already been handled.
IsInvalidControlChar(uint32_t aCh)2998 static bool IsInvalidControlChar(uint32_t aCh) {
2999   return aCh != '\r' && ((aCh & 0x7f) < 0x20 || aCh == 0x7f);
3000 }
3001 
3002 template <typename T>
ShapeTextWithoutWordCache(DrawTarget * aDrawTarget,const T * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxTextRun * aTextRun)3003 bool gfxFont::ShapeTextWithoutWordCache(DrawTarget* aDrawTarget, const T* aText,
3004                                         uint32_t aOffset, uint32_t aLength,
3005                                         Script aScript, nsAtom* aLanguage,
3006                                         bool aVertical, RoundingFlags aRounding,
3007                                         gfxTextRun* aTextRun) {
3008   uint32_t fragStart = 0;
3009   bool ok = true;
3010 
3011   for (uint32_t i = 0; i <= aLength && ok; ++i) {
3012     T ch = (i < aLength) ? aText[i] : '\n';
3013     bool invalid = gfxFontGroup::IsInvalidChar(ch);
3014     uint32_t length = i - fragStart;
3015 
3016     // break into separate fragments when we hit an invalid char
3017     if (!invalid) {
3018       continue;
3019     }
3020 
3021     if (length > 0) {
3022       ok = ShapeFragmentWithoutWordCache(
3023           aDrawTarget, aText + fragStart, aOffset + fragStart, length, aScript,
3024           aLanguage, aVertical, aRounding, aTextRun);
3025     }
3026 
3027     if (i == aLength) {
3028       break;
3029     }
3030 
3031     // fragment was terminated by an invalid char: skip it,
3032     // unless it's a control char that we want to show as a hexbox,
3033     // but record where TAB or NEWLINE occur
3034     if (ch == '\t') {
3035       aTextRun->SetIsTab(aOffset + i);
3036     } else if (ch == '\n') {
3037       aTextRun->SetIsNewline(aOffset + i);
3038     } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) {
3039       aTextRun->SetIsFormattingControl(aOffset + i);
3040     } else if (IsInvalidControlChar(ch) &&
3041                !(aTextRun->GetFlags() &
3042                  gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
3043       if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
3044         ShapeFragmentWithoutWordCache(aDrawTarget, aText + i, aOffset + i, 1,
3045                                       aScript, aLanguage, aVertical, aRounding,
3046                                       aTextRun);
3047       } else {
3048         aTextRun->SetMissingGlyph(aOffset + i, ch, this);
3049       }
3050     }
3051     fragStart = i + 1;
3052   }
3053 
3054   NS_WARNING_ASSERTION(ok, "failed to shape text - expect garbled text");
3055   return ok;
3056 }
3057 
3058 #ifndef RELEASE_OR_BETA
3059 #  define TEXT_PERF_INCR(tp, m) (tp ? (tp)->current.m++ : 0)
3060 #else
3061 #  define TEXT_PERF_INCR(tp, m)
3062 #endif
3063 
IsChar8Bit(uint8_t)3064 inline static bool IsChar8Bit(uint8_t /*aCh*/) { return true; }
IsChar8Bit(char16_t aCh)3065 inline static bool IsChar8Bit(char16_t aCh) { return aCh < 0x100; }
3066 
HasSpaces(const uint8_t * aString,uint32_t aLen)3067 inline static bool HasSpaces(const uint8_t* aString, uint32_t aLen) {
3068   return memchr(aString, 0x20, aLen) != nullptr;
3069 }
3070 
HasSpaces(const char16_t * aString,uint32_t aLen)3071 inline static bool HasSpaces(const char16_t* aString, uint32_t aLen) {
3072   for (const char16_t* ch = aString; ch < aString + aLen; ch++) {
3073     if (*ch == 0x20) {
3074       return true;
3075     }
3076   }
3077   return false;
3078 }
3079 
3080 template <typename T>
SplitAndInitTextRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const T * aString,uint32_t aRunStart,uint32_t aRunLength,Script aRunScript,nsAtom * aLanguage,ShapedTextFlags aOrientation)3081 bool gfxFont::SplitAndInitTextRun(
3082     DrawTarget* aDrawTarget, gfxTextRun* aTextRun,
3083     const T* aString,    // text for this font run
3084     uint32_t aRunStart,  // position in the textrun
3085     uint32_t aRunLength, Script aRunScript, nsAtom* aLanguage,
3086     ShapedTextFlags aOrientation) {
3087   if (aRunLength == 0) {
3088     return true;
3089   }
3090 
3091   gfxTextPerfMetrics* tp = nullptr;
3092   RoundingFlags rounding = GetRoundOffsetsToPixels(aDrawTarget);
3093 
3094 #ifndef RELEASE_OR_BETA
3095   tp = aTextRun->GetFontGroup()->GetTextPerfMetrics();
3096   if (tp) {
3097     if (mStyle.systemFont) {
3098       tp->current.numChromeTextRuns++;
3099     } else {
3100       tp->current.numContentTextRuns++;
3101     }
3102     tp->current.numChars += aRunLength;
3103     if (aRunLength > tp->current.maxTextRunLen) {
3104       tp->current.maxTextRunLen = aRunLength;
3105     }
3106   }
3107 #endif
3108 
3109   uint32_t wordCacheCharLimit =
3110       gfxPlatform::GetPlatform()->WordCacheCharLimit();
3111 
3112   bool vertical = aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
3113 
3114   // If spaces can participate in shaping (e.g. within lookups for automatic
3115   // fractions), need to shape without using the word cache which segments
3116   // textruns on space boundaries. Word cache can be used if the textrun
3117   // is short enough to fit in the word cache and it lacks spaces.
3118   tainted_boolean_hint t_canParticipate =
3119       SpaceMayParticipateInShaping(aRunScript);
3120   bool canParticipate = t_canParticipate.unverified_safe_because(
3121       "We need to ensure that this function operates safely independent of "
3122       "t_canParticipate. The worst that can happen here is that the decision "
3123       "to use the cache is incorrectly made, resulting in a bad "
3124       "rendering/slowness. However, this  would not compromise the memory "
3125       "safety of Firefox in any way, and can thus be permitted");
3126 
3127   if (canParticipate) {
3128     if (aRunLength > wordCacheCharLimit || HasSpaces(aString, aRunLength)) {
3129       TEXT_PERF_INCR(tp, wordCacheSpaceRules);
3130       return ShapeTextWithoutWordCache(aDrawTarget, aString, aRunStart,
3131                                        aRunLength, aRunScript, aLanguage,
3132                                        vertical, rounding, aTextRun);
3133     }
3134   }
3135 
3136   InitWordCache();
3137 
3138   // the only flags we care about for ShapedWord construction/caching
3139   gfx::ShapedTextFlags flags = aTextRun->GetFlags();
3140   flags &= (gfx::ShapedTextFlags::TEXT_IS_RTL |
3141             gfx::ShapedTextFlags::TEXT_DISABLE_OPTIONAL_LIGATURES |
3142             gfx::ShapedTextFlags::TEXT_USE_MATH_SCRIPT |
3143             gfx::ShapedTextFlags::TEXT_ORIENT_MASK);
3144   if (sizeof(T) == sizeof(uint8_t)) {
3145     flags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
3146   }
3147 
3148   uint32_t wordStart = 0;
3149   uint32_t hash = 0;
3150   bool wordIs8Bit = true;
3151   int32_t appUnitsPerDevUnit = aTextRun->GetAppUnitsPerDevUnit();
3152 
3153   T nextCh = aString[0];
3154   for (uint32_t i = 0; i <= aRunLength; ++i) {
3155     T ch = nextCh;
3156     nextCh = (i < aRunLength - 1) ? aString[i + 1] : '\n';
3157     T boundary = IsBoundarySpace(ch, nextCh);
3158     bool invalid = !boundary && gfxFontGroup::IsInvalidChar(ch);
3159     uint32_t length = i - wordStart;
3160 
3161     // break into separate ShapedWords when we hit an invalid char,
3162     // or a boundary space (always handled individually),
3163     // or the first non-space after a space
3164     if (!boundary && !invalid) {
3165       if (!IsChar8Bit(ch)) {
3166         wordIs8Bit = false;
3167       }
3168       // include this character in the hash, and move on to next
3169       hash = gfxShapedWord::HashMix(hash, ch);
3170       continue;
3171     }
3172 
3173     // We've decided to break here (i.e. we're at the end of a "word");
3174     // shape the word and add it to the textrun.
3175     // For words longer than the limit, we don't use the
3176     // font's word cache but just shape directly into the textrun.
3177     if (length > wordCacheCharLimit) {
3178       TEXT_PERF_INCR(tp, wordCacheLong);
3179       bool ok = ShapeFragmentWithoutWordCache(
3180           aDrawTarget, aString + wordStart, aRunStart + wordStart, length,
3181           aRunScript, aLanguage, vertical, rounding, aTextRun);
3182       if (!ok) {
3183         return false;
3184       }
3185     } else if (length > 0) {
3186       gfx::ShapedTextFlags wordFlags = flags;
3187       // in the 8-bit version of this method, TEXT_IS_8BIT was
3188       // already set as part of |flags|, so no need for a per-word
3189       // adjustment here
3190       if (sizeof(T) == sizeof(char16_t)) {
3191         if (wordIs8Bit) {
3192           wordFlags |= gfx::ShapedTextFlags::TEXT_IS_8BIT;
3193         }
3194       }
3195       gfxShapedWord* sw = GetShapedWord(
3196           aDrawTarget, aString + wordStart, length, hash, aRunScript, aLanguage,
3197           vertical, appUnitsPerDevUnit, wordFlags, rounding, tp);
3198       if (sw) {
3199         aTextRun->CopyGlyphDataFrom(sw, aRunStart + wordStart);
3200       } else {
3201         return false;  // failed, presumably out of memory?
3202       }
3203     }
3204 
3205     if (boundary) {
3206       // word was terminated by a space: add that to the textrun
3207       MOZ_ASSERT(aOrientation != ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
3208                  "text-orientation:mixed should be resolved earlier");
3209       if (boundary != ' ' || !aTextRun->SetSpaceGlyphIfSimple(
3210                                  this, aRunStart + i, ch, aOrientation)) {
3211         // Currently, the only "boundary" characters we recognize are
3212         // space and no-break space, which are both 8-bit, so we force
3213         // that flag (below). If we ever change IsBoundarySpace, we
3214         // may need to revise this.
3215         // Avoid tautological-constant-out-of-range-compare in 8-bit:
3216         DebugOnly<char16_t> boundary16 = boundary;
3217         NS_ASSERTION(boundary16 < 256, "unexpected boundary!");
3218         gfxShapedWord* sw = GetShapedWord(
3219             aDrawTarget, &boundary, 1, gfxShapedWord::HashMix(0, boundary),
3220             aRunScript, aLanguage, vertical, appUnitsPerDevUnit,
3221             flags | gfx::ShapedTextFlags::TEXT_IS_8BIT, rounding, tp);
3222         if (sw) {
3223           aTextRun->CopyGlyphDataFrom(sw, aRunStart + i);
3224           if (boundary == ' ') {
3225             aTextRun->GetCharacterGlyphs()[aRunStart + i].SetIsSpace();
3226           }
3227         } else {
3228           return false;
3229         }
3230       }
3231       hash = 0;
3232       wordStart = i + 1;
3233       wordIs8Bit = true;
3234       continue;
3235     }
3236 
3237     if (i == aRunLength) {
3238       break;
3239     }
3240 
3241     NS_ASSERTION(invalid, "how did we get here except via an invalid char?");
3242 
3243     // word was terminated by an invalid char: skip it,
3244     // unless it's a control char that we want to show as a hexbox,
3245     // but record where TAB or NEWLINE occur
3246     if (ch == '\t') {
3247       aTextRun->SetIsTab(aRunStart + i);
3248     } else if (ch == '\n') {
3249       aTextRun->SetIsNewline(aRunStart + i);
3250     } else if (GetGeneralCategory(ch) == HB_UNICODE_GENERAL_CATEGORY_FORMAT) {
3251       aTextRun->SetIsFormattingControl(aRunStart + i);
3252     } else if (IsInvalidControlChar(ch) &&
3253                !(aTextRun->GetFlags() &
3254                  gfx::ShapedTextFlags::TEXT_HIDE_CONTROL_CHARACTERS)) {
3255       if (GetFontEntry()->IsUserFont() && HasCharacter(ch)) {
3256         ShapeFragmentWithoutWordCache(aDrawTarget, aString + i, aRunStart + i,
3257                                       1, aRunScript, aLanguage, vertical,
3258                                       rounding, aTextRun);
3259       } else {
3260         aTextRun->SetMissingGlyph(aRunStart + i, ch, this);
3261       }
3262     }
3263 
3264     hash = 0;
3265     wordStart = i + 1;
3266     wordIs8Bit = true;
3267   }
3268 
3269   return true;
3270 }
3271 
3272 // Explicit instantiations of SplitAndInitTextRun, to avoid libxul link failure
3273 template bool gfxFont::SplitAndInitTextRun(
3274     DrawTarget* aDrawTarget, gfxTextRun* aTextRun, const uint8_t* aString,
3275     uint32_t aRunStart, uint32_t aRunLength, Script aRunScript,
3276     nsAtom* aLanguage, ShapedTextFlags aOrientation);
3277 template bool gfxFont::SplitAndInitTextRun(
3278     DrawTarget* aDrawTarget, gfxTextRun* aTextRun, const char16_t* aString,
3279     uint32_t aRunStart, uint32_t aRunLength, Script aRunScript,
3280     nsAtom* aLanguage, ShapedTextFlags aOrientation);
3281 
3282 template <>
InitFakeSmallCapsRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const char16_t * aText,uint32_t aOffset,uint32_t aLength,FontMatchType aMatchType,gfx::ShapedTextFlags aOrientation,Script aScript,nsAtom * aLanguage,bool aSyntheticLower,bool aSyntheticUpper)3283 bool gfxFont::InitFakeSmallCapsRun(DrawTarget* aDrawTarget,
3284                                    gfxTextRun* aTextRun, const char16_t* aText,
3285                                    uint32_t aOffset, uint32_t aLength,
3286                                    FontMatchType aMatchType,
3287                                    gfx::ShapedTextFlags aOrientation,
3288                                    Script aScript, nsAtom* aLanguage,
3289                                    bool aSyntheticLower, bool aSyntheticUpper) {
3290   bool ok = true;
3291 
3292   RefPtr<gfxFont> smallCapsFont = GetSmallCapsFont();
3293   if (!smallCapsFont) {
3294     NS_WARNING("failed to get reduced-size font for smallcaps!");
3295     smallCapsFont = this;
3296   }
3297 
3298   bool isCJK = gfxTextRun::IsCJKScript(aScript);
3299 
3300   enum RunCaseAction { kNoChange, kUppercaseReduce, kUppercase };
3301 
3302   RunCaseAction runAction = kNoChange;
3303   uint32_t runStart = 0;
3304 
3305   for (uint32_t i = 0; i <= aLength; ++i) {
3306     uint32_t extraCodeUnits = 0;  // Will be set to 1 if we need to consume
3307                                   // a trailing surrogate as well as the
3308                                   // current code unit.
3309     RunCaseAction chAction = kNoChange;
3310     // Unless we're at the end, figure out what treatment the current
3311     // character will need.
3312     if (i < aLength) {
3313       uint32_t ch = aText[i];
3314       if (i < aLength - 1 && NS_IS_SURROGATE_PAIR(ch, aText[i + 1])) {
3315         ch = SURROGATE_TO_UCS4(ch, aText[i + 1]);
3316         extraCodeUnits = 1;
3317       }
3318       // Characters that aren't the start of a cluster are ignored here.
3319       // They get added to whatever lowercase/non-lowercase run we're in.
3320       if (IsClusterExtender(ch)) {
3321         chAction = runAction;
3322       } else {
3323         if (ch != ToUpperCase(ch) || SpecialUpper(ch)) {
3324           // ch is lower case
3325           chAction = (aSyntheticLower ? kUppercaseReduce : kNoChange);
3326         } else if (ch != ToLowerCase(ch)) {
3327           // ch is upper case
3328           chAction = (aSyntheticUpper ? kUppercaseReduce : kNoChange);
3329           if (aLanguage == nsGkAtoms::el) {
3330             // In Greek, check for characters that will be modified by
3331             // the GreekUpperCase mapping - this catches accented
3332             // capitals where the accent is to be removed (bug 307039).
3333             // These are handled by using the full-size font with the
3334             // uppercasing transform.
3335             mozilla::GreekCasing::State state;
3336             bool markEta, updateEta;
3337             uint32_t ch2 =
3338                 mozilla::GreekCasing::UpperCase(ch, state, markEta, updateEta);
3339             if ((ch != ch2 || markEta) && !aSyntheticUpper) {
3340               chAction = kUppercase;
3341             }
3342           }
3343         }
3344       }
3345     }
3346 
3347     // At the end of the text or when the current character needs different
3348     // casing treatment from the current run, finish the run-in-progress
3349     // and prepare to accumulate a new run.
3350     // Note that we do not look at any source data for offset [i] here,
3351     // as that would be invalid in the case where i==length.
3352     if ((i == aLength || runAction != chAction) && runStart < i) {
3353       uint32_t runLength = i - runStart;
3354       gfxFont* f = this;
3355       switch (runAction) {
3356         case kNoChange:
3357           // just use the current font and the existing string
3358           aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
3359                                 aOrientation, isCJK);
3360           if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun, aText + runStart,
3361                                       aOffset + runStart, runLength, aScript,
3362                                       aLanguage, aOrientation)) {
3363             ok = false;
3364           }
3365           break;
3366 
3367         case kUppercaseReduce:
3368           // use reduced-size font, then fall through to uppercase the text
3369           f = smallCapsFont;
3370           [[fallthrough]];
3371 
3372         case kUppercase:
3373           // apply uppercase transform to the string
3374           nsDependentSubstring origString(aText + runStart, runLength);
3375           nsAutoString convertedString;
3376           AutoTArray<bool, 50> charsToMergeArray;
3377           AutoTArray<bool, 50> deletedCharsArray;
3378 
3379           bool mergeNeeded = nsCaseTransformTextRunFactory::TransformString(
3380               origString, convertedString, /* aAllUppercase = */ true,
3381               /* aCaseTransformsOnly = */ false, aLanguage, charsToMergeArray,
3382               deletedCharsArray);
3383 
3384           if (mergeNeeded) {
3385             // This is the hard case: the transformation caused chars
3386             // to be inserted or deleted, so we can't shape directly
3387             // into the destination textrun but have to handle the
3388             // mismatch of character positions.
3389             gfxTextRunFactory::Parameters params = {
3390                 aDrawTarget, nullptr, nullptr,
3391                 nullptr,     0,       aTextRun->GetAppUnitsPerDevUnit()};
3392             RefPtr<gfxTextRun> tempRun(gfxTextRun::Create(
3393                 &params, convertedString.Length(), aTextRun->GetFontGroup(),
3394                 gfx::ShapedTextFlags(), nsTextFrameUtils::Flags()));
3395             tempRun->AddGlyphRun(f, aMatchType, 0, true, aOrientation, isCJK);
3396             if (!f->SplitAndInitTextRun(aDrawTarget, tempRun.get(),
3397                                         convertedString.BeginReading(), 0,
3398                                         convertedString.Length(), aScript,
3399                                         aLanguage, aOrientation)) {
3400               ok = false;
3401             } else {
3402               RefPtr<gfxTextRun> mergedRun(gfxTextRun::Create(
3403                   &params, runLength, aTextRun->GetFontGroup(),
3404                   gfx::ShapedTextFlags(), nsTextFrameUtils::Flags()));
3405               MergeCharactersInTextRun(mergedRun.get(), tempRun.get(),
3406                                        charsToMergeArray.Elements(),
3407                                        deletedCharsArray.Elements());
3408               gfxTextRun::Range runRange(0, runLength);
3409               aTextRun->CopyGlyphDataFrom(mergedRun.get(), runRange,
3410                                           aOffset + runStart);
3411             }
3412           } else {
3413             aTextRun->AddGlyphRun(f, aMatchType, aOffset + runStart, true,
3414                                   aOrientation, isCJK);
3415             if (!f->SplitAndInitTextRun(aDrawTarget, aTextRun,
3416                                         convertedString.BeginReading(),
3417                                         aOffset + runStart, runLength, aScript,
3418                                         aLanguage, aOrientation)) {
3419               ok = false;
3420             }
3421           }
3422           break;
3423       }
3424 
3425       runStart = i;
3426     }
3427 
3428     i += extraCodeUnits;
3429     if (i < aLength) {
3430       runAction = chAction;
3431     }
3432   }
3433 
3434   return ok;
3435 }
3436 
3437 template <>
InitFakeSmallCapsRun(DrawTarget * aDrawTarget,gfxTextRun * aTextRun,const uint8_t * aText,uint32_t aOffset,uint32_t aLength,FontMatchType aMatchType,gfx::ShapedTextFlags aOrientation,Script aScript,nsAtom * aLanguage,bool aSyntheticLower,bool aSyntheticUpper)3438 bool gfxFont::InitFakeSmallCapsRun(DrawTarget* aDrawTarget,
3439                                    gfxTextRun* aTextRun, const uint8_t* aText,
3440                                    uint32_t aOffset, uint32_t aLength,
3441                                    FontMatchType aMatchType,
3442                                    gfx::ShapedTextFlags aOrientation,
3443                                    Script aScript, nsAtom* aLanguage,
3444                                    bool aSyntheticLower, bool aSyntheticUpper) {
3445   NS_ConvertASCIItoUTF16 unicodeString(reinterpret_cast<const char*>(aText),
3446                                        aLength);
3447   return InitFakeSmallCapsRun(
3448       aDrawTarget, aTextRun, static_cast<const char16_t*>(unicodeString.get()),
3449       aOffset, aLength, aMatchType, aOrientation, aScript, aLanguage,
3450       aSyntheticLower, aSyntheticUpper);
3451 }
3452 
GetSmallCapsFont()3453 gfxFont* gfxFont::GetSmallCapsFont() {
3454   gfxFontStyle style(*GetStyle());
3455   style.size *= SMALL_CAPS_SCALE_FACTOR;
3456   style.variantCaps = NS_FONT_VARIANT_CAPS_NORMAL;
3457   gfxFontEntry* fe = GetFontEntry();
3458   return fe->FindOrMakeFont(&style, mUnicodeRangeMap);
3459 }
3460 
GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel)3461 gfxFont* gfxFont::GetSubSuperscriptFont(int32_t aAppUnitsPerDevPixel) {
3462   gfxFontStyle style(*GetStyle());
3463   style.AdjustForSubSuperscript(aAppUnitsPerDevPixel);
3464   gfxFontEntry* fe = GetFontEntry();
3465   return fe->FindOrMakeFont(&style, mUnicodeRangeMap);
3466 }
3467 
GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit)3468 gfxGlyphExtents* gfxFont::GetOrCreateGlyphExtents(int32_t aAppUnitsPerDevUnit) {
3469   uint32_t i, count = mGlyphExtentsArray.Length();
3470   for (i = 0; i < count; ++i) {
3471     if (mGlyphExtentsArray[i]->GetAppUnitsPerDevUnit() == aAppUnitsPerDevUnit)
3472       return mGlyphExtentsArray[i].get();
3473   }
3474   gfxGlyphExtents* glyphExtents = new gfxGlyphExtents(aAppUnitsPerDevUnit);
3475   if (glyphExtents) {
3476     mGlyphExtentsArray.AppendElement(glyphExtents);
3477     // Initialize the extents of a space glyph, assuming that spaces don't
3478     // render anything!
3479     glyphExtents->SetContainedGlyphWidthAppUnits(GetSpaceGlyph(), 0);
3480   }
3481   return glyphExtents;
3482 }
3483 
SetupGlyphExtents(DrawTarget * aDrawTarget,uint32_t aGlyphID,bool aNeedTight,gfxGlyphExtents * aExtents)3484 void gfxFont::SetupGlyphExtents(DrawTarget* aDrawTarget, uint32_t aGlyphID,
3485                                 bool aNeedTight, gfxGlyphExtents* aExtents) {
3486   gfxRect svgBounds;
3487   if (mFontEntry->TryGetSVGData(this) && mFontEntry->HasSVGGlyph(aGlyphID) &&
3488       mFontEntry->GetSVGGlyphExtents(aDrawTarget, aGlyphID, GetAdjustedSize(),
3489                                      &svgBounds)) {
3490     gfxFloat d2a = aExtents->GetAppUnitsPerDevUnit();
3491     aExtents->SetTightGlyphExtents(
3492         aGlyphID, gfxRect(svgBounds.X() * d2a, svgBounds.Y() * d2a,
3493                           svgBounds.Width() * d2a, svgBounds.Height() * d2a));
3494     return;
3495   }
3496 
3497   gfxRect bounds;
3498   GetGlyphBounds(aGlyphID, &bounds, mAntialiasOption == kAntialiasNone);
3499 
3500   const Metrics& fontMetrics = GetMetrics(nsFontMetrics::eHorizontal);
3501   int32_t appUnitsPerDevUnit = aExtents->GetAppUnitsPerDevUnit();
3502   if (!aNeedTight && bounds.x >= 0.0 && bounds.y >= -fontMetrics.maxAscent &&
3503       bounds.height + bounds.y <= fontMetrics.maxDescent) {
3504     uint32_t appUnitsWidth =
3505         uint32_t(ceil((bounds.x + bounds.width) * appUnitsPerDevUnit));
3506     if (appUnitsWidth < gfxGlyphExtents::INVALID_WIDTH) {
3507       aExtents->SetContainedGlyphWidthAppUnits(aGlyphID,
3508                                                uint16_t(appUnitsWidth));
3509       return;
3510     }
3511   }
3512 #ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
3513   if (!aNeedTight) {
3514     ++gGlyphExtentsSetupFallBackToTight;
3515   }
3516 #endif
3517 
3518   gfxFloat d2a = appUnitsPerDevUnit;
3519   aExtents->SetTightGlyphExtents(
3520       aGlyphID, gfxRect(bounds.x * d2a, bounds.y * d2a, bounds.width * d2a,
3521                         bounds.height * d2a));
3522 }
3523 
3524 // Try to initialize font metrics by reading sfnt tables directly;
3525 // set mIsValid=TRUE and return TRUE on success.
3526 // Return FALSE if the gfxFontEntry subclass does not
3527 // implement GetFontTable(), or for non-sfnt fonts where tables are
3528 // not available.
3529 // If this returns TRUE without setting the mIsValid flag, then we -did-
3530 // apparently find an sfnt, but it was too broken to be used.
InitMetricsFromSfntTables(Metrics & aMetrics)3531 bool gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics) {
3532   mIsValid = false;  // font is NOT valid in case of early return
3533 
3534   const uint32_t kHheaTableTag = TRUETYPE_TAG('h', 'h', 'e', 'a');
3535   const uint32_t kOS_2TableTag = TRUETYPE_TAG('O', 'S', '/', '2');
3536 
3537   uint32_t len;
3538 
3539   if (mFUnitsConvFactor < 0.0) {
3540     // If the conversion factor from FUnits is not yet set,
3541     // get the unitsPerEm from the 'head' table via the font entry
3542     uint16_t unitsPerEm = GetFontEntry()->UnitsPerEm();
3543     if (unitsPerEm == gfxFontEntry::kInvalidUPEM) {
3544       return false;
3545     }
3546     mFUnitsConvFactor = GetAdjustedSize() / unitsPerEm;
3547   }
3548 
3549   // 'hhea' table is required for the advanceWidthMax field
3550   gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
3551   if (!hheaTable) {
3552     return false;  // no 'hhea' table -> not an sfnt
3553   }
3554   const MetricsHeader* hhea =
3555       reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable, &len));
3556   if (len < sizeof(MetricsHeader)) {
3557     return false;
3558   }
3559 
3560 #define SET_UNSIGNED(field, src) \
3561   aMetrics.field = uint16_t(src) * mFUnitsConvFactor
3562 #define SET_SIGNED(field, src) aMetrics.field = int16_t(src) * mFUnitsConvFactor
3563 
3564   SET_UNSIGNED(maxAdvance, hhea->advanceWidthMax);
3565 
3566   // 'OS/2' table is optional, if not found we'll estimate xHeight
3567   // and aveCharWidth by measuring glyphs
3568   gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
3569   if (os2Table) {
3570     const OS2Table* os2 =
3571         reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
3572     // this should always be present in any valid OS/2 of any version
3573     if (len >= offsetof(OS2Table, xAvgCharWidth) + sizeof(int16_t)) {
3574       SET_SIGNED(aveCharWidth, os2->xAvgCharWidth);
3575     }
3576   }
3577 
3578 #undef SET_SIGNED
3579 #undef SET_UNSIGNED
3580 
3581   hb_font_t* hbFont = gfxHarfBuzzShaper::CreateHBFont(this);
3582   hb_position_t position;
3583 
3584   auto FixedToFloat = [](hb_position_t f) -> gfxFloat { return f / 65536.0; };
3585 
3586   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER,
3587                                  &position)) {
3588     aMetrics.maxAscent = FixedToFloat(position);
3589   }
3590   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER,
3591                                  &position)) {
3592     aMetrics.maxDescent = -FixedToFloat(position);
3593   }
3594   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP,
3595                                  &position)) {
3596     aMetrics.externalLeading = FixedToFloat(position);
3597   }
3598 
3599   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_UNDERLINE_OFFSET,
3600                                  &position)) {
3601     aMetrics.underlineOffset = FixedToFloat(position);
3602   }
3603   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_UNDERLINE_SIZE,
3604                                  &position)) {
3605     aMetrics.underlineSize = FixedToFloat(position);
3606   }
3607   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_STRIKEOUT_OFFSET,
3608                                  &position)) {
3609     aMetrics.strikeoutOffset = FixedToFloat(position);
3610   }
3611   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_STRIKEOUT_SIZE,
3612                                  &position)) {
3613     aMetrics.strikeoutSize = FixedToFloat(position);
3614   }
3615 
3616   // Although sxHeight and sCapHeight are signed fields, we consider
3617   // zero/negative values to be erroneous and just ignore them.
3618   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_X_HEIGHT,
3619                                  &position) &&
3620       position > 0) {
3621     aMetrics.xHeight = FixedToFloat(position);
3622   }
3623   if (hb_ot_metrics_get_position(hbFont, HB_OT_METRICS_TAG_CAP_HEIGHT,
3624                                  &position) &&
3625       position > 0) {
3626     aMetrics.capHeight = FixedToFloat(position);
3627   }
3628   hb_font_destroy(hbFont);
3629 
3630   mIsValid = true;
3631 
3632   return true;
3633 }
3634 
RoundToNearestMultiple(double aValue,double aFraction)3635 static double RoundToNearestMultiple(double aValue, double aFraction) {
3636   return floor(aValue / aFraction + 0.5) * aFraction;
3637 }
3638 
CalculateDerivedMetrics(Metrics & aMetrics)3639 void gfxFont::CalculateDerivedMetrics(Metrics& aMetrics) {
3640   aMetrics.maxAscent =
3641       ceil(RoundToNearestMultiple(aMetrics.maxAscent, 1 / 1024.0));
3642   aMetrics.maxDescent =
3643       ceil(RoundToNearestMultiple(aMetrics.maxDescent, 1 / 1024.0));
3644 
3645   if (aMetrics.xHeight <= 0) {
3646     // only happens if we couldn't find either font metrics
3647     // or a char to measure;
3648     // pick an arbitrary value that's better than zero
3649     aMetrics.xHeight = aMetrics.maxAscent * DEFAULT_XHEIGHT_FACTOR;
3650   }
3651 
3652   // If we have a font that doesn't provide a capHeight value, use maxAscent
3653   // as a reasonable fallback.
3654   if (aMetrics.capHeight <= 0) {
3655     aMetrics.capHeight = aMetrics.maxAscent;
3656   }
3657 
3658   aMetrics.maxHeight = aMetrics.maxAscent + aMetrics.maxDescent;
3659 
3660   if (aMetrics.maxHeight - aMetrics.emHeight > 0.0) {
3661     aMetrics.internalLeading = aMetrics.maxHeight - aMetrics.emHeight;
3662   } else {
3663     aMetrics.internalLeading = 0.0;
3664   }
3665 
3666   aMetrics.emAscent =
3667       aMetrics.maxAscent * aMetrics.emHeight / aMetrics.maxHeight;
3668   aMetrics.emDescent = aMetrics.emHeight - aMetrics.emAscent;
3669 
3670   if (GetFontEntry()->IsFixedPitch()) {
3671     // Some Quartz fonts are fixed pitch, but there's some glyph with a bigger
3672     // advance than the average character width... this forces
3673     // those fonts to be recognized like fixed pitch fonts by layout.
3674     aMetrics.maxAdvance = aMetrics.aveCharWidth;
3675   }
3676 
3677   if (!aMetrics.strikeoutOffset) {
3678     aMetrics.strikeoutOffset = aMetrics.xHeight * 0.5;
3679   }
3680   if (!aMetrics.strikeoutSize) {
3681     aMetrics.strikeoutSize = aMetrics.underlineSize;
3682   }
3683 }
3684 
SanitizeMetrics(gfxFont::Metrics * aMetrics,bool aIsBadUnderlineFont)3685 void gfxFont::SanitizeMetrics(gfxFont::Metrics* aMetrics,
3686                               bool aIsBadUnderlineFont) {
3687   // Even if this font size is zero, this font is created with non-zero size.
3688   // However, for layout and others, we should return the metrics of zero size
3689   // font.
3690   if (mStyle.AdjustedSizeMustBeZero()) {
3691     memset(aMetrics, 0, sizeof(gfxFont::Metrics));
3692     return;
3693   }
3694 
3695   // If the font entry has ascent/descent/lineGap-override values,
3696   // replace the metrics from the font with the overrides.
3697   gfxFloat adjustedSize = GetAdjustedSize();
3698   if (mFontEntry->mAscentOverride >= 0.0) {
3699     aMetrics->maxAscent = mFontEntry->mAscentOverride * adjustedSize;
3700     aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
3701     aMetrics->internalLeading =
3702         std::max(0.0, aMetrics->maxHeight - aMetrics->emHeight);
3703   }
3704   if (mFontEntry->mDescentOverride >= 0.0) {
3705     aMetrics->maxDescent = mFontEntry->mDescentOverride * adjustedSize;
3706     aMetrics->maxHeight = aMetrics->maxAscent + aMetrics->maxDescent;
3707     aMetrics->internalLeading =
3708         std::max(0.0, aMetrics->maxHeight - aMetrics->emHeight);
3709   }
3710   if (mFontEntry->mLineGapOverride >= 0.0) {
3711     aMetrics->externalLeading = mFontEntry->mLineGapOverride * adjustedSize;
3712   }
3713 
3714   aMetrics->underlineSize = std::max(1.0, aMetrics->underlineSize);
3715   aMetrics->strikeoutSize = std::max(1.0, aMetrics->strikeoutSize);
3716 
3717   aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -1.0);
3718 
3719   if (aMetrics->maxAscent < 1.0) {
3720     // We cannot draw strikeout line and overline in the ascent...
3721     aMetrics->underlineSize = 0;
3722     aMetrics->underlineOffset = 0;
3723     aMetrics->strikeoutSize = 0;
3724     aMetrics->strikeoutOffset = 0;
3725     return;
3726   }
3727 
3728   /**
3729    * Some CJK fonts have bad underline offset. Therefore, if this is such font,
3730    * we need to lower the underline offset to bottom of *em* descent.
3731    * However, if this is system font, we should not do this for the rendering
3732    * compatibility with another application's UI on the platform.
3733    * XXX Should not use this hack if the font size is too small?
3734    *     Such text cannot be read, this might be used for tight CSS
3735    *     rendering? (E.g., Acid2)
3736    */
3737   if (!mStyle.systemFont && aIsBadUnderlineFont) {
3738     // First, we need 2 pixels between baseline and underline at least. Because
3739     // many CJK characters put their glyphs on the baseline, so, 1 pixel is too
3740     // close for CJK characters.
3741     aMetrics->underlineOffset = std::min(aMetrics->underlineOffset, -2.0);
3742 
3743     // Next, we put the underline to bottom of below of the descent space.
3744     if (aMetrics->internalLeading + aMetrics->externalLeading >
3745         aMetrics->underlineSize) {
3746       aMetrics->underlineOffset =
3747           std::min(aMetrics->underlineOffset, -aMetrics->emDescent);
3748     } else {
3749       aMetrics->underlineOffset =
3750           std::min(aMetrics->underlineOffset,
3751                    aMetrics->underlineSize - aMetrics->emDescent);
3752     }
3753   }
3754   // If underline positioned is too far from the text, descent position is
3755   // preferred so that underline will stay within the boundary.
3756   else if (aMetrics->underlineSize - aMetrics->underlineOffset >
3757            aMetrics->maxDescent) {
3758     if (aMetrics->underlineSize > aMetrics->maxDescent)
3759       aMetrics->underlineSize = std::max(aMetrics->maxDescent, 1.0);
3760     // The max underlineOffset is 1px (the min underlineSize is 1px, and min
3761     // maxDescent is 0px.)
3762     aMetrics->underlineOffset = aMetrics->underlineSize - aMetrics->maxDescent;
3763   }
3764 
3765   // If strikeout line is overflowed from the ascent, the line should be resized
3766   // and moved for that being in the ascent space. Note that the strikeoutOffset
3767   // is *middle* of the strikeout line position.
3768   gfxFloat halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
3769   if (halfOfStrikeoutSize + aMetrics->strikeoutOffset > aMetrics->maxAscent) {
3770     if (aMetrics->strikeoutSize > aMetrics->maxAscent) {
3771       aMetrics->strikeoutSize = std::max(aMetrics->maxAscent, 1.0);
3772       halfOfStrikeoutSize = floor(aMetrics->strikeoutSize / 2.0 + 0.5);
3773     }
3774     gfxFloat ascent = floor(aMetrics->maxAscent + 0.5);
3775     aMetrics->strikeoutOffset = std::max(halfOfStrikeoutSize, ascent / 2.0);
3776   }
3777 
3778   // If overline is larger than the ascent, the line should be resized.
3779   if (aMetrics->underlineSize > aMetrics->maxAscent) {
3780     aMetrics->underlineSize = aMetrics->maxAscent;
3781   }
3782 }
3783 
3784 // Create a Metrics record to be used for vertical layout. This should never
3785 // fail, as we've already decided this is a valid font. We do not have the
3786 // option of marking it invalid (as can happen if we're unable to read
3787 // horizontal metrics), because that could break a font that we're already
3788 // using for horizontal text.
3789 // So we will synthesize *something* usable here even if there aren't any of the
3790 // usual font tables (which can happen in the case of a legacy bitmap or Type1
3791 // font for which the platform-specific backend used platform APIs instead of
3792 // sfnt tables to create the horizontal metrics).
CreateVerticalMetrics()3793 UniquePtr<const gfxFont::Metrics> gfxFont::CreateVerticalMetrics() {
3794   const uint32_t kHheaTableTag = TRUETYPE_TAG('h', 'h', 'e', 'a');
3795   const uint32_t kVheaTableTag = TRUETYPE_TAG('v', 'h', 'e', 'a');
3796   const uint32_t kPostTableTag = TRUETYPE_TAG('p', 'o', 's', 't');
3797   const uint32_t kOS_2TableTag = TRUETYPE_TAG('O', 'S', '/', '2');
3798   uint32_t len;
3799 
3800   UniquePtr<Metrics> metrics = MakeUnique<Metrics>();
3801   ::memset(metrics.get(), 0, sizeof(Metrics));
3802 
3803   // Some basic defaults, in case the font lacks any real metrics tables.
3804   // TODO: consider what rounding (if any) we should apply to these.
3805   metrics->emHeight = GetAdjustedSize();
3806   metrics->emAscent = metrics->emHeight / 2;
3807   metrics->emDescent = metrics->emHeight - metrics->emAscent;
3808 
3809   metrics->maxAscent = metrics->emAscent;
3810   metrics->maxDescent = metrics->emDescent;
3811 
3812   const float UNINITIALIZED_LEADING = -10000.0f;
3813   metrics->externalLeading = UNINITIALIZED_LEADING;
3814 
3815   if (mFUnitsConvFactor < 0.0) {
3816     uint16_t upem = GetFontEntry()->UnitsPerEm();
3817     if (upem != gfxFontEntry::kInvalidUPEM) {
3818       mFUnitsConvFactor = GetAdjustedSize() / upem;
3819     }
3820   }
3821 
3822 #define SET_UNSIGNED(field, src) \
3823   metrics->field = uint16_t(src) * mFUnitsConvFactor
3824 #define SET_SIGNED(field, src) metrics->field = int16_t(src) * mFUnitsConvFactor
3825 
3826   gfxFontEntry::AutoTable os2Table(mFontEntry, kOS_2TableTag);
3827   if (os2Table && mFUnitsConvFactor >= 0.0) {
3828     const OS2Table* os2 =
3829         reinterpret_cast<const OS2Table*>(hb_blob_get_data(os2Table, &len));
3830     // These fields should always be present in any valid OS/2 table
3831     if (len >= offsetof(OS2Table, sTypoLineGap) + sizeof(int16_t)) {
3832       SET_SIGNED(strikeoutSize, os2->yStrikeoutSize);
3833       // Use ascent+descent from the horizontal metrics as the default
3834       // advance (aveCharWidth) in vertical mode
3835       gfxFloat ascentDescent =
3836           gfxFloat(mFUnitsConvFactor) *
3837           (int16_t(os2->sTypoAscender) - int16_t(os2->sTypoDescender));
3838       metrics->aveCharWidth = std::max(metrics->emHeight, ascentDescent);
3839       // Use xAvgCharWidth from horizontal metrics as minimum font extent
3840       // for vertical layout, applying half of it to ascent and half to
3841       // descent (to work with a default centered baseline).
3842       gfxFloat halfCharWidth =
3843           int16_t(os2->xAvgCharWidth) * gfxFloat(mFUnitsConvFactor) / 2;
3844       metrics->maxAscent = std::max(metrics->maxAscent, halfCharWidth);
3845       metrics->maxDescent = std::max(metrics->maxDescent, halfCharWidth);
3846     }
3847   }
3848 
3849   // If we didn't set aveCharWidth from OS/2, try to read 'hhea' metrics
3850   // and use the line height from its ascent/descent.
3851   if (!metrics->aveCharWidth) {
3852     gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
3853     if (hheaTable && mFUnitsConvFactor >= 0.0) {
3854       const MetricsHeader* hhea = reinterpret_cast<const MetricsHeader*>(
3855           hb_blob_get_data(hheaTable, &len));
3856       if (len >= sizeof(MetricsHeader)) {
3857         SET_SIGNED(aveCharWidth,
3858                    int16_t(hhea->ascender) - int16_t(hhea->descender));
3859         metrics->maxAscent = metrics->aveCharWidth / 2;
3860         metrics->maxDescent = metrics->aveCharWidth - metrics->maxAscent;
3861       }
3862     }
3863   }
3864 
3865   // Read real vertical metrics if available.
3866   gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
3867   if (vheaTable && mFUnitsConvFactor >= 0.0) {
3868     const MetricsHeader* vhea = reinterpret_cast<const MetricsHeader*>(
3869         hb_blob_get_data(vheaTable, &len));
3870     if (len >= sizeof(MetricsHeader)) {
3871       SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
3872       // Redistribute space between ascent/descent because we want a
3873       // centered vertical baseline by default.
3874       gfxFloat halfExtent =
3875           0.5 * gfxFloat(mFUnitsConvFactor) *
3876           (int16_t(vhea->ascender) + std::abs(int16_t(vhea->descender)));
3877       // Some bogus fonts have ascent and descent set to zero in 'vhea'.
3878       // In that case we just ignore them and keep our synthetic values
3879       // from above.
3880       if (halfExtent > 0) {
3881         metrics->maxAscent = halfExtent;
3882         metrics->maxDescent = halfExtent;
3883         SET_SIGNED(externalLeading, vhea->lineGap);
3884       }
3885     }
3886   }
3887 
3888   // If we didn't set aveCharWidth above, we must be dealing with a non-sfnt
3889   // font of some kind (Type1, bitmap, vector, ...), so fall back to using
3890   // whatever the platform backend figured out for horizontal layout.
3891   // And if we haven't set externalLeading yet, then copy that from the
3892   // horizontal metrics as well, to help consistency of CSS line-height.
3893   if (!metrics->aveCharWidth ||
3894       metrics->externalLeading == UNINITIALIZED_LEADING) {
3895     const Metrics& horizMetrics = GetHorizontalMetrics();
3896     if (!metrics->aveCharWidth) {
3897       metrics->aveCharWidth = horizMetrics.maxAscent + horizMetrics.maxDescent;
3898     }
3899     if (metrics->externalLeading == UNINITIALIZED_LEADING) {
3900       metrics->externalLeading = horizMetrics.externalLeading;
3901     }
3902   }
3903 
3904   // Get underline thickness from the 'post' table if available.
3905   // We also read the underline position, although in vertical-upright mode
3906   // this will not be appropriate to use directly (see nsTextFrame.cpp).
3907   gfxFontEntry::AutoTable postTable(mFontEntry, kPostTableTag);
3908   if (postTable) {
3909     const PostTable* post =
3910         reinterpret_cast<const PostTable*>(hb_blob_get_data(postTable, &len));
3911     if (len >= offsetof(PostTable, underlineThickness) + sizeof(uint16_t)) {
3912       static_assert(offsetof(PostTable, underlinePosition) <
3913                         offsetof(PostTable, underlineThickness),
3914                     "broken PostTable struct?");
3915       SET_SIGNED(underlineOffset, post->underlinePosition);
3916       SET_UNSIGNED(underlineSize, post->underlineThickness);
3917       // Also use for strikeout if we didn't find that in OS/2 above.
3918       if (!metrics->strikeoutSize) {
3919         metrics->strikeoutSize = metrics->underlineSize;
3920       }
3921     }
3922   }
3923 
3924 #undef SET_UNSIGNED
3925 #undef SET_SIGNED
3926 
3927   // If we didn't read this from a vhea table, it will still be zero.
3928   // In any case, let's make sure it is not less than the value we've
3929   // come up with for aveCharWidth.
3930   metrics->maxAdvance = std::max(metrics->maxAdvance, metrics->aveCharWidth);
3931 
3932   // Thickness of underline and strikeout may have been read from tables,
3933   // but in case they were not present, ensure a minimum of 1 pixel.
3934   metrics->underlineSize = std::max(1.0, metrics->underlineSize);
3935 
3936   metrics->strikeoutSize = std::max(1.0, metrics->strikeoutSize);
3937   metrics->strikeoutOffset = -0.5 * metrics->strikeoutSize;
3938 
3939   // Somewhat arbitrary values for now, subject to future refinement...
3940   metrics->spaceWidth = metrics->aveCharWidth;
3941   metrics->zeroWidth = metrics->aveCharWidth;
3942   metrics->maxHeight = metrics->maxAscent + metrics->maxDescent;
3943   metrics->xHeight = metrics->emHeight / 2;
3944   metrics->capHeight = metrics->maxAscent;
3945 
3946   return std::move(metrics);
3947 }
3948 
SynthesizeSpaceWidth(uint32_t aCh)3949 gfxFloat gfxFont::SynthesizeSpaceWidth(uint32_t aCh) {
3950   // return an appropriate width for various Unicode space characters
3951   // that we "fake" if they're not actually present in the font;
3952   // returns negative value if the char is not a known space.
3953   switch (aCh) {
3954     case 0x2000:  // en quad
3955     case 0x2002:
3956       return GetAdjustedSize() / 2;  // en space
3957     case 0x2001:                     // em quad
3958     case 0x2003:
3959       return GetAdjustedSize();  // em space
3960     case 0x2004:
3961       return GetAdjustedSize() / 3;  // three-per-em space
3962     case 0x2005:
3963       return GetAdjustedSize() / 4;  // four-per-em space
3964     case 0x2006:
3965       return GetAdjustedSize() / 6;  // six-per-em space
3966     case 0x2007:
3967       return GetMetrics(nsFontMetrics::eHorizontal)
3968           .ZeroOrAveCharWidth();  // figure space
3969     case 0x2008:
3970       return GetMetrics(nsFontMetrics::eHorizontal)
3971           .spaceWidth;  // punctuation space
3972     case 0x2009:
3973       return GetAdjustedSize() / 5;  // thin space
3974     case 0x200a:
3975       return GetAdjustedSize() / 10;  // hair space
3976     case 0x202f:
3977       return GetAdjustedSize() / 5;  // narrow no-break space
3978     case 0x3000:
3979       return GetAdjustedSize();  // ideographic space
3980     default:
3981       return -1.0;
3982   }
3983 }
3984 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const3985 void gfxFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
3986                                      FontCacheSizes* aSizes) const {
3987   for (uint32_t i = 0; i < mGlyphExtentsArray.Length(); ++i) {
3988     aSizes->mFontInstances +=
3989         mGlyphExtentsArray[i]->SizeOfIncludingThis(aMallocSizeOf);
3990   }
3991   if (mWordCache) {
3992     aSizes->mShapedWords += mWordCache->SizeOfIncludingThis(aMallocSizeOf);
3993   }
3994 }
3995 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const3996 void gfxFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
3997                                      FontCacheSizes* aSizes) const {
3998   aSizes->mFontInstances += aMallocSizeOf(this);
3999   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
4000 }
4001 
AddGlyphChangeObserver(GlyphChangeObserver * aObserver)4002 void gfxFont::AddGlyphChangeObserver(GlyphChangeObserver* aObserver) {
4003   if (!mGlyphChangeObservers) {
4004     mGlyphChangeObservers = MakeUnique<nsTHashSet<GlyphChangeObserver*>>();
4005   }
4006   mGlyphChangeObservers->Insert(aObserver);
4007 }
4008 
RemoveGlyphChangeObserver(GlyphChangeObserver * aObserver)4009 void gfxFont::RemoveGlyphChangeObserver(GlyphChangeObserver* aObserver) {
4010   NS_ASSERTION(mGlyphChangeObservers, "No observers registered");
4011   NS_ASSERTION(mGlyphChangeObservers->Contains(aObserver),
4012                "Observer not registered");
4013   mGlyphChangeObservers->Remove(aObserver);
4014 }
4015 
4016 #define DEFAULT_PIXEL_FONT_SIZE 16.0f
4017 
gfxFontStyle()4018 gfxFontStyle::gfxFontStyle()
4019     : size(DEFAULT_PIXEL_FONT_SIZE),
4020       sizeAdjust(0.0f),
4021       baselineOffset(0.0f),
4022       languageOverride(NO_FONT_LANGUAGE_OVERRIDE),
4023       fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
4024       weight(FontWeight::Normal()),
4025       stretch(FontStretch::Normal()),
4026       style(FontSlantStyle::Normal()),
4027       variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
4028       variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
4029       sizeAdjustBasis(uint8_t(FontSizeAdjust::Tag::None)),
4030       systemFont(true),
4031       printerFont(false),
4032       useGrayscaleAntialiasing(false),
4033       allowSyntheticWeight(true),
4034       allowSyntheticStyle(true),
4035       noFallbackVariantFeatures(true) {}
4036 
gfxFontStyle(FontSlantStyle aStyle,FontWeight aWeight,FontStretch aStretch,gfxFloat aSize,const FontSizeAdjust & aSizeAdjust,bool aSystemFont,bool aPrinterFont,bool aAllowWeightSynthesis,bool aAllowStyleSynthesis,uint32_t aLanguageOverride)4037 gfxFontStyle::gfxFontStyle(FontSlantStyle aStyle, FontWeight aWeight,
4038                            FontStretch aStretch, gfxFloat aSize,
4039                            const FontSizeAdjust& aSizeAdjust, bool aSystemFont,
4040                            bool aPrinterFont, bool aAllowWeightSynthesis,
4041                            bool aAllowStyleSynthesis,
4042                            uint32_t aLanguageOverride)
4043     : size(aSize),
4044       baselineOffset(0.0f),
4045       languageOverride(aLanguageOverride),
4046       fontSmoothingBackgroundColor(NS_RGBA(0, 0, 0, 0)),
4047       weight(aWeight),
4048       stretch(aStretch),
4049       style(aStyle),
4050       variantCaps(NS_FONT_VARIANT_CAPS_NORMAL),
4051       variantSubSuper(NS_FONT_VARIANT_POSITION_NORMAL),
4052       systemFont(aSystemFont),
4053       printerFont(aPrinterFont),
4054       useGrayscaleAntialiasing(false),
4055       allowSyntheticWeight(aAllowWeightSynthesis),
4056       allowSyntheticStyle(aAllowStyleSynthesis),
4057       noFallbackVariantFeatures(true) {
4058   MOZ_ASSERT(!mozilla::IsNaN(size));
4059 
4060   switch (aSizeAdjust.tag) {
4061     case FontSizeAdjust::Tag::None:
4062       sizeAdjust = 0.0f;
4063       break;
4064     case FontSizeAdjust::Tag::ExHeight:
4065       sizeAdjust = aSizeAdjust.AsExHeight();
4066       break;
4067     case FontSizeAdjust::Tag::CapHeight:
4068       sizeAdjust = aSizeAdjust.AsCapHeight();
4069       break;
4070     case FontSizeAdjust::Tag::ChWidth:
4071       sizeAdjust = aSizeAdjust.AsChWidth();
4072       break;
4073     case FontSizeAdjust::Tag::IcWidth:
4074       sizeAdjust = aSizeAdjust.AsIcWidth();
4075       break;
4076     case FontSizeAdjust::Tag::IcHeight:
4077       sizeAdjust = aSizeAdjust.AsIcHeight();
4078       break;
4079   }
4080   MOZ_ASSERT(!mozilla::IsNaN(sizeAdjust));
4081 
4082   sizeAdjustBasis = uint8_t(aSizeAdjust.tag);
4083   // sizeAdjustBasis is currently a small bitfield, so let's assert that the
4084   // tag value was not truncated.
4085   MOZ_ASSERT(FontSizeAdjust::Tag(sizeAdjustBasis) == aSizeAdjust.tag,
4086              "gfxFontStyle.sizeAdjustBasis too small?");
4087 
4088   if (weight > FontWeight(1000)) {
4089     weight = FontWeight(1000);
4090   }
4091   if (weight < FontWeight(1)) {
4092     weight = FontWeight(1);
4093   }
4094 
4095   if (size >= FONT_MAX_SIZE) {
4096     size = FONT_MAX_SIZE;
4097     sizeAdjust = 0.0f;
4098     sizeAdjustBasis = uint8_t(FontSizeAdjust::Tag::None);
4099   } else if (size < 0.0) {
4100     NS_WARNING("negative font size");
4101     size = 0.0;
4102   }
4103 }
4104 
Hash() const4105 PLDHashNumber gfxFontStyle::Hash() const {
4106   uint32_t hash = variationSettings.IsEmpty()
4107                       ? 0
4108                       : mozilla::HashBytes(variationSettings.Elements(),
4109                                            variationSettings.Length() *
4110                                                sizeof(gfxFontVariation));
4111   return mozilla::AddToHash(hash, systemFont, style.ForHash(),
4112                             stretch.ForHash(), weight.ForHash(), size,
4113                             int32_t(sizeAdjust * 1000.0f));
4114 }
4115 
AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel)4116 void gfxFontStyle::AdjustForSubSuperscript(int32_t aAppUnitsPerDevPixel) {
4117   MOZ_ASSERT(
4118       variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL && baselineOffset == 0,
4119       "can't adjust this style for sub/superscript");
4120 
4121   // calculate the baseline offset (before changing the size)
4122   if (variantSubSuper == NS_FONT_VARIANT_POSITION_SUPER) {
4123     baselineOffset = size * -NS_FONT_SUPERSCRIPT_OFFSET_RATIO;
4124   } else {
4125     baselineOffset = size * NS_FONT_SUBSCRIPT_OFFSET_RATIO;
4126   }
4127 
4128   // calculate reduced size, roughly mimicing behavior of font-size: smaller
4129   float cssSize = size * aAppUnitsPerDevPixel / AppUnitsPerCSSPixel();
4130   if (cssSize < NS_FONT_SUB_SUPER_SMALL_SIZE) {
4131     size *= NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL;
4132   } else if (cssSize >= NS_FONT_SUB_SUPER_LARGE_SIZE) {
4133     size *= NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
4134   } else {
4135     gfxFloat t = (cssSize - NS_FONT_SUB_SUPER_SMALL_SIZE) /
4136                  (NS_FONT_SUB_SUPER_LARGE_SIZE - NS_FONT_SUB_SUPER_SMALL_SIZE);
4137     size *= (1.0 - t) * NS_FONT_SUB_SUPER_SIZE_RATIO_SMALL +
4138             t * NS_FONT_SUB_SUPER_SIZE_RATIO_LARGE;
4139   }
4140 
4141   // clear the variant field
4142   variantSubSuper = NS_FONT_VARIANT_POSITION_NORMAL;
4143 }
4144 
TryGetMathTable()4145 bool gfxFont::TryGetMathTable() {
4146   if (!mMathInitialized) {
4147     mMathInitialized = true;
4148 
4149     hb_face_t* face = GetFontEntry()->GetHBFace();
4150     if (face) {
4151       if (hb_ot_math_has_data(face)) {
4152         mMathTable = MakeUnique<gfxMathTable>(face, GetAdjustedSize());
4153       }
4154       hb_face_destroy(face);
4155     }
4156   }
4157 
4158   return !!mMathTable;
4159 }
4160