Coverage Report

Created: 2018-09-25 14:53

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