Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/src/nsFontMetrics.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
 * License, v. 2.0. If a copy of the MPL was not distributed with this
4
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "nsFontMetrics.h"
7
#include <math.h>                       // for floor, ceil
8
#include <algorithm>                    // for max
9
#include "gfxContext.h"                 // for gfxContext
10
#include "gfxFontConstants.h"           // for NS_FONT_SYNTHESIS_*
11
#include "gfxPlatform.h"                // for gfxPlatform
12
#include "gfxPoint.h"                   // for gfxPoint
13
#include "gfxRect.h"                    // for gfxRect
14
#include "gfxTypes.h"                   // for gfxFloat
15
#include "nsBoundingMetrics.h"          // for nsBoundingMetrics
16
#include "nsDebug.h"                    // for NS_ERROR
17
#include "nsDeviceContext.h"            // for nsDeviceContext
18
#include "nsAtom.h"                    // for nsAtom
19
#include "nsMathUtils.h"                // for NS_round
20
#include "nsString.h"                   // for nsString
21
#include "nsStyleConsts.h"              // for StyleHyphens::None
22
#include "mozilla/Assertions.h"         // for MOZ_ASSERT
23
#include "mozilla/UniquePtr.h"          // for UniquePtr
24
25
class gfxUserFontSet;
26
using namespace mozilla;
27
28
namespace {
29
30
class AutoTextRun {
31
public:
32
    typedef mozilla::gfx::DrawTarget DrawTarget;
33
34
    AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
35
                const char* aString, int32_t aLength)
36
0
    {
37
0
        mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
38
0
            reinterpret_cast<const uint8_t*>(aString), aLength,
39
0
            aDrawTarget,
40
0
            aMetrics->AppUnitsPerDevPixel(),
41
0
            ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
42
0
            nullptr);
43
0
    }
44
45
    AutoTextRun(nsFontMetrics* aMetrics, DrawTarget* aDrawTarget,
46
                const char16_t* aString, int32_t aLength)
47
0
    {
48
0
        mTextRun = aMetrics->GetThebesFontGroup()->MakeTextRun(
49
0
            aString, aLength,
50
0
            aDrawTarget,
51
0
            aMetrics->AppUnitsPerDevPixel(),
52
0
            ComputeFlags(aMetrics), nsTextFrameUtils::Flags(),
53
0
            nullptr);
54
0
    }
55
56
0
    gfxTextRun *get() { return mTextRun.get(); }
57
0
    gfxTextRun *operator->() { return mTextRun.get(); }
58
59
private:
60
0
    static gfx::ShapedTextFlags ComputeFlags(nsFontMetrics* aMetrics) {
61
0
        gfx::ShapedTextFlags flags = gfx::ShapedTextFlags();
62
0
        if (aMetrics->GetTextRunRTL()) {
63
0
            flags |= gfx::ShapedTextFlags::TEXT_IS_RTL;
64
0
        }
65
0
        if (aMetrics->GetVertical()) {
66
0
            switch (aMetrics->GetTextOrientation()) {
67
0
            case NS_STYLE_TEXT_ORIENTATION_MIXED:
68
0
                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_MIXED;
69
0
                break;
70
0
            case NS_STYLE_TEXT_ORIENTATION_UPRIGHT:
71
0
                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_UPRIGHT;
72
0
                break;
73
0
            case NS_STYLE_TEXT_ORIENTATION_SIDEWAYS:
74
0
                flags |= gfx::ShapedTextFlags::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
75
0
                break;
76
0
            }
77
0
        }
78
0
        return flags;
79
0
    }
80
81
    RefPtr<gfxTextRun> mTextRun;
82
};
83
84
class StubPropertyProvider final : public gfxTextRun::PropertyProvider {
85
public:
86
    void GetHyphenationBreaks(gfxTextRun::Range aRange,
87
0
                              gfxTextRun::HyphenType* aBreakBefore) const override {
88
0
        NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
89
0
    }
90
0
    mozilla::StyleHyphens GetHyphensOption() const override {
91
0
        NS_ERROR("This shouldn't be called because we never call BreakAndMeasureText");
92
0
        return mozilla::StyleHyphens::None;
93
0
    }
94
0
    gfxFloat GetHyphenWidth() const override {
95
0
        NS_ERROR("This shouldn't be called because we never enable hyphens");
96
0
        return 0;
97
0
    }
98
0
    already_AddRefed<mozilla::gfx::DrawTarget> GetDrawTarget() const override {
99
0
        NS_ERROR("This shouldn't be called because we never enable hyphens");
100
0
        return nullptr;
101
0
    }
102
0
    uint32_t GetAppUnitsPerDevUnit() const override {
103
0
        NS_ERROR("This shouldn't be called because we never enable hyphens");
104
0
        return 60;
105
0
    }
106
0
    void GetSpacing(gfxTextRun::Range aRange, Spacing* aSpacing) const override {
107
0
        NS_ERROR("This shouldn't be called because we never enable spacing");
108
0
    }
109
};
110
111
} // namespace
112
113
nsFontMetrics::nsFontMetrics(const nsFont& aFont, const Params& aParams,
114
                             nsDeviceContext *aContext)
115
    : mFont(aFont)
116
    , mLanguage(aParams.language)
117
    , mDeviceContext(aContext)
118
    , mP2A(aContext->AppUnitsPerDevPixel())
119
    , mOrientation(aParams.orientation)
120
    , mTextRunRTL(false)
121
    , mVertical(false)
122
    , mTextOrientation(0)
123
0
{
124
0
    gfxFontStyle style(aFont.style,
125
0
                       aFont.weight,
126
0
                       aFont.stretch,
127
0
                       gfxFloat(aFont.size) / mP2A,
128
0
                       aParams.language,
129
0
                       aParams.explicitLanguage,
130
0
                       aFont.sizeAdjust,
131
0
                       aFont.systemFont,
132
0
                       mDeviceContext->IsPrinterContext(),
133
0
                       aFont.synthesis & NS_FONT_SYNTHESIS_WEIGHT,
134
0
                       aFont.synthesis & NS_FONT_SYNTHESIS_STYLE,
135
0
                       aFont.languageOverride);
136
0
137
0
    aFont.AddFontFeaturesToStyle(&style, mOrientation == gfxFont::eVertical);
138
0
    aFont.AddFontVariationsToStyle(&style);
139
0
140
0
    gfxFloat devToCssSize = gfxFloat(mP2A) /
141
0
        gfxFloat(AppUnitsPerCSSPixel());
142
0
    mFontGroup = gfxPlatform::GetPlatform()->
143
0
        CreateFontGroup(aFont.fontlist, &style, aParams.textPerf,
144
0
                        aParams.userFontSet, devToCssSize);
145
0
}
146
147
nsFontMetrics::~nsFontMetrics()
148
0
{
149
0
    // Should not be dropped by stylo
150
0
    MOZ_ASSERT(NS_IsMainThread());
151
0
    if (mDeviceContext) {
152
0
        mDeviceContext->FontMetricsDeleted(this);
153
0
    }
154
0
}
155
156
void
157
nsFontMetrics::Destroy()
158
0
{
159
0
    mDeviceContext = nullptr;
160
0
}
161
162
// XXXTODO get rid of this macro
163
0
#define ROUND_TO_TWIPS(x) (nscoord)floor(((x) * mP2A) + 0.5)
164
0
#define CEIL_TO_TWIPS(x) (nscoord)ceil((x) * mP2A)
165
166
const gfxFont::Metrics&
167
nsFontMetrics::GetMetrics(gfxFont::Orientation aOrientation) const
168
0
{
169
0
    return mFontGroup->GetFirstValidFont()->GetMetrics(aOrientation);
170
0
}
171
172
nscoord
173
nsFontMetrics::XHeight()
174
0
{
175
0
    return ROUND_TO_TWIPS(GetMetrics().xHeight);
176
0
}
177
178
nscoord
179
nsFontMetrics::CapHeight()
180
0
{
181
0
    return ROUND_TO_TWIPS(GetMetrics().capHeight);
182
0
}
183
184
nscoord
185
nsFontMetrics::SuperscriptOffset()
186
0
{
187
0
    return ROUND_TO_TWIPS(GetMetrics().emHeight *
188
0
                          NS_FONT_SUPERSCRIPT_OFFSET_RATIO);
189
0
}
190
191
nscoord
192
nsFontMetrics::SubscriptOffset()
193
0
{
194
0
    return ROUND_TO_TWIPS(GetMetrics().emHeight *
195
0
                          NS_FONT_SUBSCRIPT_OFFSET_RATIO);
196
0
}
197
198
void
199
nsFontMetrics::GetStrikeout(nscoord& aOffset, nscoord& aSize)
200
0
{
201
0
    aOffset = ROUND_TO_TWIPS(GetMetrics().strikeoutOffset);
202
0
    aSize = ROUND_TO_TWIPS(GetMetrics().strikeoutSize);
203
0
}
204
205
void
206
nsFontMetrics::GetUnderline(nscoord& aOffset, nscoord& aSize)
207
0
{
208
0
    aOffset = ROUND_TO_TWIPS(mFontGroup->GetUnderlineOffset());
209
0
    aSize = ROUND_TO_TWIPS(GetMetrics().underlineSize);
210
0
}
211
212
// GetMaxAscent/GetMaxDescent/GetMaxHeight must contain the
213
// text-decoration lines drawable area. See bug 421353.
214
// BE CAREFUL for rounding each values. The logic MUST be same as
215
// nsCSSRendering::GetTextDecorationRectInternal's.
216
217
static gfxFloat ComputeMaxDescent(const gfxFont::Metrics& aMetrics,
218
                                  gfxFontGroup* aFontGroup)
219
0
{
220
0
    gfxFloat offset = floor(-aFontGroup->GetUnderlineOffset() + 0.5);
221
0
    gfxFloat size = NS_round(aMetrics.underlineSize);
222
0
    gfxFloat minDescent = offset + size;
223
0
    return floor(std::max(minDescent, aMetrics.maxDescent) + 0.5);
224
0
}
225
226
static gfxFloat ComputeMaxAscent(const gfxFont::Metrics& aMetrics)
227
0
{
228
0
    return floor(aMetrics.maxAscent + 0.5);
229
0
}
230
231
nscoord
232
nsFontMetrics::InternalLeading()
233
0
{
234
0
    return ROUND_TO_TWIPS(GetMetrics().internalLeading);
235
0
}
236
237
nscoord
238
nsFontMetrics::ExternalLeading()
239
0
{
240
0
    return ROUND_TO_TWIPS(GetMetrics().externalLeading);
241
0
}
242
243
nscoord
244
nsFontMetrics::EmHeight()
245
0
{
246
0
    return ROUND_TO_TWIPS(GetMetrics().emHeight);
247
0
}
248
249
nscoord
250
nsFontMetrics::EmAscent()
251
0
{
252
0
    return ROUND_TO_TWIPS(GetMetrics().emAscent);
253
0
}
254
255
nscoord
256
nsFontMetrics::EmDescent()
257
0
{
258
0
    return ROUND_TO_TWIPS(GetMetrics().emDescent);
259
0
}
260
261
nscoord
262
nsFontMetrics::MaxHeight()
263
0
{
264
0
    return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics())) +
265
0
        CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
266
0
}
267
268
nscoord
269
nsFontMetrics::MaxAscent()
270
0
{
271
0
    return CEIL_TO_TWIPS(ComputeMaxAscent(GetMetrics()));
272
0
}
273
274
nscoord
275
nsFontMetrics::MaxDescent()
276
0
{
277
0
    return CEIL_TO_TWIPS(ComputeMaxDescent(GetMetrics(), mFontGroup));
278
0
}
279
280
nscoord
281
nsFontMetrics::MaxAdvance()
282
0
{
283
0
    return CEIL_TO_TWIPS(GetMetrics().maxAdvance);
284
0
}
285
286
nscoord
287
nsFontMetrics::AveCharWidth()
288
0
{
289
0
    // Use CEIL instead of ROUND for consistency with GetMaxAdvance
290
0
    return CEIL_TO_TWIPS(GetMetrics().aveCharWidth);
291
0
}
292
293
nscoord
294
nsFontMetrics::SpaceWidth()
295
0
{
296
0
    // For vertical text with mixed or sideways orientation, we want the
297
0
    // width of a horizontal space (even if we're using vertical line-spacing
298
0
    // metrics, as with "writing-mode:vertical-*;text-orientation:mixed").
299
0
    return CEIL_TO_TWIPS(
300
0
        GetMetrics(mVertical &&
301
0
                   mTextOrientation == NS_STYLE_TEXT_ORIENTATION_UPRIGHT
302
0
                       ? gfxFont::eVertical
303
0
                       : gfxFont::eHorizontal).spaceWidth);
304
0
}
305
306
int32_t
307
nsFontMetrics::GetMaxStringLength()
308
0
{
309
0
    const gfxFont::Metrics& m = GetMetrics();
310
0
    const double x = 32767.0 / std::max(1.0, m.maxAdvance);
311
0
    int32_t len = (int32_t)floor(x);
312
0
    return std::max(1, len);
313
0
}
314
315
nscoord
316
nsFontMetrics::GetWidth(const char* aString, uint32_t aLength,
317
                        DrawTarget* aDrawTarget)
318
0
{
319
0
    if (aLength == 0)
320
0
        return 0;
321
0
322
0
    if (aLength == 1 && aString[0] == ' ')
323
0
        return SpaceWidth();
324
0
325
0
    StubPropertyProvider provider;
326
0
    AutoTextRun textRun(this, aDrawTarget, aString, aLength);
327
0
    if (textRun.get()) {
328
0
      return NSToCoordRound(
329
0
          textRun->GetAdvanceWidth(Range(0, aLength), &provider));
330
0
    }
331
0
    return 0;
332
0
}
333
334
nscoord
335
nsFontMetrics::GetWidth(const char16_t* aString, uint32_t aLength,
336
                        DrawTarget* aDrawTarget)
337
0
{
338
0
    if (aLength == 0)
339
0
        return 0;
340
0
341
0
    if (aLength == 1 && aString[0] == ' ')
342
0
        return SpaceWidth();
343
0
344
0
    StubPropertyProvider provider;
345
0
    AutoTextRun textRun(this, aDrawTarget, aString, aLength);
346
0
    if (textRun.get()) {
347
0
      return NSToCoordRound(
348
0
          textRun->GetAdvanceWidth(Range(0, aLength), &provider));
349
0
    }
350
0
    return 0;
351
0
}
352
353
// Draw a string using this font handle on the surface passed in.
354
void
355
nsFontMetrics::DrawString(const char *aString, uint32_t aLength,
356
                          nscoord aX, nscoord aY,
357
                          gfxContext *aContext)
358
0
{
359
0
    if (aLength == 0)
360
0
        return;
361
0
362
0
    StubPropertyProvider provider;
363
0
    AutoTextRun textRun(this, aContext->GetDrawTarget(), aString, aLength);
364
0
    if (!textRun.get()) {
365
0
        return;
366
0
    }
367
0
    gfx::Point pt(aX, aY);
368
0
    Range range(0, aLength);
369
0
    if (mTextRunRTL) {
370
0
        if (mVertical) {
371
0
            pt.y += textRun->GetAdvanceWidth(range, &provider);
372
0
        } else {
373
0
            pt.x += textRun->GetAdvanceWidth(range, &provider);
374
0
        }
375
0
    }
376
0
    gfxTextRun::DrawParams params(aContext);
377
0
    params.provider = &provider;
378
0
    textRun->Draw(range, pt, params);
379
0
}
380
381
void
382
nsFontMetrics::DrawString(const char16_t* aString, uint32_t aLength,
383
                          nscoord aX, nscoord aY,
384
                          gfxContext *aContext,
385
                          DrawTarget* aTextRunConstructionDrawTarget)
386
0
{
387
0
    if (aLength == 0)
388
0
        return;
389
0
390
0
    StubPropertyProvider provider;
391
0
    AutoTextRun textRun(this, aTextRunConstructionDrawTarget, aString, aLength);
392
0
    if (!textRun.get()) {
393
0
        return;
394
0
    }
395
0
    gfx::Point pt(aX, aY);
396
0
    Range range(0, aLength);
397
0
    if (mTextRunRTL) {
398
0
        if (mVertical) {
399
0
            pt.y += textRun->GetAdvanceWidth(range, &provider);
400
0
        } else {
401
0
            pt.x += textRun->GetAdvanceWidth(range, &provider);
402
0
        }
403
0
    }
404
0
    gfxTextRun::DrawParams params(aContext);
405
0
    params.provider = &provider;
406
0
    textRun->Draw(range, pt, params);
407
0
}
408
409
static nsBoundingMetrics
410
GetTextBoundingMetrics(nsFontMetrics* aMetrics, const char16_t* aString,
411
                       uint32_t aLength, mozilla::gfx::DrawTarget* aDrawTarget,
412
                       gfxFont::BoundingBoxType aType)
413
0
{
414
0
    if (aLength == 0)
415
0
        return nsBoundingMetrics();
416
0
417
0
    StubPropertyProvider provider;
418
0
    AutoTextRun textRun(aMetrics, aDrawTarget, aString, aLength);
419
0
    nsBoundingMetrics m;
420
0
    if (textRun.get()) {
421
0
        gfxTextRun::Metrics theMetrics = textRun->MeasureText(
422
0
            gfxTextRun::Range(0, aLength), aType, aDrawTarget, &provider);
423
0
424
0
        m.leftBearing  = NSToCoordFloor( theMetrics.mBoundingBox.X());
425
0
        m.rightBearing = NSToCoordCeil(  theMetrics.mBoundingBox.XMost());
426
0
        m.ascent       = NSToCoordCeil( -theMetrics.mBoundingBox.Y());
427
0
        m.descent      = NSToCoordCeil(  theMetrics.mBoundingBox.YMost());
428
0
        m.width        = NSToCoordRound( theMetrics.mAdvanceWidth);
429
0
    }
430
0
    return m;
431
0
}
432
433
nsBoundingMetrics
434
nsFontMetrics::GetBoundingMetrics(const char16_t *aString, uint32_t aLength,
435
                                  DrawTarget* aDrawTarget)
436
0
{
437
0
  return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
438
0
                                gfxFont::TIGHT_HINTED_OUTLINE_EXTENTS);
439
0
}
440
441
nsBoundingMetrics
442
nsFontMetrics::GetInkBoundsForVisualOverflow(const char16_t *aString, uint32_t aLength,
443
                                             DrawTarget* aDrawTarget)
444
0
{
445
0
  return GetTextBoundingMetrics(this, aString, aLength, aDrawTarget,
446
0
                                gfxFont::LOOSE_INK_EXTENTS);
447
0
}