Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxTextRun.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim: set ts=4 et sw=4 tw=80: */
3
/* This Source Code Form is subject to the terms of the Mozilla Public
4
 * License, v. 2.0. If a copy of the MPL was not distributed with this
5
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7
#include "gfxTextRun.h"
8
#include "gfxGlyphExtents.h"
9
#include "gfxHarfBuzzShaper.h"
10
#include "gfxPlatformFontList.h"
11
#include "gfxUserFontSet.h"
12
#include "mozilla/gfx/2D.h"
13
#include "mozilla/gfx/PathHelpers.h"
14
#include "mozilla/Sprintf.h"
15
16
#include "gfxContext.h"
17
#include "gfxFontConstants.h"
18
#include "gfxFontMissingGlyphs.h"
19
#include "gfxScriptItemizer.h"
20
#include "nsUnicodeProperties.h"
21
#include "nsUnicodeRange.h"
22
#include "nsStyleConsts.h"
23
#include "nsStyleUtil.h"
24
#include "mozilla/Likely.h"
25
#include "gfx2DGlue.h"
26
#include "mozilla/gfx/Logging.h"        // for gfxCriticalError
27
#include "mozilla/UniquePtr.h"
28
#include "TextDrawTarget.h"
29
30
#ifdef XP_WIN
31
#include "gfxWindowsPlatform.h"
32
#endif
33
34
using namespace mozilla;
35
using namespace mozilla::gfx;
36
using namespace mozilla::unicode;
37
using mozilla::services::GetObserverService;
38
39
static const char16_t kEllipsisChar[] = { 0x2026, 0x0 };
40
static const char16_t kASCIIPeriodsChar[] = { '.', '.', '.', 0x0 };
41
42
#ifdef DEBUG_roc
43
#define DEBUG_TEXT_RUN_STORAGE_METRICS
44
#endif
45
46
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
47
extern uint32_t gTextRunStorageHighWaterMark;
48
extern uint32_t gTextRunStorage;
49
extern uint32_t gFontCount;
50
extern uint32_t gGlyphExtentsCount;
51
extern uint32_t gGlyphExtentsWidthsTotalSize;
52
extern uint32_t gGlyphExtentsSetupEagerSimple;
53
extern uint32_t gGlyphExtentsSetupEagerTight;
54
extern uint32_t gGlyphExtentsSetupLazyTight;
55
extern uint32_t gGlyphExtentsSetupFallBackToTight;
56
#endif
57
58
bool
59
gfxTextRun::GlyphRunIterator::NextRun()
60
0
{
61
0
    uint32_t glyphRunCount;
62
0
    if (mTextRun->mHasGlyphRunArray) {
63
0
        glyphRunCount = mTextRun->mGlyphRunArray.Length();
64
0
        if (mNextIndex >= glyphRunCount) {
65
0
            return false;
66
0
        }
67
0
        mGlyphRun = &mTextRun->mGlyphRunArray[mNextIndex];
68
0
    } else {
69
0
        if (mNextIndex > 0 || !mTextRun->mSingleGlyphRun.mFont) {
70
0
            return false;
71
0
        }
72
0
        glyphRunCount = 1;
73
0
        mGlyphRun = &mTextRun->mSingleGlyphRun;
74
0
    }
75
0
76
0
    if (mGlyphRun->mCharacterOffset >= mEndOffset) {
77
0
        return false;
78
0
    }
79
0
80
0
    mStringStart = std::max(mStartOffset, mGlyphRun->mCharacterOffset);
81
0
    uint32_t last = mNextIndex + 1 < glyphRunCount
82
0
        ? mTextRun->mGlyphRunArray[mNextIndex + 1].mCharacterOffset
83
0
        : mTextRun->GetLength();
84
0
    mStringEnd = std::min(mEndOffset, last);
85
0
86
0
    ++mNextIndex;
87
0
    return true;
88
0
}
89
90
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
91
static void
92
AccountStorageForTextRun(gfxTextRun *aTextRun, int32_t aSign)
93
{
94
    // Ignores detailed glyphs... we don't know when those have been constructed
95
    // Also ignores gfxSkipChars dynamic storage (which won't be anything
96
    // for preformatted text)
97
    // Also ignores GlyphRun array, again because it hasn't been constructed
98
    // by the time this gets called. If there's only one glyphrun that's stored
99
    // directly in the textrun anyway so no additional overhead.
100
    uint32_t length = aTextRun->GetLength();
101
    int32_t bytes = length * sizeof(gfxTextRun::CompressedGlyph);
102
    bytes += sizeof(gfxTextRun);
103
    gTextRunStorage += bytes*aSign;
104
    gTextRunStorageHighWaterMark = std::max(gTextRunStorageHighWaterMark, gTextRunStorage);
105
}
106
#endif
107
108
static bool
109
NeedsGlyphExtents(gfxTextRun *aTextRun)
110
0
{
111
0
    if (aTextRun->GetFlags() & gfx::ShapedTextFlags::TEXT_NEED_BOUNDING_BOX)
112
0
        return true;
113
0
    uint32_t numRuns;
114
0
    const gfxTextRun::GlyphRun *glyphRuns = aTextRun->GetGlyphRuns(&numRuns);
115
0
    for (uint32_t i = 0; i < numRuns; ++i) {
116
0
        if (glyphRuns[i].mFont->GetFontEntry()->IsUserFont())
117
0
            return true;
118
0
    }
119
0
    return false;
120
0
}
121
122
// Helper for textRun creation to preallocate storage for glyph records;
123
// this function returns a pointer to the newly-allocated glyph storage.
124
// Returns nullptr if allocation fails.
125
void *
126
gfxTextRun::AllocateStorageForTextRun(size_t aSize, uint32_t aLength)
127
0
{
128
0
    // Allocate the storage we need, returning nullptr on failure rather than
129
0
    // throwing an exception (because web content can create huge runs).
130
0
    void *storage = malloc(aSize + aLength * sizeof(CompressedGlyph));
131
0
    if (!storage) {
132
0
        NS_WARNING("failed to allocate storage for text run!");
133
0
        return nullptr;
134
0
    }
135
0
136
0
    // Initialize the glyph storage (beyond aSize) to zero
137
0
    memset(reinterpret_cast<char*>(storage) + aSize, 0,
138
0
           aLength * sizeof(CompressedGlyph));
139
0
140
0
    return storage;
141
0
}
142
143
already_AddRefed<gfxTextRun>
144
gfxTextRun::Create(const gfxTextRunFactory::Parameters *aParams,
145
                   uint32_t aLength, gfxFontGroup *aFontGroup,
146
                   gfx::ShapedTextFlags aFlags, 
147
                   nsTextFrameUtils::Flags aFlags2)
148
0
{
149
0
    void *storage = AllocateStorageForTextRun(sizeof(gfxTextRun), aLength);
150
0
    if (!storage) {
151
0
        return nullptr;
152
0
    }
153
0
154
0
    RefPtr<gfxTextRun> result = new (storage) gfxTextRun(aParams, aLength,
155
0
                                                         aFontGroup,
156
0
                                                         aFlags, aFlags2);
157
0
    return result.forget();
158
0
}
159
160
gfxTextRun::gfxTextRun(const gfxTextRunFactory::Parameters *aParams,
161
                       uint32_t aLength, gfxFontGroup *aFontGroup,
162
                       gfx::ShapedTextFlags aFlags,
163
                       nsTextFrameUtils::Flags aFlags2)
164
    : gfxShapedText(aLength, aFlags, aParams->mAppUnitsPerDevUnit)
165
    , mSingleGlyphRun()
166
    , mUserData(aParams->mUserData)
167
    , mFontGroup(aFontGroup)
168
    , mFlags2(aFlags2)
169
    , mReleasedFontGroup(false)
170
    , mHasGlyphRunArray(false)
171
    , mShapingState(eShapingState_Normal)
172
0
{
173
0
    NS_ASSERTION(mAppUnitsPerDevUnit > 0, "Invalid app unit scale");
174
0
    NS_ADDREF(mFontGroup);
175
0
176
0
#ifndef RELEASE_OR_BETA
177
0
    gfxTextPerfMetrics *tp = aFontGroup->GetTextPerfMetrics();
178
0
    if (tp) {
179
0
        tp->current.textrunConst++;
180
0
    }
181
0
#endif
182
0
183
0
    mCharacterGlyphs = reinterpret_cast<CompressedGlyph*>(this + 1);
184
0
185
0
    if (aParams->mSkipChars) {
186
0
        mSkipChars.TakeFrom(aParams->mSkipChars);
187
0
    }
188
0
189
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
190
    AccountStorageForTextRun(this, 1);
191
#endif
192
193
0
    mSkipDrawing = mFontGroup->ShouldSkipDrawing();
194
0
}
195
196
gfxTextRun::~gfxTextRun()
197
0
{
198
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
199
    AccountStorageForTextRun(this, -1);
200
#endif
201
#ifdef DEBUG
202
    // Make it easy to detect a dead text run
203
    mFlags = ~gfx::ShapedTextFlags();
204
    mFlags2 = ~nsTextFrameUtils::Flags();
205
#endif
206
207
0
    if (mHasGlyphRunArray) {
208
0
        mGlyphRunArray.~nsTArray<GlyphRun>();
209
0
    } else {
210
0
        mSingleGlyphRun.mFont = nullptr;
211
0
    }
212
0
213
0
    // The cached ellipsis textrun (if any) in a fontgroup will have already
214
0
    // been told to release its reference to the group, so we mustn't do that
215
0
    // again here.
216
0
    if (!mReleasedFontGroup) {
217
0
#ifndef RELEASE_OR_BETA
218
0
        gfxTextPerfMetrics *tp = mFontGroup->GetTextPerfMetrics();
219
0
        if (tp) {
220
0
            tp->current.textrunDestr++;
221
0
        }
222
0
#endif
223
0
        NS_RELEASE(mFontGroup);
224
0
    }
225
0
}
226
227
void
228
gfxTextRun::ReleaseFontGroup()
229
0
{
230
0
    NS_ASSERTION(!mReleasedFontGroup, "doubly released!");
231
0
    NS_RELEASE(mFontGroup);
232
0
    mReleasedFontGroup = true;
233
0
}
234
235
bool
236
gfxTextRun::SetPotentialLineBreaks(Range aRange, const uint8_t* aBreakBefore)
237
0
{
238
0
    NS_ASSERTION(aRange.end <= GetLength(), "Overflow");
239
0
240
0
    uint32_t changed = 0;
241
0
    CompressedGlyph* cg = mCharacterGlyphs + aRange.start;
242
0
    const CompressedGlyph* const end = cg + aRange.Length();
243
0
    while (cg < end) {
244
0
        uint8_t canBreak = *aBreakBefore++;
245
0
        if (canBreak && !cg->IsClusterStart()) {
246
0
            // XXX If we replace the line-breaker with one based more closely
247
0
            // on UAX#14 (e.g. using ICU), this may not be needed any more.
248
0
            // Avoid possible breaks inside a cluster, EXCEPT when the previous
249
0
            // character was a space (compare UAX#14 rules LB9, LB10).
250
0
            if (cg == mCharacterGlyphs || !(cg - 1)->CharIsSpace()) {
251
0
                canBreak = CompressedGlyph::FLAG_BREAK_TYPE_NONE;
252
0
            }
253
0
        }
254
0
        changed |= cg->SetCanBreakBefore(canBreak);
255
0
        ++cg;
256
0
    }
257
0
    return changed != 0;
258
0
}
259
260
gfxTextRun::LigatureData
261
gfxTextRun::ComputeLigatureData(Range aPartRange,
262
                                PropertyProvider *aProvider) const
263
0
{
264
0
    NS_ASSERTION(aPartRange.start < aPartRange.end,
265
0
                 "Computing ligature data for empty range");
266
0
    NS_ASSERTION(aPartRange.end <= GetLength(), "Character length overflow");
267
0
268
0
    LigatureData result;
269
0
    const CompressedGlyph *charGlyphs = mCharacterGlyphs;
270
0
271
0
    uint32_t i;
272
0
    for (i = aPartRange.start; !charGlyphs[i].IsLigatureGroupStart(); --i) {
273
0
        NS_ASSERTION(i > 0, "Ligature at the start of the run??");
274
0
    }
275
0
    result.mRange.start = i;
276
0
    for (i = aPartRange.start + 1;
277
0
         i < GetLength() && !charGlyphs[i].IsLigatureGroupStart(); ++i) {
278
0
    }
279
0
    result.mRange.end = i;
280
0
281
0
    int32_t ligatureWidth = GetAdvanceForGlyphs(result.mRange);
282
0
    // Count the number of started clusters we have seen
283
0
    uint32_t totalClusterCount = 0;
284
0
    uint32_t partClusterIndex = 0;
285
0
    uint32_t partClusterCount = 0;
286
0
    for (i = result.mRange.start; i < result.mRange.end; ++i) {
287
0
        // Treat the first character of the ligature as the start of a
288
0
        // cluster for our purposes of allocating ligature width to its
289
0
        // characters.
290
0
        if (i == result.mRange.start || charGlyphs[i].IsClusterStart()) {
291
0
            ++totalClusterCount;
292
0
            if (i < aPartRange.start) {
293
0
                ++partClusterIndex;
294
0
            } else if (i < aPartRange.end) {
295
0
                ++partClusterCount;
296
0
            }
297
0
        }
298
0
    }
299
0
    NS_ASSERTION(totalClusterCount > 0, "Ligature involving no clusters??");
300
0
    result.mPartAdvance = partClusterIndex * (ligatureWidth / totalClusterCount);
301
0
    result.mPartWidth = partClusterCount * (ligatureWidth / totalClusterCount);
302
0
303
0
    // Any rounding errors are apportioned to the final part of the ligature,
304
0
    // so that measuring all parts of a ligature and summing them is equal to
305
0
    // the ligature width.
306
0
    if (aPartRange.end == result.mRange.end) {
307
0
        gfxFloat allParts = totalClusterCount * (ligatureWidth / totalClusterCount);
308
0
        result.mPartWidth += ligatureWidth - allParts;
309
0
    }
310
0
311
0
    if (partClusterCount == 0) {
312
0
        // nothing to draw
313
0
        result.mClipBeforePart = result.mClipAfterPart = true;
314
0
    } else {
315
0
        // Determine whether we should clip before or after this part when
316
0
        // drawing its slice of the ligature.
317
0
        // We need to clip before the part if any cluster is drawn before
318
0
        // this part.
319
0
        result.mClipBeforePart = partClusterIndex > 0;
320
0
        // We need to clip after the part if any cluster is drawn after
321
0
        // this part.
322
0
        result.mClipAfterPart = partClusterIndex + partClusterCount < totalClusterCount;
323
0
    }
324
0
325
0
    if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
326
0
        gfxFont::Spacing spacing;
327
0
        if (aPartRange.start == result.mRange.start) {
328
0
            aProvider->GetSpacing(
329
0
                Range(aPartRange.start, aPartRange.start + 1), &spacing);
330
0
            result.mPartWidth += spacing.mBefore;
331
0
        }
332
0
        if (aPartRange.end == result.mRange.end) {
333
0
            aProvider->GetSpacing(
334
0
                Range(aPartRange.end - 1, aPartRange.end), &spacing);
335
0
            result.mPartWidth += spacing.mAfter;
336
0
        }
337
0
    }
338
0
339
0
    return result;
340
0
}
341
342
gfxFloat
343
gfxTextRun::ComputePartialLigatureWidth(Range aPartRange,
344
                                        PropertyProvider *aProvider) const
345
0
{
346
0
    if (aPartRange.start >= aPartRange.end)
347
0
        return 0;
348
0
    LigatureData data = ComputeLigatureData(aPartRange, aProvider);
349
0
    return data.mPartWidth;
350
0
}
351
352
int32_t
353
gfxTextRun::GetAdvanceForGlyphs(Range aRange) const
354
0
{
355
0
    int32_t advance = 0;
356
0
    for (auto i = aRange.start; i < aRange.end; ++i) {
357
0
        advance += GetAdvanceForGlyph(i);
358
0
    }
359
0
    return advance;
360
0
}
361
362
static void
363
GetAdjustedSpacing(const gfxTextRun *aTextRun, gfxTextRun::Range aRange,
364
                   gfxTextRun::PropertyProvider *aProvider,
365
                   gfxTextRun::PropertyProvider::Spacing *aSpacing)
366
0
{
367
0
    if (aRange.start >= aRange.end)
368
0
        return;
369
0
370
0
    aProvider->GetSpacing(aRange, aSpacing);
371
0
372
#ifdef DEBUG
373
    // Check to see if we have spacing inside ligatures
374
375
    const gfxTextRun::CompressedGlyph *charGlyphs = aTextRun->GetCharacterGlyphs();
376
    uint32_t i;
377
378
    for (i = aRange.start; i < aRange.end; ++i) {
379
        if (!charGlyphs[i].IsLigatureGroupStart()) {
380
            NS_ASSERTION(i == aRange.start ||
381
                         aSpacing[i - aRange.start].mBefore == 0,
382
                         "Before-spacing inside a ligature!");
383
            NS_ASSERTION(i - 1 <= aRange.start ||
384
                         aSpacing[i - 1 - aRange.start].mAfter == 0,
385
                         "After-spacing inside a ligature!");
386
        }
387
    }
388
#endif
389
}
390
391
bool
392
gfxTextRun::GetAdjustedSpacingArray(Range aRange, PropertyProvider *aProvider,
393
                                    Range aSpacingRange,
394
                                    nsTArray<PropertyProvider::Spacing>*
395
                                        aSpacing) const
396
0
{
397
0
    if (!aProvider || !(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING))
398
0
        return false;
399
0
    if (!aSpacing->AppendElements(aRange.Length()))
400
0
        return false;
401
0
    auto spacingOffset = aSpacingRange.start - aRange.start;
402
0
    memset(aSpacing->Elements(), 0, sizeof(gfxFont::Spacing) * spacingOffset);
403
0
    GetAdjustedSpacing(this, aSpacingRange, aProvider,
404
0
                       aSpacing->Elements() + spacingOffset);
405
0
    memset(aSpacing->Elements() + aSpacingRange.end - aRange.start, 0,
406
0
           sizeof(gfxFont::Spacing) * (aRange.end - aSpacingRange.end));
407
0
    return true;
408
0
}
409
410
void
411
gfxTextRun::ShrinkToLigatureBoundaries(Range* aRange) const
412
0
{
413
0
    if (aRange->start >= aRange->end)
414
0
        return;
415
0
416
0
    const CompressedGlyph *charGlyphs = mCharacterGlyphs;
417
0
418
0
    while (aRange->start < aRange->end &&
419
0
           !charGlyphs[aRange->start].IsLigatureGroupStart()) {
420
0
        ++aRange->start;
421
0
    }
422
0
    if (aRange->end < GetLength()) {
423
0
        while (aRange->end > aRange->start &&
424
0
               !charGlyphs[aRange->end].IsLigatureGroupStart()) {
425
0
            --aRange->end;
426
0
        }
427
0
    }
428
0
}
429
430
void
431
gfxTextRun::DrawGlyphs(gfxFont* aFont, Range aRange, gfx::Point* aPt,
432
                       PropertyProvider* aProvider, Range aSpacingRange,
433
                       TextRunDrawParams& aParams,
434
                       gfx::ShapedTextFlags aOrientation) const
435
0
{
436
0
    AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
437
0
    bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
438
0
                                               aSpacingRange, &spacingBuffer);
439
0
    aParams.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
440
0
    aFont->Draw(this, aRange.start, aRange.end, aPt, aParams, aOrientation);
441
0
}
442
443
static void
444
ClipPartialLigature(const gfxTextRun* aTextRun,
445
                    gfxFloat *aStart, gfxFloat *aEnd,
446
                    gfxFloat aOrigin,
447
                    gfxTextRun::LigatureData *aLigature)
448
0
{
449
0
    if (aLigature->mClipBeforePart) {
450
0
        if (aTextRun->IsRightToLeft()) {
451
0
            *aEnd = std::min(*aEnd, aOrigin);
452
0
        } else {
453
0
            *aStart = std::max(*aStart, aOrigin);
454
0
        }
455
0
    }
456
0
    if (aLigature->mClipAfterPart) {
457
0
        gfxFloat endEdge =
458
0
            aOrigin + aTextRun->GetDirection() * aLigature->mPartWidth;
459
0
        if (aTextRun->IsRightToLeft()) {
460
0
            *aStart = std::max(*aStart, endEdge);
461
0
        } else {
462
0
            *aEnd = std::min(*aEnd, endEdge);
463
0
        }
464
0
    }
465
0
}
466
467
void
468
gfxTextRun::DrawPartialLigature(gfxFont* aFont, Range aRange,
469
                                gfx::Point* aPt, PropertyProvider* aProvider,
470
                                TextRunDrawParams& aParams,
471
                                gfx::ShapedTextFlags aOrientation) const
472
0
{
473
0
    if (aRange.start >= aRange.end) {
474
0
        return;
475
0
    }
476
0
477
0
    // Draw partial ligature. We hack this by clipping the ligature.
478
0
    LigatureData data = ComputeLigatureData(aRange, aProvider);
479
0
    gfxRect clipExtents = aParams.context->GetClipExtents();
480
0
    gfxFloat start, end;
481
0
    if (aParams.isVerticalRun) {
482
0
        start = clipExtents.Y() * mAppUnitsPerDevUnit;
483
0
        end = clipExtents.YMost() * mAppUnitsPerDevUnit;
484
0
        ClipPartialLigature(this, &start, &end, aPt->y, &data);
485
0
    } else {
486
0
        start = clipExtents.X() * mAppUnitsPerDevUnit;
487
0
        end = clipExtents.XMost() * mAppUnitsPerDevUnit;
488
0
        ClipPartialLigature(this, &start, &end, aPt->x, &data);
489
0
    }
490
0
491
0
    {
492
0
      // use division here to ensure that when the rect is aligned on multiples
493
0
      // of mAppUnitsPerDevUnit, we clip to true device unit boundaries.
494
0
      // Also, make sure we snap the rectangle to device pixels.
495
0
      Rect clipRect = aParams.isVerticalRun ?
496
0
          Rect(clipExtents.X(), start / mAppUnitsPerDevUnit,
497
0
               clipExtents.Width(), (end - start) / mAppUnitsPerDevUnit) :
498
0
          Rect(start / mAppUnitsPerDevUnit, clipExtents.Y(),
499
0
               (end - start) / mAppUnitsPerDevUnit, clipExtents.Height());
500
0
      MaybeSnapToDevicePixels(clipRect, *aParams.dt, true);
501
0
502
0
      aParams.context->Clip(clipRect);
503
0
    }
504
0
505
0
    gfx::Point pt;
506
0
    if (aParams.isVerticalRun) {
507
0
        pt = Point(aPt->x, aPt->y - aParams.direction * data.mPartAdvance);
508
0
    } else {
509
0
        pt = Point(aPt->x - aParams.direction * data.mPartAdvance, aPt->y);
510
0
    }
511
0
512
0
    DrawGlyphs(aFont, data.mRange, &pt,
513
0
               aProvider, aRange, aParams, aOrientation);
514
0
    aParams.context->PopClip();
515
0
516
0
    if (aParams.isVerticalRun) {
517
0
        aPt->y += aParams.direction * data.mPartWidth;
518
0
    } else {
519
0
        aPt->x += aParams.direction * data.mPartWidth;
520
0
    }
521
0
}
522
523
// Returns true if a glyph run is using a font with synthetic bolding enabled,
524
// or a color font (COLR/SVG/sbix/CBDT), false otherwise. This is used to
525
// check whether the text run needs to be explicitly composited in order to
526
// support opacity.
527
static bool
528
HasSyntheticBoldOrColor(const gfxTextRun *aRun, gfxTextRun::Range aRange)
529
0
{
530
0
    gfxTextRun::GlyphRunIterator iter(aRun, aRange);
531
0
    while (iter.NextRun()) {
532
0
        gfxFont *font = iter.GetGlyphRun()->mFont;
533
0
        if (font) {
534
0
            if (font->IsSyntheticBold()) {
535
0
                return true;
536
0
            }
537
0
            gfxFontEntry* fe = font->GetFontEntry();
538
0
            if (fe->TryGetSVGData(font) || fe->TryGetColorGlyphs()) {
539
0
                return true;
540
0
            }
541
#if defined(XP_MACOSX) // sbix fonts only supported via Core Text
542
            if (fe->HasFontTable(TRUETYPE_TAG('s', 'b', 'i', 'x'))) {
543
                return true;
544
            }
545
#endif
546
        }
547
0
    }
548
0
    return false;
549
0
}
550
551
// helper class for double-buffering drawing with non-opaque color
552
struct MOZ_STACK_CLASS BufferAlphaColor {
553
    explicit BufferAlphaColor(gfxContext *aContext)
554
        : mContext(aContext)
555
0
    {
556
0
557
0
    }
558
559
0
    ~BufferAlphaColor() {}
560
561
    void PushSolidColor(const gfxRect& aBounds, const Color& aAlphaColor, uint32_t appsPerDevUnit)
562
0
    {
563
0
        mContext->Save();
564
0
        mContext->NewPath();
565
0
        mContext->Rectangle(gfxRect(aBounds.X() / appsPerDevUnit,
566
0
                    aBounds.Y() / appsPerDevUnit,
567
0
                    aBounds.Width() / appsPerDevUnit,
568
0
                    aBounds.Height() / appsPerDevUnit), true);
569
0
        mContext->Clip();
570
0
        mContext->SetColor(Color(aAlphaColor.r, aAlphaColor.g, aAlphaColor.b));
571
0
        mContext->PushGroupForBlendBack(gfxContentType::COLOR_ALPHA, aAlphaColor.a);
572
0
    }
573
574
    void PopAlpha()
575
0
    {
576
0
        // pop the text, using the color alpha as the opacity
577
0
        mContext->PopGroupAndBlend();
578
0
        mContext->Restore();
579
0
    }
580
581
    gfxContext *mContext;
582
};
583
584
void
585
gfxTextRun::Draw(Range aRange, gfx::Point aPt, const DrawParams& aParams) const
586
0
{
587
0
    NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
588
0
    NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH ||
589
0
                 !(aParams.drawMode & DrawMode::GLYPH_PATH),
590
0
                 "GLYPH_PATH cannot be used with GLYPH_FILL, GLYPH_STROKE or GLYPH_STROKE_UNDERNEATH");
591
0
    NS_ASSERTION(aParams.drawMode == DrawMode::GLYPH_PATH || !aParams.callbacks,
592
0
                 "callback must not be specified unless using GLYPH_PATH");
593
0
594
0
    bool skipDrawing = mSkipDrawing;
595
0
    if (aParams.drawMode & DrawMode::GLYPH_FILL) {
596
0
        Color currentColor;
597
0
        if (aParams.context->GetDeviceColor(currentColor) &&
598
0
            currentColor.a == 0 && !aParams.context->GetTextDrawer()) {
599
0
            skipDrawing = true;
600
0
        }
601
0
    }
602
0
603
0
    gfxFloat direction = GetDirection();
604
0
605
0
    if (skipDrawing) {
606
0
        // We don't need to draw anything;
607
0
        // but if the caller wants advance width, we need to compute it here
608
0
        if (aParams.advanceWidth) {
609
0
            gfxTextRun::Metrics metrics = MeasureText(
610
0
                aRange, gfxFont::LOOSE_INK_EXTENTS,
611
0
                aParams.context->GetDrawTarget(), aParams.provider);
612
0
            *aParams.advanceWidth = metrics.mAdvanceWidth * direction;
613
0
        }
614
0
615
0
        // return without drawing
616
0
        return;
617
0
    }
618
0
619
0
    // synthetic bolding draws glyphs twice ==> colors with opacity won't draw
620
0
    // correctly unless first drawn without alpha
621
0
    BufferAlphaColor syntheticBoldBuffer(aParams.context);
622
0
    Color currentColor;
623
0
    bool needToRestore = false;
624
0
625
0
    if (aParams.drawMode & DrawMode::GLYPH_FILL &&
626
0
        aParams.context->HasNonOpaqueNonTransparentColor(currentColor) &&
627
0
        HasSyntheticBoldOrColor(this, aRange) &&
628
0
        !aParams.context->GetTextDrawer()) {
629
0
630
0
        needToRestore = true;
631
0
        // Measure text; use the bounding box to determine the area we need
632
0
        // to buffer.
633
0
        gfxTextRun::Metrics metrics = MeasureText(
634
0
            aRange, gfxFont::LOOSE_INK_EXTENTS,
635
0
            aParams.context->GetDrawTarget(), aParams.provider);
636
0
        if (IsRightToLeft()) {
637
0
            metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x - metrics.mAdvanceWidth,
638
0
                                                 aPt.y));
639
0
        } else {
640
0
            metrics.mBoundingBox.MoveBy(gfxPoint(aPt.x, aPt.y));
641
0
        }
642
0
        syntheticBoldBuffer.PushSolidColor(metrics.mBoundingBox, currentColor,
643
0
                                           GetAppUnitsPerDevUnit());
644
0
    }
645
0
646
0
    // Set up parameters that will be constant across all glyph runs we need
647
0
    // to draw, regardless of the font used.
648
0
    TextRunDrawParams params;
649
0
    params.context = aParams.context;
650
0
    params.devPerApp = 1.0 / double(GetAppUnitsPerDevUnit());
651
0
    params.isVerticalRun = IsVertical();
652
0
    params.isRTL = IsRightToLeft();
653
0
    params.direction = direction;
654
0
    params.strokeOpts = aParams.strokeOpts;
655
0
    params.textStrokeColor = aParams.textStrokeColor;
656
0
    params.textStrokePattern = aParams.textStrokePattern;
657
0
    params.drawOpts = aParams.drawOpts;
658
0
    params.drawMode = aParams.drawMode;
659
0
    params.callbacks = aParams.callbacks;
660
0
    params.runContextPaint = aParams.contextPaint;
661
0
    params.paintSVGGlyphs = !aParams.callbacks ||
662
0
        aParams.callbacks->mShouldPaintSVGGlyphs;
663
0
    params.dt = aParams.context->GetDrawTarget();
664
0
665
0
    GlyphRunIterator iter(this, aRange);
666
0
    gfxFloat advance = 0.0;
667
0
668
0
    while (iter.NextRun()) {
669
0
        gfxFont *font = iter.GetGlyphRun()->mFont;
670
0
        uint32_t start = iter.GetStringStart();
671
0
        uint32_t end = iter.GetStringEnd();
672
0
        Range ligatureRange(start, end);
673
0
        ShrinkToLigatureBoundaries(&ligatureRange);
674
0
675
0
        bool drawPartial = (aParams.drawMode & DrawMode::GLYPH_FILL) ||
676
0
                           (aParams.drawMode == DrawMode::GLYPH_PATH &&
677
0
                            aParams.callbacks);
678
0
        gfx::Point origPt = aPt;
679
0
680
0
        if (drawPartial) {
681
0
            DrawPartialLigature(font, Range(start, ligatureRange.start),
682
0
                                &aPt, aParams.provider, params,
683
0
                                iter.GetGlyphRun()->mOrientation);
684
0
        }
685
0
686
0
        DrawGlyphs(font, ligatureRange, &aPt,
687
0
                   aParams.provider, ligatureRange, params,
688
0
                   iter.GetGlyphRun()->mOrientation);
689
0
690
0
        if (drawPartial) {
691
0
            DrawPartialLigature(font, Range(ligatureRange.end, end),
692
0
                                &aPt, aParams.provider, params,
693
0
                                iter.GetGlyphRun()->mOrientation);
694
0
        }
695
0
696
0
        if (params.isVerticalRun) {
697
0
            advance += (aPt.y - origPt.y) * params.direction;
698
0
        } else {
699
0
            advance += (aPt.x - origPt.x) * params.direction;
700
0
        }
701
0
    }
702
0
703
0
    // composite result when synthetic bolding used
704
0
    if (needToRestore) {
705
0
        syntheticBoldBuffer.PopAlpha();
706
0
    }
707
0
708
0
    if (aParams.advanceWidth) {
709
0
        *aParams.advanceWidth = advance;
710
0
    }
711
0
}
712
713
// This method is mostly parallel to Draw().
714
void
715
gfxTextRun::DrawEmphasisMarks(gfxContext *aContext,
716
                              gfxTextRun* aMark,
717
                              gfxFloat aMarkAdvance, gfx::Point aPt,
718
                              Range aRange, PropertyProvider* aProvider) const
719
0
{
720
0
    MOZ_ASSERT(aRange.end <= GetLength());
721
0
722
0
    EmphasisMarkDrawParams params;
723
0
    params.context = aContext;
724
0
    params.mark = aMark;
725
0
    params.advance = aMarkAdvance;
726
0
    params.direction = GetDirection();
727
0
    params.isVertical = IsVertical();
728
0
729
0
    float& inlineCoord = params.isVertical ? aPt.y : aPt.x;
730
0
    float direction = params.direction;
731
0
732
0
    GlyphRunIterator iter(this, aRange);
733
0
    while (iter.NextRun()) {
734
0
        gfxFont* font = iter.GetGlyphRun()->mFont;
735
0
        uint32_t start = iter.GetStringStart();
736
0
        uint32_t end = iter.GetStringEnd();
737
0
        Range ligatureRange(start, end);
738
0
        ShrinkToLigatureBoundaries(&ligatureRange);
739
0
740
0
        inlineCoord += direction * ComputePartialLigatureWidth(
741
0
            Range(start, ligatureRange.start), aProvider);
742
0
743
0
        AutoTArray<PropertyProvider::Spacing, 200> spacingBuffer;
744
0
        bool haveSpacing = GetAdjustedSpacingArray(
745
0
            ligatureRange, aProvider, ligatureRange, &spacingBuffer);
746
0
        params.spacing = haveSpacing ? spacingBuffer.Elements() : nullptr;
747
0
        font->DrawEmphasisMarks(this, &aPt, ligatureRange.start,
748
0
                                ligatureRange.Length(), params);
749
0
750
0
        inlineCoord += direction * ComputePartialLigatureWidth(
751
0
            Range(ligatureRange.end, end), aProvider);
752
0
    }
753
0
}
754
755
void
756
gfxTextRun::AccumulateMetricsForRun(gfxFont *aFont, Range aRange,
757
                                    gfxFont::BoundingBoxType aBoundingBoxType,
758
                                    DrawTarget* aRefDrawTarget,
759
                                    PropertyProvider *aProvider,
760
                                    Range aSpacingRange,
761
                                    gfx::ShapedTextFlags aOrientation,
762
                                    Metrics *aMetrics) const
763
0
{
764
0
    AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
765
0
    bool haveSpacing = GetAdjustedSpacingArray(aRange, aProvider,
766
0
                                               aSpacingRange, &spacingBuffer);
767
0
    Metrics metrics = aFont->Measure(this, aRange.start, aRange.end,
768
0
                                     aBoundingBoxType, aRefDrawTarget,
769
0
                                     haveSpacing ? spacingBuffer.Elements() : nullptr,
770
0
                                     aOrientation);
771
0
    aMetrics->CombineWith(metrics, IsRightToLeft());
772
0
}
773
774
void
775
gfxTextRun::AccumulatePartialLigatureMetrics(gfxFont *aFont, Range aRange,
776
    gfxFont::BoundingBoxType aBoundingBoxType, DrawTarget* aRefDrawTarget,
777
    PropertyProvider *aProvider, gfx::ShapedTextFlags aOrientation,
778
    Metrics *aMetrics) const
779
0
{
780
0
    if (aRange.start >= aRange.end)
781
0
        return;
782
0
783
0
    // Measure partial ligature. We hack this by clipping the metrics in the
784
0
    // same way we clip the drawing.
785
0
    LigatureData data = ComputeLigatureData(aRange, aProvider);
786
0
787
0
    // First measure the complete ligature
788
0
    Metrics metrics;
789
0
    AccumulateMetricsForRun(aFont, data.mRange,
790
0
                            aBoundingBoxType, aRefDrawTarget,
791
0
                            aProvider, aRange, aOrientation, &metrics);
792
0
793
0
    // Clip the bounding box to the ligature part
794
0
    gfxFloat bboxLeft = metrics.mBoundingBox.X();
795
0
    gfxFloat bboxRight = metrics.mBoundingBox.XMost();
796
0
    // Where we are going to start "drawing" relative to our left baseline origin
797
0
    gfxFloat origin = IsRightToLeft() ? metrics.mAdvanceWidth - data.mPartAdvance : 0;
798
0
    ClipPartialLigature(this, &bboxLeft, &bboxRight, origin, &data);
799
0
    metrics.mBoundingBox.SetBoxX(bboxLeft, bboxRight);
800
0
801
0
    // mBoundingBox is now relative to the left baseline origin for the entire
802
0
    // ligature. Shift it left.
803
0
    metrics.mBoundingBox.MoveByX(-(IsRightToLeft() ? metrics.mAdvanceWidth - (data.mPartAdvance + data.mPartWidth) : data.mPartAdvance));
804
0
    metrics.mAdvanceWidth = data.mPartWidth;
805
0
806
0
    aMetrics->CombineWith(metrics, IsRightToLeft());
807
0
}
808
809
gfxTextRun::Metrics
810
gfxTextRun::MeasureText(Range aRange,
811
                        gfxFont::BoundingBoxType aBoundingBoxType,
812
                        DrawTarget* aRefDrawTarget,
813
                        PropertyProvider *aProvider) const
814
0
{
815
0
    NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
816
0
817
0
    Metrics accumulatedMetrics;
818
0
    GlyphRunIterator iter(this, aRange);
819
0
    while (iter.NextRun()) {
820
0
        gfxFont *font = iter.GetGlyphRun()->mFont;
821
0
        uint32_t start = iter.GetStringStart();
822
0
        uint32_t end = iter.GetStringEnd();
823
0
        Range ligatureRange(start, end);
824
0
        ShrinkToLigatureBoundaries(&ligatureRange);
825
0
826
0
        AccumulatePartialLigatureMetrics(
827
0
            font, Range(start, ligatureRange.start),
828
0
            aBoundingBoxType, aRefDrawTarget, aProvider,
829
0
            iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
830
0
831
0
        // XXX This sucks. We have to get glyph extents just so we can detect
832
0
        // glyphs outside the font box, even when aBoundingBoxType is LOOSE,
833
0
        // even though in almost all cases we could get correct results just
834
0
        // by getting some ascent/descent from the font and using our stored
835
0
        // advance widths.
836
0
        AccumulateMetricsForRun(font,
837
0
            ligatureRange, aBoundingBoxType,
838
0
            aRefDrawTarget, aProvider, ligatureRange,
839
0
            iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
840
0
841
0
        AccumulatePartialLigatureMetrics(
842
0
            font, Range(ligatureRange.end, end),
843
0
            aBoundingBoxType, aRefDrawTarget, aProvider,
844
0
            iter.GetGlyphRun()->mOrientation, &accumulatedMetrics);
845
0
    }
846
0
847
0
    return accumulatedMetrics;
848
0
}
849
850
0
#define MEASUREMENT_BUFFER_SIZE 100
851
852
void
853
gfxTextRun::ClassifyAutoHyphenations(uint32_t aStart, Range aRange,
854
                                     nsTArray<HyphenType>& aHyphenBuffer,
855
                                     HyphenationState* aWordState)
856
0
{
857
0
  MOZ_ASSERT(aRange.end - aStart <= aHyphenBuffer.Length() &&
858
0
             aRange.start >= aStart, "Range out of bounds");
859
0
  MOZ_ASSERT(aWordState->mostRecentBoundary >= aStart,
860
0
             "Unexpected aMostRecentWordBoundary!!");
861
0
862
0
  uint32_t start = std::min<uint32_t>(aRange.start, aWordState->mostRecentBoundary);
863
0
864
0
  for (uint32_t i = start; i < aRange.end; ++i) {
865
0
    if (aHyphenBuffer[i - aStart] == HyphenType::Explicit &&
866
0
        !aWordState->hasExplicitHyphen) {
867
0
      aWordState->hasExplicitHyphen = true;
868
0
    }
869
0
    if (!aWordState->hasManualHyphen &&
870
0
        (aHyphenBuffer[i - aStart] == HyphenType::Soft ||
871
0
         aHyphenBuffer[i - aStart] == HyphenType::Explicit)) {
872
0
      aWordState->hasManualHyphen = true;
873
0
      // This is the first manual hyphen in the current word. We can only
874
0
      // know if the current word has a manual hyphen until now. So, we need
875
0
      // to run a sub loop to update the auto hyphens between the start of
876
0
      // the current word and this manual hyphen.
877
0
      if (aWordState->hasAutoHyphen) {
878
0
        for (uint32_t j = aWordState->mostRecentBoundary; j < i; j++) {
879
0
          if (aHyphenBuffer[j - aStart] == HyphenType::AutoWithoutManualInSameWord) {
880
0
            aHyphenBuffer[j - aStart] = HyphenType::AutoWithManualInSameWord;
881
0
          }
882
0
        }
883
0
      }
884
0
    }
885
0
    if (aHyphenBuffer[i - aStart] == HyphenType::AutoWithoutManualInSameWord) {
886
0
      if (!aWordState->hasAutoHyphen) {
887
0
        aWordState->hasAutoHyphen = true;
888
0
      }
889
0
      if (aWordState->hasManualHyphen) {
890
0
        aHyphenBuffer[i - aStart] = HyphenType::AutoWithManualInSameWord;
891
0
      }
892
0
    }
893
0
894
0
    // If we're at the word boundary, clear/reset couple states.
895
0
    if (mCharacterGlyphs[i].CharIsSpace() ||
896
0
        mCharacterGlyphs[i].CharIsTab() ||
897
0
        mCharacterGlyphs[i].CharIsNewline() ||
898
0
        // Since we will not have a boundary in the end of the string, let's
899
0
        // call the end of the string a special case for word boundary.
900
0
        i == GetLength() - 1) {
901
0
      // We can only get to know whether we should raise/clear an explicit
902
0
      // manual hyphen until we get to the end of a word, because this depends
903
0
      // on whether there exists at least one auto hyphen in the same word.
904
0
      if (!aWordState->hasAutoHyphen && aWordState->hasExplicitHyphen) {
905
0
        for (uint32_t j = aWordState->mostRecentBoundary; j <= i; j++) {
906
0
          if (aHyphenBuffer[j - aStart] == HyphenType::Explicit) {
907
0
            aHyphenBuffer[j - aStart] = HyphenType::None;
908
0
          }
909
0
        }
910
0
      }
911
0
      aWordState->mostRecentBoundary = i;
912
0
      aWordState->hasManualHyphen = false;
913
0
      aWordState->hasAutoHyphen = false;
914
0
      aWordState->hasExplicitHyphen = false;
915
0
    }
916
0
  }
917
0
}
918
919
uint32_t
920
gfxTextRun::BreakAndMeasureText(uint32_t aStart, uint32_t aMaxLength,
921
                                bool aLineBreakBefore, gfxFloat aWidth,
922
                                PropertyProvider *aProvider,
923
                                SuppressBreak aSuppressBreak,
924
                                gfxFloat *aTrimWhitespace,
925
                                bool aWhitespaceCanHang,
926
                                Metrics *aMetrics,
927
                                gfxFont::BoundingBoxType aBoundingBoxType,
928
                                DrawTarget* aRefDrawTarget,
929
                                bool *aUsedHyphenation,
930
                                uint32_t *aLastBreak,
931
                                bool aCanWordWrap,
932
                                gfxBreakPriority *aBreakPriority)
933
0
{
934
0
    aMaxLength = std::min(aMaxLength, GetLength() - aStart);
935
0
936
0
    NS_ASSERTION(aStart + aMaxLength <= GetLength(), "Substring out of range");
937
0
938
0
    Range bufferRange(aStart, aStart +
939
0
        std::min<uint32_t>(aMaxLength, MEASUREMENT_BUFFER_SIZE));
940
0
    PropertyProvider::Spacing spacingBuffer[MEASUREMENT_BUFFER_SIZE];
941
0
    bool haveSpacing = aProvider &&
942
0
        !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING);
943
0
    if (haveSpacing) {
944
0
        GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
945
0
    }
946
0
    AutoTArray<HyphenType, 4096> hyphenBuffer;
947
0
    HyphenationState wordState;
948
0
    wordState.mostRecentBoundary = aStart;
949
0
    bool haveHyphenation = aProvider &&
950
0
        (aProvider->GetHyphensOption() == StyleHyphens::Auto ||
951
0
         (aProvider->GetHyphensOption() == StyleHyphens::Manual &&
952
0
          !!(mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_HYPHEN_BREAKS)));
953
0
    if (haveHyphenation) {
954
0
        if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
955
0
            aProvider->GetHyphenationBreaks(bufferRange, hyphenBuffer.Elements());
956
0
            if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
957
0
                ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
958
0
                                         &wordState);
959
0
            }
960
0
        } else {
961
0
            haveHyphenation = false;
962
0
        }
963
0
    }
964
0
965
0
    gfxFloat width = 0;
966
0
    gfxFloat advance = 0;
967
0
    // The number of space characters that can be trimmed or hang at a soft-wrap
968
0
    uint32_t trimmableChars = 0;
969
0
    // The amount of space removed by ignoring trimmableChars
970
0
    gfxFloat trimmableAdvance = 0;
971
0
    int32_t lastBreak = -1;
972
0
    int32_t lastBreakTrimmableChars = -1;
973
0
    gfxFloat lastBreakTrimmableAdvance = -1;
974
0
    // Cache the last candidate break
975
0
    int32_t lastCandidateBreak = -1;
976
0
    int32_t lastCandidateBreakTrimmableChars = -1;
977
0
    gfxFloat lastCandidateBreakTrimmableAdvance = -1;
978
0
    bool lastCandidateBreakUsedHyphenation = false;
979
0
    gfxBreakPriority lastCandidateBreakPriority = gfxBreakPriority::eNoBreak;
980
0
    bool aborted = false;
981
0
    uint32_t end = aStart + aMaxLength;
982
0
    bool lastBreakUsedHyphenation = false;
983
0
    Range ligatureRange(aStart, end);
984
0
    ShrinkToLigatureBoundaries(&ligatureRange);
985
0
986
0
    // We may need to move `i` backwards in the following loop, and re-scan
987
0
    // part of the textrun; we'll use `rescanLimit` so we can tell when that
988
0
    // is happening: if `i < rescanLimit` then we're rescanning.
989
0
    uint32_t rescanLimit = aStart;
990
0
    for (uint32_t i = aStart; i < end; ++i) {
991
0
        if (i >= bufferRange.end) {
992
0
            // Fetch more spacing and hyphenation data
993
0
            uint32_t oldHyphenBufferLength = hyphenBuffer.Length();
994
0
            bufferRange.start = i;
995
0
            bufferRange.end = std::min(aStart + aMaxLength,
996
0
                                       i + MEASUREMENT_BUFFER_SIZE);
997
0
            // For spacing, we always overwrite the old data with the newly
998
0
            // fetched one. However, for hyphenation, hyphenation data sometimes
999
0
            // depends on the context in every word (if "hyphens: auto" is set).
1000
0
            // To ensure we get enough information between neighboring buffers,
1001
0
            // we grow the hyphenBuffer instead of overwrite it.
1002
0
            // NOTE that this means bufferRange does not correspond to the
1003
0
            // entire hyphenBuffer, but only to the most recently added portion.
1004
0
            // Therefore, we need to add the old length to hyphenBuffer.Elements()
1005
0
            // when getting more data.
1006
0
            if (haveSpacing) {
1007
0
                GetAdjustedSpacing(this, bufferRange, aProvider, spacingBuffer);
1008
0
            }
1009
0
            if (haveHyphenation) {
1010
0
                if (hyphenBuffer.AppendElements(bufferRange.Length(), fallible)) {
1011
0
                    aProvider->GetHyphenationBreaks(
1012
0
                        bufferRange, hyphenBuffer.Elements() + oldHyphenBufferLength);
1013
0
                    if (aProvider->GetHyphensOption() == StyleHyphens::Auto) {
1014
0
                        uint32_t prevMostRecentWordBoundary = wordState.mostRecentBoundary;
1015
0
                        ClassifyAutoHyphenations(aStart, bufferRange, hyphenBuffer,
1016
0
                                                 &wordState);
1017
0
                        // If the buffer boundary is in the middle of a word,
1018
0
                        // we need to go back to the start of the current word.
1019
0
                        // So, we can correct the wrong candidates that we set
1020
0
                        // in the previous runs of the loop.
1021
0
                        if (prevMostRecentWordBoundary < oldHyphenBufferLength) {
1022
0
                            rescanLimit = i;
1023
0
                            i = prevMostRecentWordBoundary - 1;
1024
0
                            continue;
1025
0
                        }
1026
0
                    }
1027
0
                } else {
1028
0
                    haveHyphenation = false;
1029
0
                }
1030
0
            }
1031
0
        }
1032
0
1033
0
        // There can't be a word-wrap break opportunity at the beginning of the
1034
0
        // line: if the width is too small for even one character to fit, it
1035
0
        // could be the first and last break opportunity on the line, and that
1036
0
        // would trigger an infinite loop.
1037
0
        if (aSuppressBreak != eSuppressAllBreaks &&
1038
0
            (aSuppressBreak != eSuppressInitialBreak || i > aStart)) {
1039
0
            bool atNaturalBreak = mCharacterGlyphs[i].CanBreakBefore() == 1;
1040
0
            bool atHyphenationBreak = !atNaturalBreak && haveHyphenation &&
1041
0
                hyphenBuffer[i - aStart] != HyphenType::None;
1042
0
            bool atAutoHyphenWithManualHyphenInSameWord = atHyphenationBreak &&
1043
0
                hyphenBuffer[i - aStart] == HyphenType::AutoWithManualInSameWord;
1044
0
            bool atBreak = atNaturalBreak || atHyphenationBreak;
1045
0
            bool wordWrapping =
1046
0
                aCanWordWrap && mCharacterGlyphs[i].IsClusterStart() &&
1047
0
                *aBreakPriority <= gfxBreakPriority::eWordWrapBreak;
1048
0
1049
0
            if (atBreak || wordWrapping) {
1050
0
                gfxFloat hyphenatedAdvance = advance;
1051
0
                if (atHyphenationBreak) {
1052
0
                    hyphenatedAdvance += aProvider->GetHyphenWidth();
1053
0
                }
1054
0
1055
0
                if (lastBreak < 0 ||
1056
0
                    width + hyphenatedAdvance - trimmableAdvance <= aWidth) {
1057
0
                    // We can break here.
1058
0
                    lastBreak = i;
1059
0
                    lastBreakTrimmableChars = trimmableChars;
1060
0
                    lastBreakTrimmableAdvance = trimmableAdvance;
1061
0
                    lastBreakUsedHyphenation = atHyphenationBreak;
1062
0
                    *aBreakPriority = atBreak ? gfxBreakPriority::eNormalBreak
1063
0
                                              : gfxBreakPriority::eWordWrapBreak;
1064
0
                }
1065
0
1066
0
                width += advance;
1067
0
                advance = 0;
1068
0
                if (width - trimmableAdvance > aWidth) {
1069
0
                    // No more text fits. Abort
1070
0
                    aborted = true;
1071
0
                    break;
1072
0
                }
1073
0
                // There are various kinds of break opportunities:
1074
0
                // 1. word wrap break,
1075
0
                // 2. natural break,
1076
0
                // 3. manual hyphenation break,
1077
0
                // 4. auto hyphenation break without any manual hyphenation
1078
0
                //    in the same word,
1079
0
                // 5. auto hyphenation break with another manual hyphenation
1080
0
                //    in the same word.
1081
0
                // Allow all of them except the last one to be a candidate.
1082
0
                // So, we can ensure that we don't use an automatic
1083
0
                // hyphenation opportunity within a word that contains another
1084
0
                // manual hyphenation, unless it is the only choice.
1085
0
                if (wordWrapping ||
1086
0
                    !atAutoHyphenWithManualHyphenInSameWord) {
1087
0
                    lastCandidateBreak = lastBreak;
1088
0
                    lastCandidateBreakTrimmableChars = lastBreakTrimmableChars;
1089
0
                    lastCandidateBreakTrimmableAdvance = lastBreakTrimmableAdvance;
1090
0
                    lastCandidateBreakUsedHyphenation = lastBreakUsedHyphenation;
1091
0
                    lastCandidateBreakPriority = *aBreakPriority;
1092
0
                }
1093
0
            }
1094
0
        }
1095
0
1096
0
        // If we're re-scanning part of a word (to re-process potential
1097
0
        // hyphenation types) then we don't want to accumulate widths again
1098
0
        // for the characters that were already added to `advance`.
1099
0
        if (i < rescanLimit) {
1100
0
            continue;
1101
0
        }
1102
0
1103
0
        gfxFloat charAdvance;
1104
0
        if (i >= ligatureRange.start && i < ligatureRange.end) {
1105
0
            charAdvance = GetAdvanceForGlyphs(Range(i, i + 1));
1106
0
            if (haveSpacing) {
1107
0
                PropertyProvider::Spacing *space =
1108
0
                    &spacingBuffer[i - bufferRange.start];
1109
0
                charAdvance += space->mBefore + space->mAfter;
1110
0
            }
1111
0
        } else {
1112
0
            charAdvance =
1113
0
                ComputePartialLigatureWidth(Range(i, i + 1), aProvider);
1114
0
        }
1115
0
1116
0
        advance += charAdvance;
1117
0
        if (aTrimWhitespace || aWhitespaceCanHang) {
1118
0
            if (mCharacterGlyphs[i].CharIsSpace()) {
1119
0
                ++trimmableChars;
1120
0
                trimmableAdvance += charAdvance;
1121
0
            } else {
1122
0
                trimmableAdvance = 0;
1123
0
                trimmableChars = 0;
1124
0
            }
1125
0
        }
1126
0
    }
1127
0
1128
0
    if (!aborted) {
1129
0
        width += advance;
1130
0
    }
1131
0
1132
0
    // There are three possibilities:
1133
0
    // 1) all the text fit (width <= aWidth)
1134
0
    // 2) some of the text fit up to a break opportunity (width > aWidth && lastBreak >= 0)
1135
0
    // 3) none of the text fits before a break opportunity (width > aWidth && lastBreak < 0)
1136
0
    uint32_t charsFit;
1137
0
    bool usedHyphenation = false;
1138
0
    if (width - trimmableAdvance <= aWidth) {
1139
0
        charsFit = aMaxLength;
1140
0
    } else if (lastBreak >= 0) {
1141
0
        if (lastCandidateBreak >= 0 && lastCandidateBreak != lastBreak) {
1142
0
            lastBreak = lastCandidateBreak;
1143
0
            lastBreakTrimmableChars = lastCandidateBreakTrimmableChars;
1144
0
            lastBreakTrimmableAdvance = lastCandidateBreakTrimmableAdvance;
1145
0
            lastBreakUsedHyphenation = lastCandidateBreakUsedHyphenation;
1146
0
            *aBreakPriority = lastCandidateBreakPriority;
1147
0
        }
1148
0
        charsFit = lastBreak - aStart;
1149
0
        trimmableChars = lastBreakTrimmableChars;
1150
0
        trimmableAdvance = lastBreakTrimmableAdvance;
1151
0
        usedHyphenation = lastBreakUsedHyphenation;
1152
0
    } else {
1153
0
        charsFit = aMaxLength;
1154
0
    }
1155
0
1156
0
    if (aMetrics) {
1157
0
        auto fitEnd = aStart + charsFit;
1158
0
        // Initially, measure everything, so that our bounding box includes
1159
0
        // any trimmable or hanging whitespace.
1160
0
        *aMetrics = MeasureText(Range(aStart, fitEnd),
1161
0
                                aBoundingBoxType, aRefDrawTarget,
1162
0
                                aProvider);
1163
0
        if (aTrimWhitespace || aWhitespaceCanHang) {
1164
0
            // Measure trailing whitespace that is to be trimmed/hung.
1165
0
            Metrics trimOrHangMetrics =
1166
0
                MeasureText(Range(fitEnd - trimmableChars, fitEnd),
1167
0
                            aBoundingBoxType, aRefDrawTarget,
1168
0
                            aProvider);
1169
0
            if (aTrimWhitespace) {
1170
0
                aMetrics->mAdvanceWidth -= trimOrHangMetrics.mAdvanceWidth;
1171
0
            } else if (aMetrics->mAdvanceWidth > aWidth) {
1172
0
                // Restrict width of hanging whitespace so it doesn't overflow.
1173
0
                aMetrics->mAdvanceWidth =
1174
0
                    std::max(aWidth, aMetrics->mAdvanceWidth -
1175
0
                                     trimOrHangMetrics.mAdvanceWidth);
1176
0
            }
1177
0
        }
1178
0
    }
1179
0
    if (aTrimWhitespace) {
1180
0
        *aTrimWhitespace = trimmableAdvance;
1181
0
    }
1182
0
    if (aUsedHyphenation) {
1183
0
        *aUsedHyphenation = usedHyphenation;
1184
0
    }
1185
0
    if (aLastBreak && charsFit == aMaxLength) {
1186
0
        if (lastBreak < 0) {
1187
0
            *aLastBreak = UINT32_MAX;
1188
0
        } else {
1189
0
            *aLastBreak = lastBreak - aStart;
1190
0
        }
1191
0
    }
1192
0
1193
0
    return charsFit;
1194
0
}
1195
1196
gfxFloat
1197
gfxTextRun::GetAdvanceWidth(Range aRange, PropertyProvider *aProvider,
1198
                            PropertyProvider::Spacing* aSpacing) const
1199
0
{
1200
0
    NS_ASSERTION(aRange.end <= GetLength(), "Substring out of range");
1201
0
1202
0
    Range ligatureRange = aRange;
1203
0
    ShrinkToLigatureBoundaries(&ligatureRange);
1204
0
1205
0
    gfxFloat result =
1206
0
        ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
1207
0
                                    aProvider) +
1208
0
        ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
1209
0
                                    aProvider);
1210
0
1211
0
    if (aSpacing) {
1212
0
        aSpacing->mBefore = aSpacing->mAfter = 0;
1213
0
    }
1214
0
1215
0
    // Account for all remaining spacing here. This is more efficient than
1216
0
    // processing it along with the glyphs.
1217
0
    if (aProvider && (mFlags & gfx::ShapedTextFlags::TEXT_ENABLE_SPACING)) {
1218
0
        uint32_t i;
1219
0
        AutoTArray<PropertyProvider::Spacing,200> spacingBuffer;
1220
0
        if (spacingBuffer.AppendElements(aRange.Length())) {
1221
0
            GetAdjustedSpacing(this, ligatureRange, aProvider,
1222
0
                               spacingBuffer.Elements());
1223
0
            for (i = 0; i < ligatureRange.Length(); ++i) {
1224
0
                PropertyProvider::Spacing *space = &spacingBuffer[i];
1225
0
                result += space->mBefore + space->mAfter;
1226
0
            }
1227
0
            if (aSpacing) {
1228
0
                aSpacing->mBefore = spacingBuffer[0].mBefore;
1229
0
                aSpacing->mAfter = spacingBuffer.LastElement().mAfter;
1230
0
            }
1231
0
        }
1232
0
    }
1233
0
1234
0
    return result + GetAdvanceForGlyphs(ligatureRange);
1235
0
}
1236
1237
gfxFloat
1238
gfxTextRun::GetMinAdvanceWidth(Range aRange)
1239
0
{
1240
0
    MOZ_ASSERT(aRange.end <= GetLength(), "Substring out of range");
1241
0
1242
0
    Range ligatureRange = aRange;
1243
0
    ShrinkToLigatureBoundaries(&ligatureRange);
1244
0
1245
0
    gfxFloat result = std::max(
1246
0
        ComputePartialLigatureWidth(Range(aRange.start, ligatureRange.start),
1247
0
                                    nullptr),
1248
0
        ComputePartialLigatureWidth(Range(ligatureRange.end, aRange.end),
1249
0
                                    nullptr));
1250
0
1251
0
    // XXX Do we need to take spacing into account? When each grapheme cluster
1252
0
    // takes its own line, we shouldn't be adding spacings around them.
1253
0
    gfxFloat clusterAdvance = 0;
1254
0
    for (uint32_t i = ligatureRange.start; i < ligatureRange.end; ++i) {
1255
0
        clusterAdvance += GetAdvanceForGlyph(i);
1256
0
        if (i + 1 == ligatureRange.end || IsClusterStart(i + 1)) {
1257
0
            result = std::max(result, clusterAdvance);
1258
0
            clusterAdvance = 0;
1259
0
        }
1260
0
    }
1261
0
1262
0
    return result;
1263
0
}
1264
1265
bool
1266
gfxTextRun::SetLineBreaks(Range aRange,
1267
                          bool aLineBreakBefore, bool aLineBreakAfter,
1268
                          gfxFloat *aAdvanceWidthDelta)
1269
0
{
1270
0
    // Do nothing because our shaping does not currently take linebreaks into
1271
0
    // account. There is no change in advance width.
1272
0
    if (aAdvanceWidthDelta) {
1273
0
        *aAdvanceWidthDelta = 0;
1274
0
    }
1275
0
    return false;
1276
0
}
1277
1278
uint32_t
1279
gfxTextRun::FindFirstGlyphRunContaining(uint32_t aOffset) const
1280
0
{
1281
0
    NS_ASSERTION(aOffset <= GetLength(), "Bad offset looking for glyphrun");
1282
0
    NS_ASSERTION(GetLength() == 0 ||
1283
0
                 (!mHasGlyphRunArray && mSingleGlyphRun.mFont) ||
1284
0
                 (mHasGlyphRunArray && mGlyphRunArray.Length() > 0),
1285
0
                 "non-empty text but no glyph runs present!");
1286
0
    if (!mHasGlyphRunArray) {
1287
0
        return 0;
1288
0
    }
1289
0
    if (aOffset == GetLength()) {
1290
0
        return mGlyphRunArray.Length();
1291
0
    }
1292
0
    uint32_t start = 0;
1293
0
    uint32_t end = mGlyphRunArray.Length();
1294
0
    while (end - start > 1) {
1295
0
        uint32_t mid = (start + end)/2;
1296
0
        if (mGlyphRunArray[mid].mCharacterOffset <= aOffset) {
1297
0
            start = mid;
1298
0
        } else {
1299
0
            end = mid;
1300
0
        }
1301
0
    }
1302
0
    NS_ASSERTION(mGlyphRunArray[start].mCharacterOffset <= aOffset,
1303
0
                 "Hmm, something went wrong, aOffset should have been found");
1304
0
    return start;
1305
0
}
1306
1307
nsresult
1308
gfxTextRun::AddGlyphRun(gfxFont *aFont, gfxTextRange::MatchType aMatchType,
1309
                        uint32_t aUTF16Offset, bool aForceNewRun,
1310
                        gfx::ShapedTextFlags aOrientation)
1311
0
{
1312
0
    NS_ASSERTION(aFont, "adding glyph run for null font!");
1313
0
    NS_ASSERTION(aOrientation != gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED,
1314
0
                 "mixed orientation should have been resolved");
1315
0
    if (!aFont) {
1316
0
        return NS_OK;
1317
0
    }
1318
0
    if (!mHasGlyphRunArray) {
1319
0
        // We don't currently have an array.
1320
0
        if (!mSingleGlyphRun.mFont) {
1321
0
            // This is the first glyph run: just store it directly.
1322
0
            mSingleGlyphRun.mFont = aFont;
1323
0
            mSingleGlyphRun.mMatchType = aMatchType;
1324
0
            mSingleGlyphRun.mOrientation = aOrientation;
1325
0
            mSingleGlyphRun.mCharacterOffset = aUTF16Offset;
1326
0
            return NS_OK;
1327
0
        }
1328
0
    }
1329
0
    uint32_t numGlyphRuns = mHasGlyphRunArray ? mGlyphRunArray.Length() : 1;
1330
0
    if (!aForceNewRun && numGlyphRuns > 0) {
1331
0
        GlyphRun* lastGlyphRun =
1332
0
            mHasGlyphRunArray ? &mGlyphRunArray[numGlyphRuns - 1]
1333
0
                              : &mSingleGlyphRun;
1334
0
1335
0
        NS_ASSERTION(lastGlyphRun->mCharacterOffset <= aUTF16Offset,
1336
0
                     "Glyph runs out of order (and run not forced)");
1337
0
1338
0
        // Don't append a run if the font is already the one we want
1339
0
        if (lastGlyphRun->mFont == aFont &&
1340
0
            lastGlyphRun->mMatchType == aMatchType &&
1341
0
            lastGlyphRun->mOrientation == aOrientation)
1342
0
        {
1343
0
            return NS_OK;
1344
0
        }
1345
0
1346
0
        // If the offset has not changed, avoid leaving a zero-length run
1347
0
        // by overwriting the last entry instead of appending...
1348
0
        if (lastGlyphRun->mCharacterOffset == aUTF16Offset) {
1349
0
1350
0
            // ...except that if the run before the last entry had the same
1351
0
            // font as the new one wants, merge with it instead of creating
1352
0
            // adjacent runs with the same font
1353
0
            if (numGlyphRuns > 1 &&
1354
0
                mGlyphRunArray[numGlyphRuns - 2].mFont == aFont &&
1355
0
                mGlyphRunArray[numGlyphRuns - 2].mMatchType == aMatchType &&
1356
0
                mGlyphRunArray[numGlyphRuns - 2].mOrientation == aOrientation)
1357
0
            {
1358
0
                mGlyphRunArray.TruncateLength(numGlyphRuns - 1);
1359
0
                if (mGlyphRunArray.Length() == 1) {
1360
0
                    ConvertFromGlyphRunArray();
1361
0
                }
1362
0
                return NS_OK;
1363
0
            }
1364
0
1365
0
            lastGlyphRun->mFont = aFont;
1366
0
            lastGlyphRun->mMatchType = aMatchType;
1367
0
            lastGlyphRun->mOrientation = aOrientation;
1368
0
            return NS_OK;
1369
0
        }
1370
0
    }
1371
0
1372
0
    NS_ASSERTION(aForceNewRun || numGlyphRuns > 0 || aUTF16Offset == 0,
1373
0
                 "First run doesn't cover the first character (and run not forced)?");
1374
0
1375
0
    if (!mHasGlyphRunArray) {
1376
0
        ConvertToGlyphRunArray();
1377
0
    }
1378
0
1379
0
    GlyphRun* glyphRun = mGlyphRunArray.AppendElement();
1380
0
    if (!glyphRun) {
1381
0
        if (mGlyphRunArray.Length() == 1) {
1382
0
            ConvertFromGlyphRunArray();
1383
0
        }
1384
0
        return NS_ERROR_OUT_OF_MEMORY;
1385
0
    }
1386
0
    glyphRun->mFont = aFont;
1387
0
    glyphRun->mCharacterOffset = aUTF16Offset;
1388
0
    glyphRun->mMatchType = aMatchType;
1389
0
    glyphRun->mOrientation = aOrientation;
1390
0
1391
0
    return NS_OK;
1392
0
}
1393
1394
void
1395
gfxTextRun::SortGlyphRuns()
1396
0
{
1397
0
    if (!mHasGlyphRunArray) {
1398
0
        return;
1399
0
    }
1400
0
1401
0
    // We should never have an empty or one-element array here; if there's only
1402
0
    // one glyphrun, it should be stored directly in the textrun without using
1403
0
    // an array at all.
1404
0
    MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1405
0
1406
0
    AutoTArray<GlyphRun,16> runs(std::move(mGlyphRunArray));
1407
0
    GlyphRunOffsetComparator comp;
1408
0
    runs.Sort(comp);
1409
0
1410
0
    // Now copy back, coalescing adjacent glyph runs that have the same font
1411
0
    mGlyphRunArray.Clear();
1412
0
    gfxFont* prevFont = nullptr;
1413
0
    gfx::ShapedTextFlags prevOrient = gfx::ShapedTextFlags();
1414
0
    DebugOnly<uint32_t> prevOffset = 0;
1415
0
    for (auto& run : runs) {
1416
0
        // a GlyphRun with the same font and orientation as the previous can
1417
0
        // just be skipped; the last GlyphRun will cover its character range.
1418
0
        MOZ_ASSERT(run.mFont != nullptr);
1419
0
        if (prevFont == nullptr ||
1420
0
            run.mFont != prevFont || run.mOrientation != prevOrient) {
1421
0
            // If two fonts have the same character offset, Sort() will have
1422
0
            // randomized the order.
1423
0
            MOZ_ASSERT(prevFont == nullptr ||
1424
0
                       run.mCharacterOffset != prevOffset,
1425
0
                       "Two fonts for the same run, glyph indices unreliable");
1426
0
            prevFont = run.mFont;
1427
0
            prevOrient = run.mOrientation;
1428
#ifdef DEBUG
1429
            prevOffset = run.mCharacterOffset;
1430
#endif
1431
0
            if (!mGlyphRunArray.AppendElement(std::move(run))) {
1432
0
                NS_WARNING("Failed to append glyph run!");
1433
0
            }
1434
0
        }
1435
0
    }
1436
0
1437
0
    MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1438
0
    if (mGlyphRunArray.Length() == 1) {
1439
0
        ConvertFromGlyphRunArray();
1440
0
    }
1441
0
}
1442
1443
// Note that SanitizeGlyphRuns scans all glyph runs in the textrun;
1444
// therefore we only call it once, at the end of textrun construction,
1445
// NOT incrementally as each glyph run is added (bug 680402).
1446
void
1447
gfxTextRun::SanitizeGlyphRuns()
1448
0
{
1449
0
    if (!mHasGlyphRunArray) {
1450
0
        return;
1451
0
    }
1452
0
1453
0
    MOZ_ASSERT(mGlyphRunArray.Length() > 1);
1454
0
1455
0
    // If any glyph run starts with ligature-continuation characters, we need to advance it
1456
0
    // to the first "real" character to avoid drawing partial ligature glyphs from wrong font
1457
0
    // (seen with U+FEFF in reftest 474417-1, as Core Text eliminates the glyph, which makes
1458
0
    // it appear as if a ligature has been formed)
1459
0
    int32_t i, lastRunIndex = mGlyphRunArray.Length() - 1;
1460
0
    const CompressedGlyph *charGlyphs = mCharacterGlyphs;
1461
0
    for (i = lastRunIndex; i >= 0; --i) {
1462
0
        GlyphRun& run = mGlyphRunArray[i];
1463
0
        while (charGlyphs[run.mCharacterOffset].IsLigatureContinuation() &&
1464
0
               run.mCharacterOffset < GetLength()) {
1465
0
            run.mCharacterOffset++;
1466
0
        }
1467
0
        // if the run has become empty, eliminate it
1468
0
        if ((i < lastRunIndex &&
1469
0
             run.mCharacterOffset >= mGlyphRunArray[i+1].mCharacterOffset) ||
1470
0
            (i == lastRunIndex && run.mCharacterOffset == GetLength())) {
1471
0
            mGlyphRunArray.RemoveElementAt(i);
1472
0
            --lastRunIndex;
1473
0
        }
1474
0
    }
1475
0
1476
0
    MOZ_ASSERT(mGlyphRunArray.Length() > 0);
1477
0
    if (mGlyphRunArray.Length() == 1) {
1478
0
        ConvertFromGlyphRunArray();
1479
0
    }
1480
0
}
1481
1482
uint32_t
1483
gfxTextRun::CountMissingGlyphs() const
1484
0
{
1485
0
    uint32_t i;
1486
0
    uint32_t count = 0;
1487
0
    for (i = 0; i < GetLength(); ++i) {
1488
0
        if (mCharacterGlyphs[i].IsMissing()) {
1489
0
            ++count;
1490
0
        }
1491
0
    }
1492
0
    return count;
1493
0
}
1494
1495
void
1496
gfxTextRun::CopyGlyphDataFrom(gfxShapedWord *aShapedWord, uint32_t aOffset)
1497
0
{
1498
0
    uint32_t wordLen = aShapedWord->GetLength();
1499
0
    NS_ASSERTION(aOffset + wordLen <= GetLength(),
1500
0
                 "word overruns end of textrun!");
1501
0
1502
0
    CompressedGlyph *charGlyphs = GetCharacterGlyphs();
1503
0
    const CompressedGlyph *wordGlyphs = aShapedWord->GetCharacterGlyphs();
1504
0
    if (aShapedWord->HasDetailedGlyphs()) {
1505
0
        for (uint32_t i = 0; i < wordLen; ++i, ++aOffset) {
1506
0
            const CompressedGlyph& g = wordGlyphs[i];
1507
0
            if (g.IsSimpleGlyph()) {
1508
0
                charGlyphs[aOffset] = g;
1509
0
            } else {
1510
0
                const DetailedGlyph *details =
1511
0
                    g.GetGlyphCount() > 0 ?
1512
0
                        aShapedWord->GetDetailedGlyphs(i) : nullptr;
1513
0
                SetGlyphs(aOffset, g, details);
1514
0
            }
1515
0
        }
1516
0
    } else {
1517
0
        memcpy(charGlyphs + aOffset, wordGlyphs,
1518
0
               wordLen * sizeof(CompressedGlyph));
1519
0
    }
1520
0
}
1521
1522
void
1523
gfxTextRun::CopyGlyphDataFrom(gfxTextRun *aSource, Range aRange, uint32_t aDest)
1524
0
{
1525
0
    NS_ASSERTION(aRange.end <= aSource->GetLength(),
1526
0
                 "Source substring out of range");
1527
0
    NS_ASSERTION(aDest + aRange.Length() <= GetLength(),
1528
0
                 "Destination substring out of range");
1529
0
1530
0
    if (aSource->mSkipDrawing) {
1531
0
        mSkipDrawing = true;
1532
0
    }
1533
0
1534
0
    // Copy base glyph data, and DetailedGlyph data where present
1535
0
    const CompressedGlyph *srcGlyphs = aSource->mCharacterGlyphs + aRange.start;
1536
0
    CompressedGlyph *dstGlyphs = mCharacterGlyphs + aDest;
1537
0
    for (uint32_t i = 0; i < aRange.Length(); ++i) {
1538
0
        CompressedGlyph g = srcGlyphs[i];
1539
0
        g.SetCanBreakBefore(!g.IsClusterStart() ?
1540
0
            CompressedGlyph::FLAG_BREAK_TYPE_NONE :
1541
0
            dstGlyphs[i].CanBreakBefore());
1542
0
        if (!g.IsSimpleGlyph()) {
1543
0
            uint32_t count = g.GetGlyphCount();
1544
0
            if (count > 0) {
1545
0
                DetailedGlyph *dst = AllocateDetailedGlyphs(i + aDest, count);
1546
0
                if (dst) {
1547
0
                    DetailedGlyph *src =
1548
0
                        aSource->GetDetailedGlyphs(i + aRange.start);
1549
0
                    if (src) {
1550
0
                        ::memcpy(dst, src, count * sizeof(DetailedGlyph));
1551
0
                    } else {
1552
0
                        g.SetMissing(0);
1553
0
                    }
1554
0
                } else {
1555
0
                    g.SetMissing(0);
1556
0
                }
1557
0
            }
1558
0
        }
1559
0
        dstGlyphs[i] = g;
1560
0
    }
1561
0
1562
0
    // Copy glyph runs
1563
0
    GlyphRunIterator iter(aSource, aRange);
1564
#ifdef DEBUG
1565
    const GlyphRun *prevRun = nullptr;
1566
#endif
1567
0
    while (iter.NextRun()) {
1568
0
        gfxFont *font = iter.GetGlyphRun()->mFont;
1569
0
        NS_ASSERTION(!prevRun || prevRun->mFont != iter.GetGlyphRun()->mFont ||
1570
0
                     prevRun->mMatchType != iter.GetGlyphRun()->mMatchType ||
1571
0
                     prevRun->mOrientation != iter.GetGlyphRun()->mOrientation,
1572
0
                     "Glyphruns not coalesced?");
1573
#ifdef DEBUG
1574
        prevRun = iter.GetGlyphRun();
1575
        uint32_t end = iter.GetStringEnd();
1576
#endif
1577
        uint32_t start = iter.GetStringStart();
1578
0
1579
0
        // These used to be NS_ASSERTION()s, but WARNING is more appropriate.
1580
0
        // Although it's unusual (and not desirable), it's possible for us to assign
1581
0
        // different fonts to a base character and a following diacritic.
1582
0
        // Example on OSX 10.5/10.6 with default fonts installed:
1583
0
        //     data:text/html,<p style="font-family:helvetica, arial, sans-serif;">
1584
0
        //                    &%23x043E;&%23x0486;&%23x20;&%23x043E;&%23x0486;
1585
0
        // This means the rendering of the cluster will probably not be very good,
1586
0
        // but it's the best we can do for now if the specified font only covered the
1587
0
        // initial base character and not its applied marks.
1588
0
        NS_WARNING_ASSERTION(
1589
0
          aSource->IsClusterStart(start),
1590
0
          "Started font run in the middle of a cluster");
1591
0
        NS_WARNING_ASSERTION(
1592
0
          end == aSource->GetLength() || aSource->IsClusterStart(end),
1593
0
          "Ended font run in the middle of a cluster");
1594
0
1595
0
        nsresult rv = AddGlyphRun(font, iter.GetGlyphRun()->mMatchType,
1596
0
                                  start - aRange.start + aDest, false,
1597
0
                                  iter.GetGlyphRun()->mOrientation);
1598
0
        if (NS_FAILED(rv))
1599
0
            return;
1600
0
    }
1601
0
}
1602
1603
void
1604
gfxTextRun::ClearGlyphsAndCharacters()
1605
0
{
1606
0
    ResetGlyphRuns();
1607
0
    memset(reinterpret_cast<char*>(mCharacterGlyphs), 0,
1608
0
           mLength * sizeof(CompressedGlyph));
1609
0
    mDetailedGlyphs = nullptr;
1610
0
}
1611
1612
void
1613
gfxTextRun::SetSpaceGlyph(gfxFont* aFont, DrawTarget* aDrawTarget,
1614
                          uint32_t aCharIndex,
1615
                          gfx::ShapedTextFlags aOrientation)
1616
0
{
1617
0
    if (SetSpaceGlyphIfSimple(aFont, aCharIndex, ' ', aOrientation)) {
1618
0
        return;
1619
0
    }
1620
0
1621
0
    aFont->InitWordCache();
1622
0
    static const uint8_t space = ' ';
1623
0
    gfx::ShapedTextFlags
1624
0
        flags = gfx::ShapedTextFlags::TEXT_IS_8BIT |
1625
0
                aOrientation;
1626
0
    bool vertical =
1627
0
        !!(GetFlags() & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT);
1628
0
    gfxFontShaper::RoundingFlags roundingFlags =
1629
0
        aFont->GetRoundOffsetsToPixels(aDrawTarget);
1630
0
    gfxShapedWord* sw = aFont->GetShapedWord(aDrawTarget,
1631
0
                                             &space, 1,
1632
0
                                             gfxShapedWord::HashMix(0, ' '),
1633
0
                                             Script::LATIN,
1634
0
                                             vertical,
1635
0
                                             mAppUnitsPerDevUnit,
1636
0
                                             flags,
1637
0
                                             roundingFlags,
1638
0
                                             nullptr);
1639
0
    if (sw) {
1640
0
        AddGlyphRun(aFont, gfxTextRange::MatchType::kFontGroup, aCharIndex,
1641
0
                    false, aOrientation);
1642
0
        CopyGlyphDataFrom(sw, aCharIndex);
1643
0
    }
1644
0
}
1645
1646
bool
1647
gfxTextRun::SetSpaceGlyphIfSimple(gfxFont* aFont, uint32_t aCharIndex,
1648
                                  char16_t aSpaceChar,
1649
                                  gfx::ShapedTextFlags aOrientation)
1650
0
{
1651
0
    uint32_t spaceGlyph = aFont->GetSpaceGlyph();
1652
0
    if (!spaceGlyph || !CompressedGlyph::IsSimpleGlyphID(spaceGlyph)) {
1653
0
        return false;
1654
0
    }
1655
0
1656
0
    gfxFont::Orientation fontOrientation =
1657
0
        (aOrientation & gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT) ?
1658
0
            gfxFont::eVertical : gfxFont::eHorizontal;
1659
0
    uint32_t spaceWidthAppUnits =
1660
0
        NS_lroundf(aFont->GetMetrics(fontOrientation).spaceWidth *
1661
0
                   mAppUnitsPerDevUnit);
1662
0
    if (!CompressedGlyph::IsSimpleAdvance(spaceWidthAppUnits)) {
1663
0
        return false;
1664
0
    }
1665
0
1666
0
    AddGlyphRun(aFont, gfxTextRange::MatchType::kFontGroup, aCharIndex, false,
1667
0
                aOrientation);
1668
0
    CompressedGlyph g =
1669
0
        CompressedGlyph::MakeSimpleGlyph(spaceWidthAppUnits, spaceGlyph);
1670
0
    if (aSpaceChar == ' ') {
1671
0
        g.SetIsSpace();
1672
0
    }
1673
0
    GetCharacterGlyphs()[aCharIndex] = g;
1674
0
    return true;
1675
0
}
1676
1677
void
1678
gfxTextRun::FetchGlyphExtents(DrawTarget* aRefDrawTarget)
1679
0
{
1680
0
    bool needsGlyphExtents = NeedsGlyphExtents(this);
1681
0
    if (!needsGlyphExtents && !mDetailedGlyphs)
1682
0
        return;
1683
0
1684
0
    uint32_t runCount;
1685
0
    const GlyphRun* glyphRuns = GetGlyphRuns(&runCount);
1686
0
    CompressedGlyph *charGlyphs = mCharacterGlyphs;
1687
0
    for (uint32_t i = 0; i < runCount; ++i) {
1688
0
        const GlyphRun& run = glyphRuns[i];
1689
0
        gfxFont *font = run.mFont;
1690
0
        if (MOZ_UNLIKELY(font->GetStyle()->size == 0) ||
1691
0
            MOZ_UNLIKELY(font->GetStyle()->sizeAdjust == 0.0f)) {
1692
0
            continue;
1693
0
        }
1694
0
1695
0
        uint32_t start = run.mCharacterOffset;
1696
0
        uint32_t end = i + 1 < runCount ?
1697
0
            glyphRuns[i + 1].mCharacterOffset : GetLength();
1698
0
        uint32_t j;
1699
0
        gfxGlyphExtents *extents = font->GetOrCreateGlyphExtents(mAppUnitsPerDevUnit);
1700
0
1701
0
        for (j = start; j < end; ++j) {
1702
0
            const gfxTextRun::CompressedGlyph *glyphData = &charGlyphs[j];
1703
0
            if (glyphData->IsSimpleGlyph()) {
1704
0
                // If we're in speed mode, don't set up glyph extents here; we'll
1705
0
                // just return "optimistic" glyph bounds later
1706
0
                if (needsGlyphExtents) {
1707
0
                    uint32_t glyphIndex = glyphData->GetSimpleGlyph();
1708
0
                    if (!extents->IsGlyphKnown(glyphIndex)) {
1709
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1710
                        ++gGlyphExtentsSetupEagerSimple;
1711
#endif
1712
                        font->SetupGlyphExtents(aRefDrawTarget,
1713
0
                                                glyphIndex, false, extents);
1714
0
                    }
1715
0
                }
1716
0
            } else if (!glyphData->IsMissing()) {
1717
0
                uint32_t glyphCount = glyphData->GetGlyphCount();
1718
0
                if (glyphCount == 0) {
1719
0
                    continue;
1720
0
                }
1721
0
                const gfxTextRun::DetailedGlyph *details = GetDetailedGlyphs(j);
1722
0
                if (!details) {
1723
0
                    continue;
1724
0
                }
1725
0
                for (uint32_t k = 0; k < glyphCount; ++k, ++details) {
1726
0
                    uint32_t glyphIndex = details->mGlyphID;
1727
0
                    if (!extents->IsGlyphKnownWithTightExtents(glyphIndex)) {
1728
#ifdef DEBUG_TEXT_RUN_STORAGE_METRICS
1729
                        ++gGlyphExtentsSetupEagerTight;
1730
#endif
1731
                        font->SetupGlyphExtents(aRefDrawTarget,
1732
0
                                                glyphIndex, true, extents);
1733
0
                    }
1734
0
                }
1735
0
            }
1736
0
        }
1737
0
    }
1738
0
}
1739
1740
1741
size_t
1742
gfxTextRun::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf)
1743
0
{
1744
0
    // The second arg is how much gfxTextRun::AllocateStorage would have
1745
0
    // allocated.
1746
0
    size_t total = mHasGlyphRunArray
1747
0
        ? mGlyphRunArray.ShallowSizeOfExcludingThis(aMallocSizeOf)
1748
0
        : 0;
1749
0
1750
0
    if (mDetailedGlyphs) {
1751
0
        total += mDetailedGlyphs->SizeOfIncludingThis(aMallocSizeOf);
1752
0
    }
1753
0
1754
0
    return total;
1755
0
}
1756
1757
size_t
1758
gfxTextRun::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf)
1759
0
{
1760
0
    return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
1761
0
}
1762
1763
1764
#ifdef DEBUG
1765
void
1766
gfxTextRun::Dump(FILE* aOutput) {
1767
    if (!aOutput) {
1768
        aOutput = stdout;
1769
    }
1770
1771
    fputc('[', aOutput);
1772
    uint32_t numGlyphRuns;
1773
    const GlyphRun* glyphRuns = GetGlyphRuns(&numGlyphRuns);
1774
    for (uint32_t i = 0; i < numGlyphRuns; ++i) {
1775
        if (i > 0) {
1776
            fputc(',', aOutput);
1777
        }
1778
        gfxFont* font = glyphRuns[i].mFont;
1779
        const gfxFontStyle* style = font->GetStyle();
1780
        nsAutoString styleString;
1781
        nsStyleUtil::AppendFontSlantStyle(style->style, styleString);
1782
        nsAutoCString lang;
1783
        style->language->ToUTF8String(lang);
1784
        fprintf(aOutput, "%d: %s %f/%g/%s/%s", glyphRuns[i].mCharacterOffset,
1785
                font->GetName().get(), style->size,
1786
                style->weight.ToFloat(),
1787
                NS_ConvertUTF16toUTF8(styleString).get(),
1788
                lang.get());
1789
    }
1790
    fputc(']', aOutput);
1791
}
1792
#endif
1793
1794
gfxFontGroup::gfxFontGroup(const FontFamilyList& aFontFamilyList,
1795
                           const gfxFontStyle *aStyle,
1796
                           gfxTextPerfMetrics* aTextPerf,
1797
                           gfxUserFontSet *aUserFontSet,
1798
                           gfxFloat aDevToCssSize)
1799
    : mFamilyList(aFontFamilyList)
1800
    , mStyle(*aStyle)
1801
    , mUnderlineOffset(UNDERLINE_OFFSET_NOT_SET)
1802
    , mHyphenWidth(-1)
1803
    , mDevToCssSize(aDevToCssSize)
1804
    , mUserFontSet(aUserFontSet)
1805
    , mTextPerf(aTextPerf)
1806
    , mLastPrefLang(eFontPrefLang_Western)
1807
    , mPageLang(gfxPlatformFontList::GetFontPrefLangFor(aStyle->language))
1808
    , mLastPrefFirstFont(false)
1809
    , mSkipDrawing(false)
1810
0
{
1811
0
    // We don't use SetUserFontSet() here, as we want to unconditionally call
1812
0
    // BuildFontList() rather than only do UpdateUserFonts() if it changed.
1813
0
    mCurrGeneration = GetGeneration();
1814
0
    BuildFontList();
1815
0
}
1816
1817
gfxFontGroup::~gfxFontGroup()
1818
0
{
1819
0
    // Should not be dropped by stylo
1820
0
    MOZ_ASSERT(NS_IsMainThread());
1821
0
}
1822
1823
void
1824
gfxFontGroup::BuildFontList()
1825
0
{
1826
0
    // initialize fonts in the font family list
1827
0
    AutoTArray<FamilyAndGeneric,10> fonts;
1828
0
    gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
1829
0
1830
0
    // lookup fonts in the fontlist
1831
0
    for (const FontFamilyName& name : mFamilyList.GetFontlist()->mNames) {
1832
0
        if (name.IsNamed()) {
1833
0
            AddPlatformFont(nsAtomCString(name.mName), fonts);
1834
0
        } else {
1835
0
            pfl->AddGenericFonts(name.mType, mStyle.language, fonts);
1836
0
            if (mTextPerf) {
1837
0
                mTextPerf->current.genericLookups++;
1838
0
            }
1839
0
        }
1840
0
    }
1841
0
1842
0
    // if necessary, append default generic onto the end
1843
0
    if (mFamilyList.GetDefaultFontType() != eFamily_none &&
1844
0
        !mFamilyList.HasDefaultGeneric()) {
1845
0
        pfl->AddGenericFonts(mFamilyList.GetDefaultFontType(),
1846
0
                             mStyle.language, fonts);
1847
0
        if (mTextPerf) {
1848
0
            mTextPerf->current.genericLookups++;
1849
0
        }
1850
0
    }
1851
0
1852
0
    // build the fontlist from the specified families
1853
0
    for (const auto& f : fonts) {
1854
0
        AddFamilyToFontList(f.mFamily, f.mGeneric);
1855
0
    }
1856
0
}
1857
1858
void
1859
gfxFontGroup::AddPlatformFont(const nsACString& aName,
1860
                              nsTArray<FamilyAndGeneric>& aFamilyList)
1861
0
{
1862
0
    // First, look up in the user font set...
1863
0
    // If the fontSet matches the family, we must not look for a platform
1864
0
    // font of the same name, even if we fail to actually get a fontEntry
1865
0
    // here; we'll fall back to the next name in the CSS font-family list.
1866
0
    if (mUserFontSet) {
1867
0
        // Add userfonts to the fontlist whether already loaded
1868
0
        // or not. Loading is initiated during font matching.
1869
0
        gfxFontFamily* family = mUserFontSet->LookupFamily(aName);
1870
0
        if (family) {
1871
0
            aFamilyList.AppendElement(family);
1872
0
            return;
1873
0
        }
1874
0
    }
1875
0
1876
0
    // Not known in the user font set ==> check system fonts
1877
0
    gfxPlatformFontList::PlatformFontList()
1878
0
        ->FindAndAddFamilies(aName, &aFamilyList,
1879
0
                             gfxPlatformFontList::FindFamiliesFlags(0),
1880
0
                             &mStyle, mDevToCssSize);
1881
0
}
1882
1883
void
1884
gfxFontGroup::AddFamilyToFontList(gfxFontFamily* aFamily,
1885
                                  FontFamilyType aGeneric)
1886
0
{
1887
0
    NS_ASSERTION(aFamily, "trying to add a null font family to fontlist");
1888
0
    AutoTArray<gfxFontEntry*,4> fontEntryList;
1889
0
    aFamily->FindAllFontsForStyle(mStyle, fontEntryList);
1890
0
    // add these to the fontlist
1891
0
    for (gfxFontEntry* fe : fontEntryList) {
1892
0
        if (!HasFont(fe)) {
1893
0
            FamilyFace ff(aFamily, fe, aGeneric);
1894
0
            if (fe->mIsUserFontContainer) {
1895
0
                ff.CheckState(mSkipDrawing);
1896
0
            }
1897
0
            mFonts.AppendElement(ff);
1898
0
        }
1899
0
    }
1900
0
    // for a family marked as "check fallback faces", only mark the last
1901
0
    // entry so that fallbacks for a family are only checked once
1902
0
    if (aFamily->CheckForFallbackFaces() &&
1903
0
        !fontEntryList.IsEmpty() && !mFonts.IsEmpty()) {
1904
0
        mFonts.LastElement().SetCheckForFallbackFaces();
1905
0
    }
1906
0
}
1907
1908
bool
1909
gfxFontGroup::HasFont(const gfxFontEntry *aFontEntry)
1910
0
{
1911
0
    uint32_t count = mFonts.Length();
1912
0
    for (uint32_t i = 0; i < count; ++i) {
1913
0
        if (mFonts[i].FontEntry() == aFontEntry) {
1914
0
            return true;
1915
0
        }
1916
0
    }
1917
0
    return false;
1918
0
}
1919
1920
gfxFont*
1921
gfxFontGroup::GetFontAt(int32_t i, uint32_t aCh)
1922
0
{
1923
0
    if (uint32_t(i) >= mFonts.Length()) {
1924
0
        return nullptr;
1925
0
    }
1926
0
1927
0
    FamilyFace& ff = mFonts[i];
1928
0
    if (ff.IsInvalid() || ff.IsLoading()) {
1929
0
        return nullptr;
1930
0
    }
1931
0
1932
0
    gfxFont* font = ff.Font();
1933
0
    if (!font) {
1934
0
        gfxFontEntry* fe = mFonts[i].FontEntry();
1935
0
        gfxCharacterMap* unicodeRangeMap = nullptr;
1936
0
        if (fe->mIsUserFontContainer) {
1937
0
            gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
1938
0
            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
1939
0
                ufe->CharacterInUnicodeRange(aCh) && !mSkipDrawing &&
1940
0
                !FontLoadingForFamily(ff.Family(), aCh)) {
1941
0
                ufe->Load();
1942
0
                ff.CheckState(mSkipDrawing);
1943
0
            }
1944
0
            fe = ufe->GetPlatformFontEntry();
1945
0
            if (!fe) {
1946
0
                return nullptr;
1947
0
            }
1948
0
            unicodeRangeMap = ufe->GetUnicodeRangeMap();
1949
0
        }
1950
0
        font = fe->FindOrMakeFont(&mStyle, unicodeRangeMap);
1951
0
        if (!font || !font->Valid()) {
1952
0
            ff.SetInvalid();
1953
0
            // We can't just |delete font| here, in case there are other
1954
0
            // references to the object FindOrMakeFont returned.
1955
0
            RefPtr<gfxFont> ref(font);
1956
0
            return nullptr;
1957
0
        }
1958
0
        mFonts[i].SetFont(font);
1959
0
    }
1960
0
    return font;
1961
0
}
1962
1963
void
1964
gfxFontGroup::FamilyFace::CheckState(bool& aSkipDrawing)
1965
0
{
1966
0
    gfxFontEntry* fe = FontEntry();
1967
0
    if (fe->mIsUserFontContainer) {
1968
0
        gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
1969
0
        gfxUserFontEntry::UserFontLoadState state = ufe->LoadState();
1970
0
        switch (state) {
1971
0
            case gfxUserFontEntry::STATUS_LOAD_PENDING:
1972
0
            case gfxUserFontEntry::STATUS_LOADING:
1973
0
                SetLoading(true);
1974
0
                break;
1975
0
            case gfxUserFontEntry::STATUS_FAILED:
1976
0
                SetInvalid();
1977
0
                // fall-thru to the default case
1978
0
                MOZ_FALLTHROUGH;
1979
0
            default:
1980
0
                SetLoading(false);
1981
0
        }
1982
0
        if (ufe->WaitForUserFont()) {
1983
0
            aSkipDrawing = true;
1984
0
        }
1985
0
    }
1986
0
}
1987
1988
bool
1989
gfxFontGroup::FamilyFace::EqualsUserFont(const gfxUserFontEntry* aUserFont) const
1990
0
{
1991
0
    gfxFontEntry* fe = FontEntry();
1992
0
    // if there's a font, the entry is the underlying platform font
1993
0
    if (mFontCreated) {
1994
0
        gfxFontEntry* pfe = aUserFont->GetPlatformFontEntry();
1995
0
        if (pfe == fe) {
1996
0
            return true;
1997
0
        }
1998
0
    } else if (fe == aUserFont) {
1999
0
        return true;
2000
0
    }
2001
0
    return false;
2002
0
}
2003
2004
bool
2005
gfxFontGroup::FontLoadingForFamily(gfxFontFamily* aFamily, uint32_t aCh) const
2006
0
{
2007
0
    uint32_t count = mFonts.Length();
2008
0
    for (uint32_t i = 0; i < count; ++i) {
2009
0
        const FamilyFace& ff = mFonts[i];
2010
0
        if (ff.IsLoading() && ff.Family() == aFamily) {
2011
0
            const gfxUserFontEntry* ufe =
2012
0
                static_cast<gfxUserFontEntry*>(ff.FontEntry());
2013
0
            if (ufe->CharacterInUnicodeRange(aCh)) {
2014
0
                return true;
2015
0
            }
2016
0
        }
2017
0
    }
2018
0
    return false;
2019
0
}
2020
2021
gfxFont*
2022
gfxFontGroup::GetDefaultFont()
2023
0
{
2024
0
    if (mDefaultFont) {
2025
0
        return mDefaultFont.get();
2026
0
    }
2027
0
2028
0
    gfxPlatformFontList *pfl = gfxPlatformFontList::PlatformFontList();
2029
0
    gfxFontFamily *defaultFamily = pfl->GetDefaultFont(&mStyle);
2030
0
    NS_ASSERTION(defaultFamily,
2031
0
                 "invalid default font returned by GetDefaultFont");
2032
0
2033
0
    if (defaultFamily) {
2034
0
        gfxFontEntry *fe =
2035
0
            defaultFamily->FindFontForStyle(mStyle, true);
2036
0
        if (fe) {
2037
0
            mDefaultFont = fe->FindOrMakeFont(&mStyle);
2038
0
        }
2039
0
    }
2040
0
2041
0
    uint32_t numInits, loaderState;
2042
0
    pfl->GetFontlistInitInfo(numInits, loaderState);
2043
0
    NS_ASSERTION(numInits != 0,
2044
0
                 "must initialize system fontlist before getting default font!");
2045
0
2046
0
    uint32_t numFonts = 0;
2047
0
    if (!mDefaultFont) {
2048
0
        // Try for a "font of last resort...."
2049
0
        // Because an empty font list would be Really Bad for later code
2050
0
        // that assumes it will be able to get valid metrics for layout,
2051
0
        // just look for the first usable font and put in the list.
2052
0
        // (see bug 554544)
2053
0
        AutoTArray<RefPtr<gfxFontFamily>,200> familyList;
2054
0
        pfl->GetFontFamilyList(familyList);
2055
0
        numFonts = familyList.Length();
2056
0
        for (uint32_t i = 0; i < numFonts; ++i) {
2057
0
            gfxFontEntry *fe =
2058
0
                familyList[i]->FindFontForStyle(mStyle, true);
2059
0
            if (fe) {
2060
0
                mDefaultFont = fe->FindOrMakeFont(&mStyle);
2061
0
                if (mDefaultFont) {
2062
0
                    break;
2063
0
                }
2064
0
            }
2065
0
        }
2066
0
    }
2067
0
2068
0
    if (!mDefaultFont) {
2069
0
        // an empty font list at this point is fatal; we're not going to
2070
0
        // be able to do even the most basic layout operations
2071
0
2072
0
        // annotate crash report with fontlist info
2073
0
        nsAutoCString fontInitInfo;
2074
0
        fontInitInfo.AppendPrintf("no fonts - init: %d fonts: %d loader: %d",
2075
0
                                  numInits, numFonts, loaderState);
2076
#ifdef XP_WIN
2077
        bool dwriteEnabled = gfxWindowsPlatform::GetPlatform()->DWriteEnabled();
2078
        double upTime = (double) GetTickCount();
2079
        fontInitInfo.AppendPrintf(" backend: %s system-uptime: %9.3f sec",
2080
                                  dwriteEnabled ? "directwrite" : "gdi", upTime/1000);
2081
#endif
2082
0
        gfxCriticalError() << fontInitInfo.get();
2083
0
2084
0
        char msg[256]; // CHECK buffer length if revising message below
2085
0
        nsAutoCString familiesString;
2086
0
        mFamilyList.ToString(familiesString);
2087
0
        SprintfLiteral(msg, "unable to find a usable font (%.220s)",
2088
0
                       familiesString.get());
2089
0
        MOZ_CRASH_UNSAFE_OOL(msg);
2090
0
    }
2091
0
2092
0
    return mDefaultFont.get();
2093
0
}
2094
2095
gfxFont*
2096
gfxFontGroup::GetFirstValidFont(uint32_t aCh, FontFamilyType* aGeneric)
2097
0
{
2098
0
    uint32_t count = mFonts.Length();
2099
0
    for (uint32_t i = 0; i < count; ++i) {
2100
0
        FamilyFace& ff = mFonts[i];
2101
0
        if (ff.IsInvalid()) {
2102
0
            continue;
2103
0
        }
2104
0
2105
0
        // already have a font?
2106
0
        gfxFont* font = ff.Font();
2107
0
        if (font) {
2108
0
            if (aGeneric) {
2109
0
                *aGeneric = ff.Generic();
2110
0
            }
2111
0
            return font;
2112
0
        }
2113
0
2114
0
        // Need to build a font, loading userfont if not loaded. In
2115
0
        // cases where unicode range might apply, use the character
2116
0
        // provided.
2117
0
        if (ff.IsUserFontContainer()) {
2118
0
            gfxUserFontEntry* ufe =
2119
0
                static_cast<gfxUserFontEntry*>(mFonts[i].FontEntry());
2120
0
            bool inRange = ufe->CharacterInUnicodeRange(aCh);
2121
0
            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
2122
0
                inRange && !mSkipDrawing &&
2123
0
                !FontLoadingForFamily(ff.Family(), aCh)) {
2124
0
                ufe->Load();
2125
0
                ff.CheckState(mSkipDrawing);
2126
0
            }
2127
0
            if (ufe->LoadState() != gfxUserFontEntry::STATUS_LOADED ||
2128
0
                !inRange) {
2129
0
                continue;
2130
0
            }
2131
0
        }
2132
0
2133
0
        font = GetFontAt(i, aCh);
2134
0
        if (font) {
2135
0
            if (aGeneric) {
2136
0
                *aGeneric = mFonts[i].Generic();
2137
0
            }
2138
0
            return font;
2139
0
        }
2140
0
    }
2141
0
    if (aGeneric) {
2142
0
        *aGeneric = eFamily_none;
2143
0
    }
2144
0
    return GetDefaultFont();
2145
0
}
2146
2147
gfxFont *
2148
gfxFontGroup::GetFirstMathFont()
2149
0
{
2150
0
    uint32_t count = mFonts.Length();
2151
0
    for (uint32_t i = 0; i < count; ++i) {
2152
0
        gfxFont* font = GetFontAt(i);
2153
0
        if (font && font->TryGetMathTable()) {
2154
0
            return font;
2155
0
        }
2156
0
    }
2157
0
    return nullptr;
2158
0
}
2159
2160
gfxFontGroup *
2161
gfxFontGroup::Copy(const gfxFontStyle *aStyle)
2162
0
{
2163
0
    gfxFontGroup *fg =
2164
0
        new gfxFontGroup(mFamilyList, aStyle, mTextPerf,
2165
0
                         mUserFontSet, mDevToCssSize);
2166
0
    return fg;
2167
0
}
2168
2169
bool
2170
gfxFontGroup::IsInvalidChar(uint8_t ch)
2171
0
{
2172
0
    return ((ch & 0x7f) < 0x20 || ch == 0x7f);
2173
0
}
2174
2175
bool
2176
gfxFontGroup::IsInvalidChar(char16_t ch)
2177
0
{
2178
0
    // All printable 7-bit ASCII values are OK
2179
0
    if (ch >= ' ' && ch < 0x7f) {
2180
0
        return false;
2181
0
    }
2182
0
    // No point in sending non-printing control chars through font shaping
2183
0
    if (ch <= 0x9f) {
2184
0
        return true;
2185
0
    }
2186
0
    // Word-separating format/bidi control characters are not shaped as part
2187
0
    // of words.
2188
0
    return (((ch & 0xFF00) == 0x2000 /* Unicode control character */ &&
2189
0
             (ch == 0x200B/*ZWSP*/ || ch == 0x2028/*LSEP*/ ||
2190
0
              ch == 0x2029/*PSEP*/ || ch == 0x2060/*WJ*/)) ||
2191
0
            ch == 0xfeff/*ZWNBSP*/ ||
2192
0
            IsBidiControl(ch));
2193
0
}
2194
2195
already_AddRefed<gfxTextRun>
2196
gfxFontGroup::MakeEmptyTextRun(const Parameters *aParams,
2197
                               gfx::ShapedTextFlags aFlags,
2198
                               nsTextFrameUtils::Flags aFlags2)
2199
0
{
2200
0
    aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2201
0
    return gfxTextRun::Create(aParams, 0, this, aFlags, aFlags2);
2202
0
}
2203
2204
already_AddRefed<gfxTextRun>
2205
gfxFontGroup::MakeSpaceTextRun(const Parameters *aParams,
2206
                               gfx::ShapedTextFlags aFlags,
2207
                               nsTextFrameUtils::Flags aFlags2)
2208
0
{
2209
0
    aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2210
0
2211
0
    RefPtr<gfxTextRun> textRun =
2212
0
        gfxTextRun::Create(aParams, 1, this, aFlags, aFlags2);
2213
0
    if (!textRun) {
2214
0
        return nullptr;
2215
0
    }
2216
0
2217
0
    gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2218
0
    if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2219
0
        orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2220
0
    }
2221
0
2222
0
    gfxFont *font = GetFirstValidFont();
2223
0
    if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2224
0
        MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2225
0
        // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2226
0
        // them, and always create at least size 1 fonts, i.e. they still
2227
0
        // render something for size 0 fonts.
2228
0
        textRun->AddGlyphRun(font, gfxTextRange::MatchType::kFontGroup, 0,
2229
0
                             false, orientation);
2230
0
    }
2231
0
    else {
2232
0
        if (font->GetSpaceGlyph()) {
2233
0
            // Normally, the font has a cached space glyph, so we can avoid
2234
0
            // the cost of calling FindFontForChar.
2235
0
            textRun->SetSpaceGlyph(font, aParams->mDrawTarget, 0, orientation);
2236
0
        } else {
2237
0
            // In case the primary font doesn't have <space> (bug 970891),
2238
0
            // find one that does.
2239
0
            gfxTextRange::MatchType matchType;
2240
0
            gfxFont* spaceFont =
2241
0
                FindFontForChar(' ', 0, 0, Script::LATIN, nullptr,
2242
0
                                &matchType);
2243
0
            if (spaceFont) {
2244
0
                textRun->SetSpaceGlyph(spaceFont, aParams->mDrawTarget, 0,
2245
0
                                       orientation);
2246
0
            }
2247
0
        }
2248
0
    }
2249
0
2250
0
    // Note that the gfxGlyphExtents glyph bounds storage for the font will
2251
0
    // always contain an entry for the font's space glyph, so we don't have
2252
0
    // to call FetchGlyphExtents here.
2253
0
    return textRun.forget();
2254
0
}
2255
2256
already_AddRefed<gfxTextRun>
2257
gfxFontGroup::MakeBlankTextRun(uint32_t aLength,
2258
                               const Parameters *aParams,
2259
                               gfx::ShapedTextFlags aFlags,
2260
                               nsTextFrameUtils::Flags aFlags2)
2261
0
{
2262
0
    RefPtr<gfxTextRun> textRun =
2263
0
        gfxTextRun::Create(aParams, aLength, this, aFlags, aFlags2);
2264
0
    if (!textRun) {
2265
0
        return nullptr;
2266
0
    }
2267
0
2268
0
    gfx::ShapedTextFlags orientation = aFlags & ShapedTextFlags::TEXT_ORIENT_MASK;
2269
0
    if (orientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
2270
0
        orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
2271
0
    }
2272
0
    textRun->AddGlyphRun(GetFirstValidFont(),
2273
0
                         gfxTextRange::MatchType::kFontGroup, 0, false,
2274
0
                         orientation);
2275
0
    return textRun.forget();
2276
0
}
2277
2278
already_AddRefed<gfxTextRun>
2279
gfxFontGroup::MakeHyphenTextRun(DrawTarget* aDrawTarget,
2280
                                uint32_t aAppUnitsPerDevUnit)
2281
0
{
2282
0
    // only use U+2010 if it is supported by the first font in the group;
2283
0
    // it's better to use ASCII '-' from the primary font than to fall back to
2284
0
    // U+2010 from some other, possibly poorly-matching face
2285
0
    static const char16_t hyphen = 0x2010;
2286
0
    gfxFont *font = GetFirstValidFont(uint32_t(hyphen));
2287
0
    if (font->HasCharacter(hyphen)) {
2288
0
        return MakeTextRun(&hyphen, 1, aDrawTarget, aAppUnitsPerDevUnit,
2289
0
                           ShapedTextFlags(),
2290
0
                           nsTextFrameUtils::Flags(), nullptr);
2291
0
    }
2292
0
2293
0
    static const uint8_t dash = '-';
2294
0
    return MakeTextRun(&dash, 1, aDrawTarget, aAppUnitsPerDevUnit,
2295
0
                       ShapedTextFlags(),
2296
0
                       nsTextFrameUtils::Flags(), nullptr);
2297
0
}
2298
2299
gfxFloat
2300
gfxFontGroup::GetHyphenWidth(const gfxTextRun::PropertyProvider* aProvider)
2301
0
{
2302
0
    if (mHyphenWidth < 0) {
2303
0
        RefPtr<DrawTarget> dt(aProvider->GetDrawTarget());
2304
0
        if (dt) {
2305
0
            RefPtr<gfxTextRun>
2306
0
                hyphRun(MakeHyphenTextRun(dt,
2307
0
                                          aProvider->GetAppUnitsPerDevUnit()));
2308
0
            mHyphenWidth = hyphRun.get() ? hyphRun->GetAdvanceWidth() : 0;
2309
0
        }
2310
0
    }
2311
0
    return mHyphenWidth;
2312
0
}
2313
2314
already_AddRefed<gfxTextRun>
2315
gfxFontGroup::MakeTextRun(const uint8_t *aString, uint32_t aLength,
2316
                          const Parameters *aParams,
2317
                          gfx::ShapedTextFlags aFlags,
2318
                          nsTextFrameUtils::Flags aFlags2,
2319
                          gfxMissingFontRecorder *aMFR)
2320
0
{
2321
0
    if (aLength == 0) {
2322
0
        return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2323
0
    }
2324
0
    if (aLength == 1 && aString[0] == ' ') {
2325
0
        return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2326
0
    }
2327
0
2328
0
    aFlags |= ShapedTextFlags::TEXT_IS_8BIT;
2329
0
2330
0
    if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2331
0
        MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2332
0
        // Short-circuit for size-0 fonts, as Windows and ATSUI can't handle
2333
0
        // them, and always create at least size 1 fonts, i.e. they still
2334
0
        // render something for size 0 fonts.
2335
0
        return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
2336
0
    }
2337
0
2338
0
    RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
2339
0
                                                    aFlags, aFlags2);
2340
0
    if (!textRun) {
2341
0
        return nullptr;
2342
0
    }
2343
0
2344
0
    InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2345
0
2346
0
    textRun->FetchGlyphExtents(aParams->mDrawTarget);
2347
0
2348
0
    return textRun.forget();
2349
0
}
2350
2351
already_AddRefed<gfxTextRun>
2352
gfxFontGroup::MakeTextRun(const char16_t *aString, uint32_t aLength,
2353
                          const Parameters *aParams,
2354
                          gfx::ShapedTextFlags aFlags, 
2355
                          nsTextFrameUtils::Flags aFlags2,
2356
                          gfxMissingFontRecorder *aMFR)
2357
0
{
2358
0
    if (aLength == 0) {
2359
0
        return MakeEmptyTextRun(aParams, aFlags, aFlags2);
2360
0
    }
2361
0
    if (aLength == 1 && aString[0] == ' ') {
2362
0
        return MakeSpaceTextRun(aParams, aFlags, aFlags2);
2363
0
    }
2364
0
    if (MOZ_UNLIKELY(GetStyle()->size == 0) ||
2365
0
        MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0f)) {
2366
0
        return MakeBlankTextRun(aLength, aParams, aFlags, aFlags2);
2367
0
    }
2368
0
2369
0
    RefPtr<gfxTextRun> textRun = gfxTextRun::Create(aParams, aLength, this,
2370
0
                                                    aFlags, aFlags2);
2371
0
    if (!textRun) {
2372
0
        return nullptr;
2373
0
    }
2374
0
2375
0
    InitTextRun(aParams->mDrawTarget, textRun.get(), aString, aLength, aMFR);
2376
0
2377
0
    textRun->FetchGlyphExtents(aParams->mDrawTarget);
2378
0
2379
0
    return textRun.forget();
2380
0
}
2381
2382
template<typename T>
2383
void
2384
gfxFontGroup::InitTextRun(DrawTarget* aDrawTarget,
2385
                          gfxTextRun *aTextRun,
2386
                          const T *aString,
2387
                          uint32_t aLength,
2388
                          gfxMissingFontRecorder *aMFR)
2389
0
{
2390
0
    NS_ASSERTION(aLength > 0, "don't call InitTextRun for a zero-length run");
2391
0
2392
0
    // we need to do numeral processing even on 8-bit text,
2393
0
    // in case we're converting Western to Hindi/Arabic digits
2394
0
    int32_t numOption = gfxPlatform::GetPlatform()->GetBidiNumeralOption();
2395
0
    UniquePtr<char16_t[]> transformedString;
2396
0
    if (numOption != IBMBIDI_NUMERAL_NOMINAL) {
2397
0
        // scan the string for numerals that may need to be transformed;
2398
0
        // if we find any, we'll make a local copy here and use that for
2399
0
        // font matching and glyph generation/shaping
2400
0
        bool prevIsArabic =
2401
0
            !!(aTextRun->GetFlags() & ShapedTextFlags::TEXT_INCOMING_ARABICCHAR);
2402
0
        for (uint32_t i = 0; i < aLength; ++i) {
2403
0
            char16_t origCh = aString[i];
2404
0
            char16_t newCh = HandleNumberInChar(origCh, prevIsArabic, numOption);
2405
0
            if (newCh != origCh) {
2406
0
                if (!transformedString) {
2407
0
                    transformedString = MakeUnique<char16_t[]>(aLength);
2408
0
                    if (sizeof(T) == sizeof(char16_t)) {
2409
0
                        memcpy(transformedString.get(), aString, i * sizeof(char16_t));
2410
0
                    } else {
2411
0
                        for (uint32_t j = 0; j < i; ++j) {
2412
0
                            transformedString[j] = aString[j];
2413
0
                        }
2414
0
                    }
2415
0
                }
2416
0
            }
2417
0
            if (transformedString) {
2418
0
                transformedString[i] = newCh;
2419
0
            }
2420
0
            prevIsArabic = IS_ARABIC_CHAR(newCh);
2421
0
        }
2422
0
    }
2423
0
2424
0
    LogModule* log = mStyle.systemFont
2425
0
                   ? gfxPlatform::GetLog(eGfxLog_textrunui)
2426
0
                   : gfxPlatform::GetLog(eGfxLog_textrun);
2427
0
2428
0
    // variant fallback handling may end up passing through this twice
2429
0
    bool redo;
2430
0
    do {
2431
0
        redo = false;
2432
0
2433
0
        if (sizeof(T) == sizeof(uint8_t) && !transformedString) {
2434
0
2435
0
            if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2436
0
                nsAutoCString lang;
2437
0
                mStyle.language->ToUTF8String(lang);
2438
0
                nsAutoCString families;
2439
0
                mFamilyList.ToString(families);
2440
0
                nsAutoCString str((const char*)aString, aLength);
2441
0
                nsAutoString styleString;
2442
0
                nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
2443
0
                MOZ_LOG(log, LogLevel::Warning,\
2444
0
                       ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2445
0
                        "len %d weight: %g stretch: %g%% style: %s size: %6.2f %zu-byte "
2446
0
                        "TEXTRUN [%s] ENDTEXTRUN\n",
2447
0
                        (mStyle.systemFont ? "textrunui" : "textrun"),
2448
0
                        families.get(),
2449
0
                        (mFamilyList.GetDefaultFontType() == eFamily_serif ?
2450
0
                         "serif" :
2451
0
                         (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
2452
0
                          "sans-serif" : "none")),
2453
0
                        lang.get(), static_cast<int>(Script::LATIN), aLength,
2454
0
                        mStyle.weight.ToFloat(),
2455
0
                        mStyle.stretch.Percentage(),
2456
0
                        NS_ConvertUTF16toUTF8(styleString).get(),
2457
0
                        mStyle.size,
2458
0
                        sizeof(T),
2459
0
                        str.get()));
2460
0
            }
2461
0
2462
0
            // the text is still purely 8-bit; bypass the script-run itemizer
2463
0
            // and treat it as a single Latin run
2464
0
            InitScriptRun(aDrawTarget, aTextRun, aString,
2465
0
                          0, aLength, Script::LATIN, aMFR);
2466
0
        } else {
2467
0
            const char16_t *textPtr;
2468
0
            if (transformedString) {
2469
0
                textPtr = transformedString.get();
2470
0
            } else {
2471
0
                // typecast to avoid compilation error for the 8-bit version,
2472
0
                // even though this is dead code in that case
2473
0
                textPtr = reinterpret_cast<const char16_t*>(aString);
2474
0
            }
2475
0
2476
0
            // split into script runs so that script can potentially influence
2477
0
            // the font matching process below
2478
0
            gfxScriptItemizer scriptRuns(textPtr, aLength);
2479
0
2480
0
            uint32_t runStart = 0, runLimit = aLength;
2481
0
            Script runScript = Script::LATIN;
2482
0
            while (scriptRuns.Next(runStart, runLimit, runScript)) {
2483
0
2484
0
                if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Warning))) {
2485
0
                    nsAutoCString lang;
2486
0
                    mStyle.language->ToUTF8String(lang);
2487
0
                    nsAutoCString families;
2488
0
                    mFamilyList.ToString(families);
2489
0
                    nsAutoString styleString;
2490
0
                    nsStyleUtil::AppendFontSlantStyle(mStyle.style, styleString);
2491
0
                    uint32_t runLen = runLimit - runStart;
2492
0
                    MOZ_LOG(log, LogLevel::Warning,\
2493
0
                           ("(%s) fontgroup: [%s] default: %s lang: %s script: %d "
2494
0
                            "len %d weight: %g stretch: %g%% style: %s size: %6.2f "
2495
0
                            "%zu-byte TEXTRUN [%s] ENDTEXTRUN\n",
2496
0
                            (mStyle.systemFont ? "textrunui" : "textrun"),
2497
0
                            families.get(),
2498
0
                            (mFamilyList.GetDefaultFontType() == eFamily_serif ?
2499
0
                             "serif" :
2500
0
                             (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
2501
0
                              "sans-serif" : "none")),
2502
0
                            lang.get(), static_cast<int>(runScript), runLen,
2503
0
                            mStyle.weight.ToFloat(),
2504
0
                            mStyle.stretch.Percentage(),
2505
0
                            NS_ConvertUTF16toUTF8(styleString).get(),
2506
0
                            mStyle.size,
2507
0
                            sizeof(T),
2508
0
                            NS_ConvertUTF16toUTF8(textPtr + runStart, runLen).get()));
2509
0
                }
2510
0
2511
0
                InitScriptRun(aDrawTarget, aTextRun, textPtr + runStart,
2512
0
                              runStart, runLimit - runStart, runScript, aMFR);
2513
0
            }
2514
0
        }
2515
0
2516
0
        // if shaping was aborted due to lack of feature support, clear out
2517
0
        // glyph runs and redo shaping with fallback forced on
2518
0
        if (aTextRun->GetShapingState() == gfxTextRun::eShapingState_Aborted) {
2519
0
            redo = true;
2520
0
            aTextRun->SetShapingState(
2521
0
                gfxTextRun::eShapingState_ForceFallbackFeature);
2522
0
            aTextRun->ClearGlyphsAndCharacters();
2523
0
        }
2524
0
2525
0
    } while (redo);
2526
0
2527
0
    if (sizeof(T) == sizeof(char16_t) && aLength > 0) {
2528
0
        gfxTextRun::CompressedGlyph *glyph = aTextRun->GetCharacterGlyphs();
2529
0
        if (!glyph->IsSimpleGlyph()) {
2530
0
            glyph->SetClusterStart(true);
2531
0
        }
2532
0
    }
2533
0
2534
0
    // It's possible for CoreText to omit glyph runs if it decides they contain
2535
0
    // only invisibles (e.g., U+FEFF, see reftest 474417-1). In this case, we
2536
0
    // need to eliminate them from the glyph run array to avoid drawing "partial
2537
0
    // ligatures" with the wrong font.
2538
0
    // We don't do this during InitScriptRun (or gfxFont::InitTextRun) because
2539
0
    // it will iterate back over all glyphruns in the textrun, which leads to
2540
0
    // pathologically-bad perf in the case where a textrun contains many script
2541
0
    // changes (see bug 680402) - we'd end up re-sanitizing all the earlier runs
2542
0
    // every time a new script subrun is processed.
2543
0
    aTextRun->SanitizeGlyphRuns();
2544
0
2545
0
    aTextRun->SortGlyphRuns();
2546
0
}
Unexecuted instantiation: void gfxFontGroup::InitTextRun<unsigned char>(mozilla::gfx::DrawTarget*, gfxTextRun*, unsigned char const*, unsigned int, gfxMissingFontRecorder*)
Unexecuted instantiation: void gfxFontGroup::InitTextRun<char16_t>(mozilla::gfx::DrawTarget*, gfxTextRun*, char16_t const*, unsigned int, gfxMissingFontRecorder*)
2547
2548
static inline bool
2549
IsPUA(uint32_t aUSV)
2550
0
{
2551
0
    // We could look up the General Category of the codepoint here,
2552
0
    // but it's simpler to check PUA codepoint ranges.
2553
0
    return (aUSV >= 0xE000 && aUSV <= 0xF8FF) || (aUSV >= 0xF0000);
2554
0
}
2555
2556
template<typename T>
2557
void
2558
gfxFontGroup::InitScriptRun(DrawTarget* aDrawTarget,
2559
                            gfxTextRun *aTextRun,
2560
                            const T *aString, // text for this script run,
2561
                                              // not the entire textrun
2562
                            uint32_t aOffset, // position of the script run
2563
                                              // within the textrun
2564
                            uint32_t aLength, // length of the script run
2565
                            Script aRunScript,
2566
                            gfxMissingFontRecorder *aMFR)
2567
0
{
2568
0
    NS_ASSERTION(aLength > 0, "don't call InitScriptRun for a 0-length run");
2569
0
    NS_ASSERTION(aTextRun->GetShapingState() != gfxTextRun::eShapingState_Aborted,
2570
0
                 "don't call InitScriptRun with aborted shaping state");
2571
0
2572
0
    // confirm the load state of userfonts in the list
2573
0
    if (mUserFontSet &&
2574
0
        mCurrGeneration != mUserFontSet->GetGeneration()) {
2575
0
        UpdateUserFonts();
2576
0
    }
2577
0
2578
0
    gfxFont *mainFont = GetFirstValidFont();
2579
0
2580
0
    ShapedTextFlags orientation =
2581
0
        aTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK;
2582
0
2583
0
    if (orientation != ShapedTextFlags::TEXT_ORIENT_HORIZONTAL &&
2584
0
        (aRunScript == Script::MONGOLIAN || aRunScript == Script::PHAGS_PA)) {
2585
0
        // Mongolian and Phags-pa text should ignore text-orientation and
2586
0
        // always render in its "native" vertical mode, implemented by fonts
2587
0
        // as sideways-right (i.e as if shaped horizontally, and then the
2588
0
        // entire line is rotated to render vertically). Therefore, we ignore
2589
0
        // the aOrientation value from the textrun's flags, and make all
2590
0
        // vertical Mongolian/Phags-pa use sideways-right.
2591
0
        orientation = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
2592
0
    }
2593
0
2594
0
    uint32_t runStart = 0;
2595
0
    AutoTArray<gfxTextRange,3> fontRanges;
2596
0
    ComputeRanges(fontRanges, aString, aLength, aRunScript, orientation);
2597
0
    uint32_t numRanges = fontRanges.Length();
2598
0
    bool missingChars = false;
2599
0
2600
0
    for (uint32_t r = 0; r < numRanges; r++) {
2601
0
        const gfxTextRange& range = fontRanges[r];
2602
0
        uint32_t matchedLength = range.Length();
2603
0
        gfxFont *matchedFont = range.font;
2604
0
        // create the glyph run for this range
2605
0
        if (matchedFont && mStyle.noFallbackVariantFeatures) {
2606
0
            // common case - just do glyph layout and record the
2607
0
            // resulting positioned glyphs
2608
0
            aTextRun->AddGlyphRun(matchedFont, range.matchType,
2609
0
                                  aOffset + runStart, (matchedLength > 0),
2610
0
                                  range.orientation);
2611
0
            if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
2612
0
                                                  aString + runStart,
2613
0
                                                  aOffset + runStart,
2614
0
                                                  matchedLength,
2615
0
                                                  aRunScript,
2616
0
                                                  range.orientation)) {
2617
0
                // glyph layout failed! treat as missing glyphs
2618
0
                matchedFont = nullptr;
2619
0
            }
2620
0
        } else if (matchedFont) {
2621
0
            // shape with some variant feature that requires fallback handling
2622
0
            bool petiteToSmallCaps = false;
2623
0
            bool syntheticLower = false;
2624
0
            bool syntheticUpper = false;
2625
0
2626
0
            if (mStyle.variantSubSuper != NS_FONT_VARIANT_POSITION_NORMAL &&
2627
0
                (aTextRun->GetShapingState() ==
2628
0
                     gfxTextRun::eShapingState_ForceFallbackFeature ||
2629
0
                 !matchedFont->SupportsSubSuperscript(mStyle.variantSubSuper,
2630
0
                                                      aString, aLength,
2631
0
                                                      aRunScript)))
2632
0
            {
2633
0
                // fallback for subscript/superscript variant glyphs
2634
0
2635
0
                // if the feature was already used, abort and force
2636
0
                // fallback across the entire textrun
2637
0
                gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2638
0
2639
0
                if (ss == gfxTextRun::eShapingState_Normal) {
2640
0
                    aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFallback);
2641
0
                } else if (ss == gfxTextRun::eShapingState_ShapingWithFeature) {
2642
0
                    aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2643
0
                    return;
2644
0
                }
2645
0
2646
0
                RefPtr<gfxFont> subSuperFont =
2647
0
                    matchedFont->GetSubSuperscriptFont(aTextRun->GetAppUnitsPerDevUnit());
2648
0
                aTextRun->AddGlyphRun(subSuperFont, range.matchType,
2649
0
                                      aOffset + runStart, (matchedLength > 0),
2650
0
                                      range.orientation);
2651
0
                if (!subSuperFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
2652
0
                                                       aString + runStart,
2653
0
                                                       aOffset + runStart,
2654
0
                                                       matchedLength,
2655
0
                                                       aRunScript,
2656
0
                                                       range.orientation)) {
2657
0
                    // glyph layout failed! treat as missing glyphs
2658
0
                    matchedFont = nullptr;
2659
0
                }
2660
0
            } else if (mStyle.variantCaps != NS_FONT_VARIANT_CAPS_NORMAL &&
2661
0
                       !matchedFont->SupportsVariantCaps(aRunScript,
2662
0
                                                         mStyle.variantCaps,
2663
0
                                                         petiteToSmallCaps,
2664
0
                                                         syntheticLower,
2665
0
                                                         syntheticUpper))
2666
0
            {
2667
0
                // fallback for small-caps variant glyphs
2668
0
                if (!matchedFont->InitFakeSmallCapsRun(aDrawTarget, aTextRun,
2669
0
                                                       aString + runStart,
2670
0
                                                       aOffset + runStart,
2671
0
                                                       matchedLength,
2672
0
                                                       range.matchType,
2673
0
                                                       range.orientation,
2674
0
                                                       aRunScript,
2675
0
                                                       syntheticLower,
2676
0
                                                       syntheticUpper)) {
2677
0
                    matchedFont = nullptr;
2678
0
                }
2679
0
            } else {
2680
0
                // shape normally with variant feature enabled
2681
0
                gfxTextRun::ShapingState ss = aTextRun->GetShapingState();
2682
0
2683
0
                // adjust the shaping state if necessary
2684
0
                if (ss == gfxTextRun::eShapingState_Normal) {
2685
0
                    aTextRun->SetShapingState(gfxTextRun::eShapingState_ShapingWithFeature);
2686
0
                } else if (ss == gfxTextRun::eShapingState_ShapingWithFallback) {
2687
0
                    // already have shaping results using fallback, need to redo
2688
0
                    aTextRun->SetShapingState(gfxTextRun::eShapingState_Aborted);
2689
0
                    return;
2690
0
                }
2691
0
2692
0
                // do glyph layout and record the resulting positioned glyphs
2693
0
                aTextRun->AddGlyphRun(matchedFont, range.matchType,
2694
0
                                      aOffset + runStart, (matchedLength > 0),
2695
0
                                      range.orientation);
2696
0
                if (!matchedFont->SplitAndInitTextRun(aDrawTarget, aTextRun,
2697
0
                                                      aString + runStart,
2698
0
                                                      aOffset + runStart,
2699
0
                                                      matchedLength,
2700
0
                                                      aRunScript,
2701
0
                                                      range.orientation)) {
2702
0
                    // glyph layout failed! treat as missing glyphs
2703
0
                    matchedFont = nullptr;
2704
0
                }
2705
0
            }
2706
0
        } else {
2707
0
            aTextRun->AddGlyphRun(mainFont,
2708
0
                                  gfxTextRange::MatchType::kFontGroup,
2709
0
                                  aOffset + runStart, (matchedLength > 0),
2710
0
                                  range.orientation);
2711
0
        }
2712
0
2713
0
        if (!matchedFont) {
2714
0
            // We need to set cluster boundaries (and mark spaces) so that
2715
0
            // surrogate pairs, combining characters, etc behave properly,
2716
0
            // even if we don't have glyphs for them
2717
0
            aTextRun->SetupClusterBoundaries(aOffset + runStart, aString + runStart,
2718
0
                                             matchedLength);
2719
0
2720
0
            // various "missing" characters may need special handling,
2721
0
            // so we check for them here
2722
0
            uint32_t runLimit = runStart + matchedLength;
2723
0
            for (uint32_t index = runStart; index < runLimit; index++) {
2724
0
                T ch = aString[index];
2725
0
2726
0
                // tab and newline are not to be displayed as hexboxes,
2727
0
                // but do need to be recorded in the textrun
2728
0
                if (ch == '\n') {
2729
0
                    aTextRun->SetIsNewline(aOffset + index);
2730
0
                    continue;
2731
0
                }
2732
0
                if (ch == '\t') {
2733
0
                    aTextRun->SetIsTab(aOffset + index);
2734
0
                    continue;
2735
0
                }
2736
0
2737
0
                // for 16-bit textruns only, check for surrogate pairs and
2738
0
                // special Unicode spaces; omit these checks in 8-bit runs
2739
0
                if (sizeof(T) == sizeof(char16_t)) {
2740
0
                    if (NS_IS_HIGH_SURROGATE(ch) &&
2741
0
                        index + 1 < aLength &&
2742
0
                        NS_IS_LOW_SURROGATE(aString[index + 1]))
2743
0
                    {
2744
0
                        uint32_t usv =
2745
0
                            SURROGATE_TO_UCS4(ch, aString[index + 1]);
2746
0
                        aTextRun->SetMissingGlyph(aOffset + index,
2747
0
                                                  usv,
2748
0
                                                  mainFont);
2749
0
                        index++;
2750
0
                        if (!mSkipDrawing && !IsPUA(usv)) {
2751
0
                            missingChars = true;
2752
0
                        }
2753
0
                        continue;
2754
0
                    }
2755
0
2756
0
                    // check if this is a known Unicode whitespace character that
2757
0
                    // we can render using the space glyph with a custom width
2758
0
                    gfxFloat wid = mainFont->SynthesizeSpaceWidth(ch);
2759
0
                    if (wid >= 0.0) {
2760
0
                        nscoord advance =
2761
0
                            aTextRun->GetAppUnitsPerDevUnit() * floor(wid + 0.5);
2762
0
                        if (gfxShapedText::CompressedGlyph::IsSimpleAdvance(advance)) {
2763
0
                            aTextRun->GetCharacterGlyphs()[aOffset + index].
2764
0
                                SetSimpleGlyph(advance,
2765
0
                                               mainFont->GetSpaceGlyph());
2766
0
                        } else {
2767
0
                            gfxTextRun::DetailedGlyph detailedGlyph;
2768
0
                            detailedGlyph.mGlyphID = mainFont->GetSpaceGlyph();
2769
0
                            detailedGlyph.mAdvance = advance;
2770
0
                            CompressedGlyph g =
2771
0
                                CompressedGlyph::MakeComplex(true, true, 1);
2772
0
                            aTextRun->SetGlyphs(aOffset + index,
2773
0
                                                g, &detailedGlyph);
2774
0
                        }
2775
0
                        continue;
2776
0
                    }
2777
0
                }
2778
0
2779
0
                if (IsInvalidChar(ch)) {
2780
0
                    // invalid chars are left as zero-width/invisible
2781
0
                    continue;
2782
0
                }
2783
0
2784
0
                // record char code so we can draw a box with the Unicode value
2785
0
                aTextRun->SetMissingGlyph(aOffset + index, ch, mainFont);
2786
0
                if (!mSkipDrawing && !IsPUA(ch)) {
2787
0
                    missingChars = true;
2788
0
                }
2789
0
            }
2790
0
        }
2791
0
2792
0
        runStart += matchedLength;
2793
0
    }
2794
0
2795
0
    if (aMFR && missingChars) {
2796
0
        aMFR->RecordScript(aRunScript);
2797
0
    }
2798
0
}
Unexecuted instantiation: void gfxFontGroup::InitScriptRun<unsigned char>(mozilla::gfx::DrawTarget*, gfxTextRun*, unsigned char const*, unsigned int, unsigned int, mozilla::unicode::Script, gfxMissingFontRecorder*)
Unexecuted instantiation: void gfxFontGroup::InitScriptRun<char16_t>(mozilla::gfx::DrawTarget*, gfxTextRun*, char16_t const*, unsigned int, unsigned int, mozilla::unicode::Script, gfxMissingFontRecorder*)
2799
2800
gfxTextRun *
2801
gfxFontGroup::GetEllipsisTextRun(int32_t aAppUnitsPerDevPixel,
2802
                                 gfx::ShapedTextFlags aFlags,
2803
                                 LazyReferenceDrawTargetGetter& aRefDrawTargetGetter)
2804
0
{
2805
0
    MOZ_ASSERT(!(aFlags & ~ShapedTextFlags::TEXT_ORIENT_MASK),
2806
0
               "flags here should only be used to specify orientation");
2807
0
    if (mCachedEllipsisTextRun &&
2808
0
        (mCachedEllipsisTextRun->GetFlags() & ShapedTextFlags::TEXT_ORIENT_MASK) == aFlags &&
2809
0
        mCachedEllipsisTextRun->GetAppUnitsPerDevUnit() == aAppUnitsPerDevPixel) {
2810
0
        return mCachedEllipsisTextRun.get();
2811
0
    }
2812
0
2813
0
    // Use a Unicode ellipsis if the font supports it,
2814
0
    // otherwise use three ASCII periods as fallback.
2815
0
    gfxFont* firstFont = GetFirstValidFont(uint32_t(kEllipsisChar[0]));
2816
0
    nsString ellipsis = firstFont->HasCharacter(kEllipsisChar[0])
2817
0
        ? nsDependentString(kEllipsisChar,
2818
0
                            ArrayLength(kEllipsisChar) - 1)
2819
0
        : nsDependentString(kASCIIPeriodsChar,
2820
0
                            ArrayLength(kASCIIPeriodsChar) - 1);
2821
0
2822
0
    RefPtr<DrawTarget> refDT = aRefDrawTargetGetter.GetRefDrawTarget();
2823
0
    Parameters params = {
2824
0
        refDT, nullptr, nullptr, nullptr, 0, aAppUnitsPerDevPixel
2825
0
    };
2826
0
    mCachedEllipsisTextRun =
2827
0
        MakeTextRun(ellipsis.get(), ellipsis.Length(), &params,
2828
0
                    aFlags, nsTextFrameUtils::Flags(), nullptr);
2829
0
    if (!mCachedEllipsisTextRun) {
2830
0
        return nullptr;
2831
0
    }
2832
0
    // don't let the presence of a cached ellipsis textrun prolong the
2833
0
    // fontgroup's life
2834
0
    mCachedEllipsisTextRun->ReleaseFontGroup();
2835
0
    return mCachedEllipsisTextRun.get();
2836
0
}
2837
2838
gfxFont*
2839
gfxFontGroup::FindFallbackFaceForChar(gfxFontFamily* aFamily, uint32_t aCh)
2840
0
{
2841
0
    GlobalFontMatch data(aCh, mStyle);
2842
0
    aFamily->SearchAllFontsForChar(&data);
2843
0
    gfxFontEntry* fe = data.mBestMatch;
2844
0
    if (!fe) {
2845
0
        return nullptr;
2846
0
    }
2847
0
    return fe->FindOrMakeFont(&mStyle);
2848
0
}
2849
2850
gfxFloat
2851
gfxFontGroup::GetUnderlineOffset()
2852
0
{
2853
0
    if (mUnderlineOffset == UNDERLINE_OFFSET_NOT_SET) {
2854
0
        // if the fontlist contains a bad underline font, make the underline
2855
0
        // offset the min of the first valid font and bad font underline offsets
2856
0
        uint32_t len = mFonts.Length();
2857
0
        for (uint32_t i = 0; i < len; i++) {
2858
0
            FamilyFace& ff = mFonts[i];
2859
0
            if (!ff.IsUserFontContainer() &&
2860
0
                !ff.FontEntry()->IsUserFont() &&
2861
0
                ff.Family() &&
2862
0
                ff.Family()->IsBadUnderlineFamily()) {
2863
0
                gfxFont* font = GetFontAt(i);
2864
0
                if (!font) {
2865
0
                    continue;
2866
0
                }
2867
0
                gfxFloat bad = font->GetMetrics(gfxFont::eHorizontal).
2868
0
                                         underlineOffset;
2869
0
                gfxFloat first =
2870
0
                    GetFirstValidFont()->GetMetrics(gfxFont::eHorizontal).
2871
0
                                             underlineOffset;
2872
0
                mUnderlineOffset = std::min(first, bad);
2873
0
                return mUnderlineOffset;
2874
0
            }
2875
0
        }
2876
0
2877
0
        // no bad underline fonts, use the first valid font's metric
2878
0
        mUnderlineOffset = GetFirstValidFont()->
2879
0
            GetMetrics(gfxFont::eHorizontal).underlineOffset;
2880
0
    }
2881
0
2882
0
    return mUnderlineOffset;
2883
0
}
2884
2885
0
#define NARROW_NO_BREAK_SPACE 0x202fu
2886
2887
gfxFont*
2888
gfxFontGroup::FindFontForChar(uint32_t aCh, uint32_t aPrevCh, uint32_t aNextCh,
2889
                              Script aRunScript, gfxFont *aPrevMatchedFont,
2890
                              gfxTextRange::MatchType* aMatchType)
2891
0
{
2892
0
    // If the char is a cluster extender, we want to use the same font as the
2893
0
    // preceding character if possible. This is preferable to using the font
2894
0
    // group because it avoids breaks in shaping within a cluster.
2895
0
    if (aPrevMatchedFont && IsClusterExtender(aCh) &&
2896
0
        aPrevMatchedFont->HasCharacter(aCh)) {
2897
0
        return aPrevMatchedFont;
2898
0
    }
2899
0
2900
0
    // Special cases for NNBSP (as used in Mongolian):
2901
0
    if (aCh == NARROW_NO_BREAK_SPACE) {
2902
0
        // If there is no preceding character, try the font that we'd use
2903
0
        // for the next char (unless it's just another NNBSP; we don't try
2904
0
        // to look ahead through a whole run of them).
2905
0
        if (!aPrevCh && aNextCh && aNextCh != NARROW_NO_BREAK_SPACE) {
2906
0
            gfxFont* nextFont =
2907
0
                FindFontForChar(aNextCh, 0, 0, aRunScript, aPrevMatchedFont,
2908
0
                                aMatchType);
2909
0
            if (nextFont && nextFont->HasCharacter(aCh)) {
2910
0
                return nextFont;
2911
0
            }
2912
0
        }
2913
0
        // Otherwise, treat NNBSP like a cluster extender (as above) and try
2914
0
        // to continue the preceding font run.
2915
0
        if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
2916
0
            return aPrevMatchedFont;
2917
0
        }
2918
0
    }
2919
0
2920
0
    // To optimize common cases, try the first font in the font-group
2921
0
    // before going into the more detailed checks below
2922
0
    uint32_t nextIndex = 0;
2923
0
    bool isJoinControl = gfxFontUtils::IsJoinControl(aCh);
2924
0
    bool wasJoinCauser = gfxFontUtils::IsJoinCauser(aPrevCh);
2925
0
    bool isVarSelector = gfxFontUtils::IsVarSelector(aCh);
2926
0
2927
0
    if (!isJoinControl && !wasJoinCauser && !isVarSelector) {
2928
0
        gfxFont* firstFont = GetFontAt(0, aCh);
2929
0
        if (firstFont) {
2930
0
            if (firstFont->HasCharacter(aCh)) {
2931
0
                *aMatchType = gfxTextRange::MatchType::kFontGroup |
2932
0
                              gfxTextRange::MatchType(mFonts[0].Generic());
2933
0
                return firstFont;
2934
0
            }
2935
0
2936
0
            gfxFont* font = nullptr;
2937
0
            if (mFonts[0].CheckForFallbackFaces()) {
2938
0
                font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
2939
0
            } else if (!firstFont->GetFontEntry()->IsUserFont()) {
2940
0
                // For platform fonts (but not userfonts), we may need to do
2941
0
                // fallback within the family to handle cases where some faces
2942
0
                // such as Italic or Black have reduced character sets compared
2943
0
                // to the family's Regular face.
2944
0
                font = FindFallbackFaceForChar(mFonts[0].Family(), aCh);
2945
0
            }
2946
0
            if (font) {
2947
0
                *aMatchType = gfxTextRange::MatchType::kFontGroup |
2948
0
                              gfxTextRange::MatchType(mFonts[0].Generic());
2949
0
                return font;
2950
0
            }
2951
0
        }
2952
0
2953
0
        // we don't need to check the first font again below
2954
0
        ++nextIndex;
2955
0
    }
2956
0
2957
0
    if (aPrevMatchedFont) {
2958
0
        // Don't switch fonts for control characters, regardless of
2959
0
        // whether they are present in the current font, as they won't
2960
0
        // actually be rendered (see bug 716229)
2961
0
        if (isJoinControl ||
2962
0
            GetGeneralCategory(aCh) == HB_UNICODE_GENERAL_CATEGORY_CONTROL) {
2963
0
            return aPrevMatchedFont;
2964
0
        }
2965
0
2966
0
        // if previous character was a join-causer (ZWJ),
2967
0
        // use the same font as the previous range if we can
2968
0
        if (wasJoinCauser) {
2969
0
            if (aPrevMatchedFont->HasCharacter(aCh)) {
2970
0
                return aPrevMatchedFont;
2971
0
            }
2972
0
        }
2973
0
    }
2974
0
2975
0
    // if this character is a variation selector,
2976
0
    // use the previous font regardless of whether it supports VS or not.
2977
0
    // otherwise the text run will be divided.
2978
0
    if (isVarSelector) {
2979
0
        if (aPrevMatchedFont) {
2980
0
            return aPrevMatchedFont;
2981
0
        }
2982
0
        // VS alone. it's meaningless to search different fonts
2983
0
        return nullptr;
2984
0
    }
2985
0
2986
0
    // 1. check remaining fonts in the font group
2987
0
    uint32_t fontListLength = mFonts.Length();
2988
0
    for (uint32_t i = nextIndex; i < fontListLength; i++) {
2989
0
        FamilyFace& ff = mFonts[i];
2990
0
        if (ff.IsInvalid() || ff.IsLoading()) {
2991
0
            continue;
2992
0
        }
2993
0
2994
0
        // if available, use already made gfxFont and check for character
2995
0
        gfxFont* font = ff.Font();
2996
0
        if (font) {
2997
0
            if (font->HasCharacter(aCh)) {
2998
0
                *aMatchType = gfxTextRange::MatchType::kFontGroup |
2999
0
                              gfxTextRange::MatchType(ff.Generic());
3000
0
                return font;
3001
0
            }
3002
0
            continue;
3003
0
        }
3004
0
3005
0
        // don't have a gfxFont yet, test before building
3006
0
        gfxFontEntry *fe = ff.FontEntry();
3007
0
        if (fe->mIsUserFontContainer) {
3008
0
            // for userfonts, need to test both the unicode range map and
3009
0
            // the cmap of the platform font entry
3010
0
            gfxUserFontEntry* ufe = static_cast<gfxUserFontEntry*>(fe);
3011
0
3012
0
            // never match a character outside the defined unicode range
3013
0
            if (!ufe->CharacterInUnicodeRange(aCh)) {
3014
0
                continue;
3015
0
            }
3016
0
3017
0
            // load if not already loaded but only if no other font in similar
3018
0
            // range within family is loading
3019
0
            if (ufe->LoadState() == gfxUserFontEntry::STATUS_NOT_LOADED &&
3020
0
                !mSkipDrawing && !FontLoadingForFamily(ff.Family(), aCh)) {
3021
0
                ufe->Load();
3022
0
                ff.CheckState(mSkipDrawing);
3023
0
            }
3024
0
            gfxFontEntry* pfe = ufe->GetPlatformFontEntry();
3025
0
            if (pfe && pfe->HasCharacter(aCh)) {
3026
0
                font = GetFontAt(i, aCh);
3027
0
                if (font) {
3028
0
                    *aMatchType = gfxTextRange::MatchType::kFontGroup |
3029
0
                                  gfxTextRange::MatchType(mFonts[i].Generic());
3030
0
                    return font;
3031
0
                }
3032
0
            }
3033
0
        } else if (fe->HasCharacter(aCh)) {
3034
0
            // for normal platform fonts, after checking the cmap
3035
0
            // build the font via GetFontAt
3036
0
            font = GetFontAt(i, aCh);
3037
0
            if (font) {
3038
0
                *aMatchType = gfxTextRange::MatchType::kFontGroup |
3039
0
                              gfxTextRange::MatchType(mFonts[i].Generic());
3040
0
                return font;
3041
0
            }
3042
0
        }
3043
0
3044
0
        // check other family faces if needed
3045
0
        if (ff.CheckForFallbackFaces()) {
3046
0
            NS_ASSERTION(i == 0 ? true :
3047
0
                         !mFonts[i-1].CheckForFallbackFaces() ||
3048
0
                         !mFonts[i-1].Family()->Name().Equals(ff.Family()->Name()),
3049
0
                         "should only do fallback once per font family");
3050
0
            font = FindFallbackFaceForChar(ff.Family(), aCh);
3051
0
            if (font) {
3052
0
                *aMatchType = gfxTextRange::MatchType::kFontGroup |
3053
0
                              gfxTextRange::MatchType(ff.Generic());
3054
0
                return font;
3055
0
            }
3056
0
        } else {
3057
0
            // For platform fonts, but not user fonts, consider intra-family
3058
0
            // fallback to handle styles with reduced character sets (see
3059
0
            // also above).
3060
0
            fe = ff.FontEntry();
3061
0
            if (!fe->mIsUserFontContainer && !fe->IsUserFont()) {
3062
0
                font = FindFallbackFaceForChar(ff.Family(), aCh);
3063
0
                if (font) {
3064
0
                    *aMatchType = gfxTextRange::MatchType::kFontGroup |
3065
0
                                  gfxTextRange::MatchType(ff.Generic());
3066
0
                    return font;
3067
0
                }
3068
0
            }
3069
0
        }
3070
0
    }
3071
0
3072
0
    if (fontListLength == 0) {
3073
0
        gfxFont* defaultFont = GetDefaultFont();
3074
0
        if (defaultFont->HasCharacter(aCh)) {
3075
0
            *aMatchType = gfxTextRange::MatchType::kFontGroup;
3076
0
            return defaultFont;
3077
0
        }
3078
0
    }
3079
0
3080
0
    // if character is in Private Use Area, don't do matching against pref or system fonts
3081
0
    if ((aCh >= 0xE000  && aCh <= 0xF8FF) || (aCh >= 0xF0000 && aCh <= 0x10FFFD))
3082
0
        return nullptr;
3083
0
3084
0
    // 2. search pref fonts
3085
0
    gfxFont* font = WhichPrefFontSupportsChar(aCh, aNextCh);
3086
0
    if (font) {
3087
0
        *aMatchType = gfxTextRange::MatchType::kPrefsFallback;
3088
0
        return font;
3089
0
    }
3090
0
3091
0
    // 3. use fallback fonts
3092
0
    // -- before searching for something else check the font used for the previous character
3093
0
    if (aPrevMatchedFont && aPrevMatchedFont->HasCharacter(aCh)) {
3094
0
        *aMatchType = gfxTextRange::MatchType::kSystemFallback;
3095
0
        return aPrevMatchedFont;
3096
0
    }
3097
0
3098
0
    // for known "space" characters, don't do a full system-fallback search;
3099
0
    // we'll synthesize appropriate-width spaces instead of missing-glyph boxes
3100
0
    if (GetGeneralCategory(aCh) ==
3101
0
            HB_UNICODE_GENERAL_CATEGORY_SPACE_SEPARATOR &&
3102
0
        GetFirstValidFont()->SynthesizeSpaceWidth(aCh) >= 0.0)
3103
0
    {
3104
0
        return nullptr;
3105
0
    }
3106
0
3107
0
    // -- otherwise look for other stuff
3108
0
    *aMatchType = gfxTextRange::MatchType::kSystemFallback;
3109
0
    return WhichSystemFontSupportsChar(aCh, aNextCh, aRunScript);
3110
0
}
3111
3112
template<typename T>
3113
void gfxFontGroup::ComputeRanges(nsTArray<gfxTextRange>& aRanges,
3114
                                 const T *aString, uint32_t aLength,
3115
                                 Script aRunScript,
3116
                                 gfx::ShapedTextFlags aOrientation)
3117
0
{
3118
0
    NS_ASSERTION(aRanges.Length() == 0, "aRanges must be initially empty");
3119
0
    NS_ASSERTION(aLength > 0, "don't call ComputeRanges for zero-length text");
3120
0
3121
0
    uint32_t prevCh = 0;
3122
0
    uint32_t nextCh = aString[0];
3123
0
    if (sizeof(T) == sizeof(char16_t)) {
3124
0
        if (aLength > 1 && NS_IS_HIGH_SURROGATE(nextCh) &&
3125
0
                           NS_IS_LOW_SURROGATE(aString[1])) {
3126
0
            nextCh = SURROGATE_TO_UCS4(nextCh, aString[1]);
3127
0
        }
3128
0
    }
3129
0
    int32_t lastRangeIndex = -1;
3130
0
3131
0
    // initialize prevFont to the group's primary font, so that this will be
3132
0
    // used for string-initial control chars, etc rather than risk hitting font
3133
0
    // fallback for these (bug 716229)
3134
0
    FontFamilyType generic = eFamily_none;
3135
0
    gfxFont *prevFont = GetFirstValidFont(' ', &generic);
3136
0
3137
0
    // if we use the initial value of prevFont, we treat this as a match from
3138
0
    // the font group; fixes bug 978313
3139
0
    gfxTextRange::MatchType matchType = gfxTextRange::MatchType::kFontGroup |
3140
0
                                        gfxTextRange::MatchType(generic);
3141
0
3142
0
    for (uint32_t i = 0; i < aLength; i++) {
3143
0
3144
0
        const uint32_t origI = i; // save off in case we increase for surrogate
3145
0
3146
0
        // set up current ch
3147
0
        uint32_t ch = nextCh;
3148
0
3149
0
        // Get next char (if any) so that FindFontForChar can look ahead
3150
0
        // for a possible variation selector.
3151
0
3152
0
        if (sizeof(T) == sizeof(char16_t)) {
3153
0
            // In 16-bit case only, check for surrogate pairs.
3154
0
            if (ch > 0xffffu) {
3155
0
                i++;
3156
0
            }
3157
0
            if (i < aLength - 1) {
3158
0
                nextCh = aString[i + 1];
3159
0
                if ((i + 2 < aLength) && NS_IS_HIGH_SURROGATE(nextCh) &&
3160
0
                                         NS_IS_LOW_SURROGATE(aString[i + 2])) {
3161
0
                    nextCh = SURROGATE_TO_UCS4(nextCh, aString[i + 2]);
3162
0
                }
3163
0
            } else {
3164
0
                nextCh = 0;
3165
0
            }
3166
0
        } else {
3167
0
            // 8-bit case is trivial.
3168
0
            nextCh = i < aLength - 1 ? aString[i + 1] : 0;
3169
0
        }
3170
0
3171
0
        if (ch == 0xa0) {
3172
0
            ch = ' ';
3173
0
        }
3174
0
3175
0
        gfxFont* font;
3176
0
3177
0
        // Find the font for this char; but try to avoid calling the expensive
3178
0
        // FindFontForChar method for the most common case, where the first
3179
0
        // font in the list supports the current char, and it is not one of
3180
0
        // the special cases where FindFontForChar will attempt to propagate
3181
0
        // the font selected for an adjacent character.
3182
0
        if ((font = GetFontAt(0, ch)) != nullptr
3183
0
            && font->HasCharacter(ch)
3184
0
            && (sizeof(T) == sizeof(uint8_t)
3185
0
                || (!IsClusterExtender(ch)
3186
0
                    && ch != NARROW_NO_BREAK_SPACE
3187
0
                    && !gfxFontUtils::IsJoinControl(ch)
3188
0
                    && !gfxFontUtils::IsJoinCauser(prevCh)
3189
0
                    && !gfxFontUtils::IsVarSelector(ch)))) {
3190
0
            matchType = gfxTextRange::MatchType::kFontGroup |
3191
0
                        gfxTextRange::MatchType(mFonts[0].Generic());
3192
0
        } else {
3193
0
            font = FindFontForChar(ch, prevCh, nextCh, aRunScript, prevFont,
3194
0
                                   &matchType);
3195
0
        }
3196
0
3197
0
#ifndef RELEASE_OR_BETA
3198
0
        if (MOZ_UNLIKELY(mTextPerf)) {
3199
0
            if (matchType & gfxTextRange::MatchType::kPrefsFallback) {
3200
0
                mTextPerf->current.fallbackPrefs++;
3201
0
            } else if (matchType & gfxTextRange::MatchType::kSystemFallback) {
3202
0
                mTextPerf->current.fallbackSystem++;
3203
0
            }
3204
0
        }
3205
0
#endif
3206
0
3207
0
        prevCh = ch;
3208
0
3209
0
        ShapedTextFlags orient = aOrientation;
3210
0
        if (aOrientation == ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED) {
3211
0
            // For CSS text-orientation:mixed, we need to resolve orientation
3212
0
            // on a per-character basis using the UTR50 orientation property.
3213
0
            switch (GetVerticalOrientation(ch)) {
3214
0
            case VERTICAL_ORIENTATION_U:
3215
0
            case VERTICAL_ORIENTATION_Tu:
3216
0
                orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
3217
0
                break;
3218
0
            case VERTICAL_ORIENTATION_Tr: {
3219
0
                // We check for a vertical presentation form first as that's
3220
0
                // likely to be cheaper than inspecting lookups to see if the
3221
0
                // 'vert' feature is going to handle this character, and if the
3222
0
                // presentation form is available then it will be used as
3223
0
                // fallback if needed, so it's OK if the feature is missing.
3224
0
                uint32_t v = gfxHarfBuzzShaper::GetVerticalPresentationForm(ch);
3225
0
                orient = (!font ||
3226
0
                          (v && font->HasCharacter(v)) ||
3227
0
                          font->FeatureWillHandleChar(aRunScript,
3228
0
                                                      HB_TAG('v','e','r','t'),
3229
0
                                                      ch))
3230
0
                         ? ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT
3231
0
                         : ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
3232
0
                break;
3233
0
            }
3234
0
            case VERTICAL_ORIENTATION_R:
3235
0
                orient = ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
3236
0
                break;
3237
0
            }
3238
0
        }
3239
0
3240
0
        if (lastRangeIndex == -1) {
3241
0
            // first char ==> make a new range
3242
0
            aRanges.AppendElement(gfxTextRange(0, 1, font, matchType, orient));
3243
0
            lastRangeIndex++;
3244
0
            prevFont = font;
3245
0
        } else {
3246
0
            // if font or orientation has changed, make a new range...
3247
0
            // unless ch is a variation selector (bug 1248248)
3248
0
            gfxTextRange& prevRange = aRanges[lastRangeIndex];
3249
0
            if (prevRange.font != font ||
3250
0
                (prevRange.orientation != orient && !IsClusterExtender(ch))) {
3251
0
                // close out the previous range
3252
0
                prevRange.end = origI;
3253
0
                aRanges.AppendElement(gfxTextRange(origI, i + 1,
3254
0
                                                   font, matchType, orient));
3255
0
                lastRangeIndex++;
3256
0
3257
0
                // update prevFont for the next match, *unless* we switched
3258
0
                // fonts on a ZWJ, in which case propagating the changed font
3259
0
                // is probably not a good idea (see bug 619511)
3260
0
                if (sizeof(T) == sizeof(uint8_t) ||
3261
0
                    !gfxFontUtils::IsJoinCauser(ch))
3262
0
                {
3263
0
                    prevFont = font;
3264
0
                }
3265
0
            } else {
3266
0
                prevRange.matchType |= matchType;
3267
0
            }
3268
0
        }
3269
0
    }
3270
0
3271
0
    aRanges[lastRangeIndex].end = aLength;
3272
0
3273
0
#ifndef RELEASE_OR_BETA
3274
0
    LogModule* log = mStyle.systemFont
3275
0
                   ? gfxPlatform::GetLog(eGfxLog_textrunui)
3276
0
                   : gfxPlatform::GetLog(eGfxLog_textrun);
3277
0
3278
0
    if (MOZ_UNLIKELY(MOZ_LOG_TEST(log, LogLevel::Debug))) {
3279
0
        nsAutoCString lang;
3280
0
        mStyle.language->ToUTF8String(lang);
3281
0
        nsAutoCString families;
3282
0
        mFamilyList.ToString(families);
3283
0
3284
0
        // collect the font matched for each range
3285
0
        nsAutoCString fontMatches;
3286
0
        for (size_t i = 0, i_end = aRanges.Length(); i < i_end; i++) {
3287
0
            const gfxTextRange& r = aRanges[i];
3288
0
            nsAutoCString matchTypes;
3289
0
            if (r.matchType & gfxTextRange::MatchType::kFontGroup) {
3290
0
                matchTypes.AppendLiteral("list");
3291
0
            }
3292
0
            if (r.matchType & gfxTextRange::MatchType::kPrefsFallback) {
3293
0
                if (!matchTypes.IsEmpty()) {
3294
0
                    matchTypes.AppendLiteral(",");
3295
0
                }
3296
0
                matchTypes.AppendLiteral("prefs");
3297
0
            }
3298
0
            if (r.matchType & gfxTextRange::MatchType::kPrefsFallback) {
3299
0
                if (!matchTypes.IsEmpty()) {
3300
0
                    matchTypes.AppendLiteral(",");
3301
0
                }
3302
0
                matchTypes.AppendLiteral("sys");
3303
0
            }
3304
0
            fontMatches.AppendPrintf(" [%u:%u] %.200s (%s)", r.start, r.end,
3305
0
                (r.font.get() ?
3306
0
                 r.font->GetName().get() : "<null>"),
3307
0
                matchTypes.get());
3308
0
        }
3309
0
        MOZ_LOG(log, LogLevel::Debug,\
3310
0
               ("(%s-fontmatching) fontgroup: [%s] default: %s lang: %s script: %d"
3311
0
                "%s\n",
3312
0
                (mStyle.systemFont ? "textrunui" : "textrun"),
3313
0
                families.get(),
3314
0
                (mFamilyList.GetDefaultFontType() == eFamily_serif ?
3315
0
                 "serif" :
3316
0
                 (mFamilyList.GetDefaultFontType() == eFamily_sans_serif ?
3317
0
                  "sans-serif" : "none")),
3318
0
                lang.get(), static_cast<int>(aRunScript),
3319
0
                fontMatches.get()));
3320
0
    }
3321
0
#endif
3322
0
}
Unexecuted instantiation: void gfxFontGroup::ComputeRanges<unsigned char>(nsTArray<gfxTextRange>&, unsigned char const*, unsigned int, mozilla::unicode::Script, mozilla::gfx::ShapedTextFlags)
Unexecuted instantiation: void gfxFontGroup::ComputeRanges<char16_t>(nsTArray<gfxTextRange>&, char16_t const*, unsigned int, mozilla::unicode::Script, mozilla::gfx::ShapedTextFlags)
3323
3324
gfxUserFontSet*
3325
gfxFontGroup::GetUserFontSet()
3326
0
{
3327
0
    return mUserFontSet;
3328
0
}
3329
3330
void
3331
gfxFontGroup::SetUserFontSet(gfxUserFontSet *aUserFontSet)
3332
0
{
3333
0
    if (aUserFontSet == mUserFontSet) {
3334
0
        return;
3335
0
    }
3336
0
    mUserFontSet = aUserFontSet;
3337
0
    mCurrGeneration = GetGeneration() - 1;
3338
0
    UpdateUserFonts();
3339
0
}
3340
3341
uint64_t
3342
gfxFontGroup::GetGeneration()
3343
0
{
3344
0
    if (!mUserFontSet)
3345
0
        return 0;
3346
0
    return mUserFontSet->GetGeneration();
3347
0
}
3348
3349
uint64_t
3350
gfxFontGroup::GetRebuildGeneration()
3351
0
{
3352
0
    if (!mUserFontSet)
3353
0
        return 0;
3354
0
    return mUserFontSet->GetRebuildGeneration();
3355
0
}
3356
3357
void
3358
gfxFontGroup::UpdateUserFonts()
3359
0
{
3360
0
    if (mCurrGeneration < GetRebuildGeneration()) {
3361
0
        // fonts in userfont set changed, need to redo the fontlist
3362
0
        mFonts.Clear();
3363
0
        ClearCachedData();
3364
0
        BuildFontList();
3365
0
        mCurrGeneration = GetGeneration();
3366
0
    } else if (mCurrGeneration != GetGeneration()) {
3367
0
        // load state change occurred, verify load state and validity of fonts
3368
0
        ClearCachedData();
3369
0
3370
0
        uint32_t len = mFonts.Length();
3371
0
        for (uint32_t i = 0; i < len; i++) {
3372
0
            FamilyFace& ff = mFonts[i];
3373
0
            if (ff.Font() || !ff.IsUserFontContainer()) {
3374
0
                continue;
3375
0
            }
3376
0
            ff.CheckState(mSkipDrawing);
3377
0
        }
3378
0
3379
0
        mCurrGeneration = GetGeneration();
3380
0
    }
3381
0
}
3382
3383
bool
3384
gfxFontGroup::ContainsUserFont(const gfxUserFontEntry* aUserFont)
3385
0
{
3386
0
    UpdateUserFonts();
3387
0
    // search through the fonts list for a specific user font
3388
0
    uint32_t len = mFonts.Length();
3389
0
    for (uint32_t i = 0; i < len; i++) {
3390
0
        FamilyFace& ff = mFonts[i];
3391
0
        if (ff.EqualsUserFont(aUserFont)) {
3392
0
            return true;
3393
0
        }
3394
0
    }
3395
0
    return false;
3396
0
}
3397
3398
gfxFont*
3399
gfxFontGroup::WhichPrefFontSupportsChar(uint32_t aCh, uint32_t aNextCh)
3400
0
{
3401
0
    eFontPrefLang charLang;
3402
0
    gfxPlatformFontList* pfl = gfxPlatformFontList::PlatformFontList();
3403
0
3404
0
    EmojiPresentation emoji = GetEmojiPresentation(aCh);
3405
0
    if ((emoji != EmojiPresentation::TextOnly &&
3406
0
         (aNextCh == kVariationSelector16 ||
3407
0
          (emoji == EmojiPresentation::EmojiDefault &&
3408
0
           aNextCh != kVariationSelector15)))) {
3409
0
        charLang = eFontPrefLang_Emoji;
3410
0
    } else {
3411
0
        // get the pref font list if it hasn't been set up already
3412
0
        uint32_t unicodeRange = FindCharUnicodeRange(aCh);
3413
0
        charLang = pfl->GetFontPrefLangFor(unicodeRange);
3414
0
    }
3415
0
3416
0
    // if the last pref font was the first family in the pref list, no need to recheck through a list of families
3417
0
    if (mLastPrefFont && charLang == mLastPrefLang &&
3418
0
        mLastPrefFirstFont && mLastPrefFont->HasCharacter(aCh)) {
3419
0
        return mLastPrefFont;
3420
0
    }
3421
0
3422
0
    // based on char lang and page lang, set up list of pref lang fonts to check
3423
0
    eFontPrefLang prefLangs[kMaxLenPrefLangList];
3424
0
    uint32_t i, numLangs = 0;
3425
0
3426
0
    pfl->GetLangPrefs(prefLangs, numLangs, charLang, mPageLang);
3427
0
3428
0
    for (i = 0; i < numLangs; i++) {
3429
0
        eFontPrefLang currentLang = prefLangs[i];
3430
0
        mozilla::FontFamilyType defaultGeneric =
3431
0
            pfl->GetDefaultGeneric(currentLang);
3432
0
        nsTArray<RefPtr<gfxFontFamily>>* families =
3433
0
            pfl->GetPrefFontsLangGroup(defaultGeneric, currentLang);
3434
0
        NS_ASSERTION(families, "no pref font families found");
3435
0
3436
0
        // find the first pref font that includes the character
3437
0
        uint32_t  j, numPrefs;
3438
0
        numPrefs = families->Length();
3439
0
        for (j = 0; j < numPrefs; j++) {
3440
0
            // look up the appropriate face
3441
0
            gfxFontFamily *family = (*families)[j];
3442
0
            if (!family) {
3443
0
                continue;
3444
0
            }
3445
0
3446
0
            // if a pref font is used, it's likely to be used again in the same text run.
3447
0
            // the style doesn't change so the face lookup can be cached rather than calling
3448
0
            // FindOrMakeFont repeatedly.  speeds up FindFontForChar lookup times for subsequent
3449
0
            // pref font lookups
3450
0
            if (family == mLastPrefFamily && mLastPrefFont->HasCharacter(aCh)) {
3451
0
                return mLastPrefFont;
3452
0
            }
3453
0
3454
0
            gfxFontEntry *fe = family->FindFontForStyle(mStyle);
3455
0
            if (!fe) {
3456
0
                continue;
3457
0
            }
3458
0
3459
0
            // if ch in cmap, create and return a gfxFont
3460
0
            if (fe->HasCharacter(aCh)) {
3461
0
                gfxFont* prefFont = fe->FindOrMakeFont(&mStyle);
3462
0
                if (!prefFont) {
3463
0
                    continue;
3464
0
                }
3465
0
                mLastPrefFamily = family;
3466
0
                mLastPrefFont = prefFont;
3467
0
                mLastPrefLang = charLang;
3468
0
                mLastPrefFirstFont = (i == 0 && j == 0);
3469
0
                return prefFont;
3470
0
            }
3471
0
3472
0
            // If the char was not available, see if we can fall back to an
3473
0
            // alternative face in the same family.
3474
0
            gfxFont* prefFont = FindFallbackFaceForChar(family, aCh);
3475
0
            if (prefFont) {
3476
0
                mLastPrefFamily = family;
3477
0
                mLastPrefFont = prefFont;
3478
0
                mLastPrefLang = charLang;
3479
0
                mLastPrefFirstFont = (i == 0 && j == 0);
3480
0
                return prefFont;
3481
0
            }
3482
0
        }
3483
0
    }
3484
0
3485
0
    return nullptr;
3486
0
}
3487
3488
gfxFont*
3489
gfxFontGroup::WhichSystemFontSupportsChar(uint32_t aCh, uint32_t aNextCh,
3490
                                          Script aRunScript)
3491
0
{
3492
0
    gfxFontEntry *fe =
3493
0
        gfxPlatformFontList::PlatformFontList()->
3494
0
            SystemFindFontForChar(aCh, aNextCh, aRunScript, &mStyle);
3495
0
    if (fe) {
3496
0
        return fe->FindOrMakeFont(&mStyle);
3497
0
    }
3498
0
3499
0
    return nullptr;
3500
0
}
3501
3502
void
3503
gfxMissingFontRecorder::Flush()
3504
0
{
3505
0
    static bool mNotifiedFontsInitialized = false;
3506
0
    static uint32_t mNotifiedFonts[gfxMissingFontRecorder::kNumScriptBitsWords];
3507
0
    if (!mNotifiedFontsInitialized) {
3508
0
        memset(&mNotifiedFonts, 0, sizeof(mNotifiedFonts));
3509
0
        mNotifiedFontsInitialized = true;
3510
0
    }
3511
0
3512
0
    nsAutoString fontNeeded;
3513
0
    for (uint32_t i = 0; i < kNumScriptBitsWords; ++i) {
3514
0
        mMissingFonts[i] &= ~mNotifiedFonts[i];
3515
0
        if (!mMissingFonts[i]) {
3516
0
            continue;
3517
0
        }
3518
0
        for (uint32_t j = 0; j < 32; ++j) {
3519
0
            if (!(mMissingFonts[i] & (1 << j))) {
3520
0
                continue;
3521
0
            }
3522
0
            mNotifiedFonts[i] |= (1 << j);
3523
0
            if (!fontNeeded.IsEmpty()) {
3524
0
                fontNeeded.Append(char16_t(','));
3525
0
            }
3526
0
            uint32_t sc = i * 32 + j;
3527
0
            MOZ_ASSERT(sc < static_cast<uint32_t>(Script::NUM_SCRIPT_CODES),
3528
0
                       "how did we set the bit for an invalid script code?");
3529
0
            uint32_t tag = GetScriptTagForCode(static_cast<Script>(sc));
3530
0
            fontNeeded.Append(char16_t(tag >> 24));
3531
0
            fontNeeded.Append(char16_t((tag >> 16) & 0xff));
3532
0
            fontNeeded.Append(char16_t((tag >> 8) & 0xff));
3533
0
            fontNeeded.Append(char16_t(tag & 0xff));
3534
0
        }
3535
0
        mMissingFonts[i] = 0;
3536
0
    }
3537
0
    if (!fontNeeded.IsEmpty()) {
3538
0
        nsCOMPtr<nsIObserverService> service = GetObserverService();
3539
0
        service->NotifyObservers(nullptr, "font-needed", fontNeeded.get());
3540
0
    }
3541
0
}