Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/thebes/gfxFT2FontBase.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 "gfxFT2FontBase.h"
7
#include "gfxFT2Utils.h"
8
#include "harfbuzz/hb.h"
9
#include "mozilla/Likely.h"
10
#include "gfxFontConstants.h"
11
#include "gfxFontUtils.h"
12
#include <algorithm>
13
#include <dlfcn.h>
14
15
#include FT_TRUETYPE_TAGS_H
16
#include FT_TRUETYPE_TABLES_H
17
#include FT_ADVANCES_H
18
#include FT_MULTIPLE_MASTERS_H
19
20
#ifndef FT_FACE_FLAG_COLOR
21
#define FT_FACE_FLAG_COLOR ( 1L << 14 )
22
#endif
23
24
using namespace mozilla::gfx;
25
26
gfxFT2FontBase::gfxFT2FontBase(const RefPtr<UnscaledFontFreeType>& aUnscaledFont,
27
                               cairo_scaled_font_t *aScaledFont,
28
                               gfxFontEntry *aFontEntry,
29
                               const gfxFontStyle *aFontStyle)
30
    : gfxFont(aUnscaledFont, aFontEntry, aFontStyle, kAntialiasDefault, aScaledFont)
31
    , mSpaceGlyph(0)
32
0
{
33
0
    mEmbolden = aFontStyle->NeedsSyntheticBold(aFontEntry);
34
0
35
0
    cairo_scaled_font_reference(mScaledFont);
36
0
37
0
    InitMetrics();
38
0
}
39
40
gfxFT2FontBase::~gfxFT2FontBase()
41
0
{
42
0
    cairo_scaled_font_destroy(mScaledFont);
43
0
}
44
45
uint32_t
46
gfxFT2FontBase::GetGlyph(uint32_t aCharCode)
47
0
{
48
0
    // FcFreeTypeCharIndex needs to lock the FT_Face and can end up searching
49
0
    // through all the postscript glyph names in the font.  Therefore use a
50
0
    // lightweight cache, which is stored on the cairo_font_face_t.
51
0
52
0
    cairo_font_face_t *face =
53
0
        cairo_scaled_font_get_font_face(GetCairoScaledFont());
54
0
55
0
    if (cairo_font_face_status(face) != CAIRO_STATUS_SUCCESS)
56
0
        return 0;
57
0
58
0
    // This cache algorithm and size is based on what is done in
59
0
    // cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph.  I
60
0
    // think the concept is that adjacent characters probably come mostly from
61
0
    // one Unicode block.  This assumption is probably not so valid with
62
0
    // scripts with large character sets as used for East Asian languages.
63
0
64
0
    struct CmapCacheSlot {
65
0
        uint32_t mCharCode;
66
0
        uint32_t mGlyphIndex;
67
0
    };
68
0
    const uint32_t kNumSlots = 256;
69
0
    static cairo_user_data_key_t sCmapCacheKey;
70
0
71
0
    CmapCacheSlot *slots = static_cast<CmapCacheSlot*>
72
0
        (cairo_font_face_get_user_data(face, &sCmapCacheKey));
73
0
74
0
    if (!slots) {
75
0
        // cairo's caches can keep some cairo_font_faces alive past our last
76
0
        // destroy, so the destroy function (free) for the cache must be
77
0
        // callable from cairo without any assumptions about what other
78
0
        // modules have not been shutdown.
79
0
        slots = static_cast<CmapCacheSlot*>
80
0
            (calloc(kNumSlots, sizeof(CmapCacheSlot)));
81
0
        if (!slots)
82
0
            return 0;
83
0
84
0
        cairo_status_t status =
85
0
            cairo_font_face_set_user_data(face, &sCmapCacheKey, slots, free);
86
0
        if (status != CAIRO_STATUS_SUCCESS) { // OOM
87
0
            free(slots);
88
0
            return 0;
89
0
        }
90
0
91
0
        // Invalidate slot 0 by setting its char code to something that would
92
0
        // never end up in slot 0.  All other slots are already invalid
93
0
        // because they have mCharCode = 0 and a glyph for char code 0 will
94
0
        // always be in the slot 0.
95
0
        slots[0].mCharCode = 1;
96
0
    }
97
0
98
0
    CmapCacheSlot *slot = &slots[aCharCode % kNumSlots];
99
0
    if (slot->mCharCode != aCharCode) {
100
0
        slot->mCharCode = aCharCode;
101
0
        slot->mGlyphIndex = gfxFT2LockedFace(this).GetGlyph(aCharCode);
102
0
    }
103
0
104
0
    return slot->mGlyphIndex;
105
0
}
106
107
void
108
gfxFT2FontBase::GetGlyphExtents(uint32_t aGlyph, cairo_text_extents_t* aExtents)
109
0
{
110
0
    MOZ_ASSERT(aExtents != nullptr, "aExtents must not be NULL");
111
0
112
0
    cairo_glyph_t glyphs[1];
113
0
    glyphs[0].index = aGlyph;
114
0
    glyphs[0].x = 0.0;
115
0
    glyphs[0].y = 0.0;
116
0
    // cairo does some caching for us here but perhaps a small gain could be
117
0
    // made by caching more.  It is usually only the advance that is needed,
118
0
    // so caching only the advance could allow many requests to be cached with
119
0
    // little memory use.  Ideally this cache would be merged with
120
0
    // gfxGlyphExtents.
121
0
    cairo_scaled_font_glyph_extents(GetCairoScaledFont(), glyphs, 1, aExtents);
122
0
}
123
124
// aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
125
static inline FT_Long
126
ScaleRoundDesignUnits(FT_Short aDesignMetric, FT_Fixed aScale)
127
0
{
128
0
    FT_Long fixed26dot6 = FT_MulFix(aDesignMetric, aScale);
129
0
    return ROUND_26_6_TO_INT(fixed26dot6);
130
0
}
131
132
// Snap a line to pixels while keeping the center and size of the line as
133
// close to the original position as possible.
134
//
135
// Pango does similar snapping for underline and strikethrough when fonts are
136
// hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
137
// top and size of lines.  Optimizing the distance between the line and
138
// baseline is probably good for the gap between text and underline, but
139
// optimizing the center of the line is better for positioning strikethough.
140
static void
141
SnapLineToPixels(gfxFloat& aOffset, gfxFloat& aSize)
142
0
{
143
0
    gfxFloat snappedSize = std::max(floor(aSize + 0.5), 1.0);
144
0
    // Correct offset for change in size
145
0
    gfxFloat offset = aOffset - 0.5 * (aSize - snappedSize);
146
0
    // Snap offset
147
0
    aOffset = floor(offset + 0.5);
148
0
    aSize = snappedSize;
149
0
}
150
151
/**
152
 * Get extents for a simple character representable by a single glyph.
153
 * The return value is the glyph id of that glyph or zero if no such glyph
154
 * exists.  aExtents is only set when this returns a non-zero glyph id.
155
 */
156
uint32_t
157
gfxFT2FontBase::GetCharExtents(char aChar, cairo_text_extents_t* aExtents)
158
0
{
159
0
    FT_UInt gid = GetGlyph(aChar);
160
0
    if (gid) {
161
0
        GetGlyphExtents(gid, aExtents);
162
0
    }
163
0
    return gid;
164
0
}
165
166
/**
167
 * Get glyph id and width for a simple character.
168
 * The return value is the glyph id of that glyph or zero if no such glyph
169
 * exists.  aWidth is only set when this returns a non-zero glyph id.
170
 * This is just for use during initialization, and doesn't use the width cache.
171
 */
172
uint32_t
173
gfxFT2FontBase::GetCharWidth(char aChar, gfxFloat* aWidth)
174
0
{
175
0
    FT_UInt gid = GetGlyph(aChar);
176
0
    if (gid) {
177
0
        int32_t width;
178
0
        if (!GetFTGlyphAdvance(gid, &width)) {
179
0
            cairo_text_extents_t extents;
180
0
            GetGlyphExtents(gid, &extents);
181
0
            width = NS_lround(0x10000 * extents.x_advance);
182
0
        }
183
0
        *aWidth = FLOAT_FROM_16_16(width);
184
0
    }
185
0
    return gid;
186
0
}
187
188
void
189
gfxFT2FontBase::InitMetrics()
190
0
{
191
0
    mFUnitsConvFactor = 0.0;
192
0
193
0
    if (MOZ_UNLIKELY(GetStyle()->size <= 0.0) ||
194
0
        MOZ_UNLIKELY(GetStyle()->sizeAdjust == 0.0)) {
195
0
        memset(&mMetrics, 0, sizeof(mMetrics)); // zero initialize
196
0
        mSpaceGlyph = GetGlyph(' ');
197
0
        return;
198
0
    }
199
0
200
0
    // Explicitly lock the face so we can release it early before calling
201
0
    // back into Cairo below.
202
0
    FT_Face face = cairo_ft_scaled_font_lock_face(GetCairoScaledFont());
203
0
204
0
    if (MOZ_UNLIKELY(!face)) {
205
0
        // No face.  This unfortunate situation might happen if the font
206
0
        // file is (re)moved at the wrong time.
207
0
        const gfxFloat emHeight = GetAdjustedSize();
208
0
        mMetrics.emHeight = emHeight;
209
0
        mMetrics.maxAscent = mMetrics.emAscent = 0.8 * emHeight;
210
0
        mMetrics.maxDescent = mMetrics.emDescent = 0.2 * emHeight;
211
0
        mMetrics.maxHeight = emHeight;
212
0
        mMetrics.internalLeading = 0.0;
213
0
        mMetrics.externalLeading = 0.2 * emHeight;
214
0
        const gfxFloat spaceWidth = 0.5 * emHeight;
215
0
        mMetrics.spaceWidth = spaceWidth;
216
0
        mMetrics.maxAdvance = spaceWidth;
217
0
        mMetrics.aveCharWidth = spaceWidth;
218
0
        mMetrics.zeroOrAveCharWidth = spaceWidth;
219
0
        const gfxFloat xHeight = 0.5 * emHeight;
220
0
        mMetrics.xHeight = xHeight;
221
0
        mMetrics.capHeight = mMetrics.maxAscent;
222
0
        const gfxFloat underlineSize = emHeight / 14.0;
223
0
        mMetrics.underlineSize = underlineSize;
224
0
        mMetrics.underlineOffset = -underlineSize;
225
0
        mMetrics.strikeoutOffset = 0.25 * emHeight;
226
0
        mMetrics.strikeoutSize = underlineSize;
227
0
228
0
        SanitizeMetrics(&mMetrics, false);
229
0
        return;
230
0
    }
231
0
232
0
    if (face->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS) {
233
0
        // Resolve variations from entry (descriptor) and style (property)
234
0
        AutoTArray<gfxFontVariation,8> settings;
235
0
        mFontEntry->GetVariationsForStyle(settings, mStyle);
236
0
        SetupVarCoords(mFontEntry->GetMMVar(), settings, &mCoords);
237
0
        if (!mCoords.IsEmpty()) {
238
#if MOZ_TREE_FREETYPE
239
            FT_Set_Var_Design_Coordinates(face, mCoords.Length(), mCoords.Elements());
240
#else
241
            typedef FT_Error (*SetCoordsFunc)(FT_Face, FT_UInt, FT_Fixed*);
242
0
            static SetCoordsFunc setCoords;
243
0
            static bool firstTime = true;
244
0
            if (firstTime) {
245
0
                firstTime = false;
246
0
                setCoords = (SetCoordsFunc)
247
0
                    dlsym(RTLD_DEFAULT, "FT_Set_Var_Design_Coordinates");
248
0
            }
249
0
            if (setCoords) {
250
0
                (*setCoords)(face, mCoords.Length(), mCoords.Elements());
251
0
            }
252
0
#endif
253
0
        }
254
0
    }
255
0
256
0
    const FT_Size_Metrics& ftMetrics = face->size->metrics;
257
0
258
0
    mMetrics.maxAscent = FLOAT_FROM_26_6(ftMetrics.ascender);
259
0
    mMetrics.maxDescent = -FLOAT_FROM_26_6(ftMetrics.descender);
260
0
    mMetrics.maxAdvance = FLOAT_FROM_26_6(ftMetrics.max_advance);
261
0
    gfxFloat lineHeight = FLOAT_FROM_26_6(ftMetrics.height);
262
0
263
0
    gfxFloat emHeight;
264
0
    // Scale for vertical design metric conversion: pixels per design unit.
265
0
    // If this remains at 0.0, we can't use metrics from OS/2 etc.
266
0
    gfxFloat yScale = 0.0;
267
0
    if (FT_IS_SCALABLE(face)) {
268
0
        // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
269
0
        // have subpixel accuracy.
270
0
        //
271
0
        // FT_Size_Metrics::y_scale is in 16.16 fixed point format.  Its
272
0
        // (fractional) value is a factor that converts vertical metrics from
273
0
        // design units to units of 1/64 pixels, so that the result may be
274
0
        // interpreted as pixels in 26.6 fixed point format.
275
0
        mFUnitsConvFactor = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.x_scale));
276
0
        yScale = FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics.y_scale));
277
0
        emHeight = face->units_per_EM * yScale;
278
0
    } else { // Not scalable.
279
0
        emHeight = ftMetrics.y_ppem;
280
0
        // FT_Face doc says units_per_EM and a bunch of following fields
281
0
        // are "only relevant to scalable outlines". If it's an sfnt,
282
0
        // we can get units_per_EM from the 'head' table instead; otherwise,
283
0
        // we don't have a unitsPerEm value so we can't compute/use yScale or
284
0
        // mFUnitsConvFactor (x scale).
285
0
        const TT_Header* head =
286
0
            static_cast<TT_Header*>(FT_Get_Sfnt_Table(face, ft_sfnt_head));
287
0
        if (head) {
288
0
            // Bug 1267909 - Even if the font is not explicitly scalable,
289
0
            // if the face has color bitmaps, it should be treated as scalable
290
0
            // and scaled to the desired size. Metrics based on y_ppem need
291
0
            // to be rescaled for the adjusted size. This makes metrics agree
292
0
            // with the scales we pass to Cairo for Fontconfig fonts.
293
0
            if (face->face_flags & FT_FACE_FLAG_COLOR) {
294
0
                emHeight = GetAdjustedSize();
295
0
                gfxFloat adjustScale = emHeight / ftMetrics.y_ppem;
296
0
                mMetrics.maxAscent *= adjustScale;
297
0
                mMetrics.maxDescent *= adjustScale;
298
0
                mMetrics.maxAdvance *= adjustScale;
299
0
                lineHeight *= adjustScale;
300
0
            }
301
0
            gfxFloat emUnit = head->Units_Per_EM;
302
0
            mFUnitsConvFactor = ftMetrics.x_ppem / emUnit;
303
0
            yScale = emHeight / emUnit;
304
0
        }
305
0
    }
306
0
307
0
    TT_OS2 *os2 =
308
0
        static_cast<TT_OS2*>(FT_Get_Sfnt_Table(face, ft_sfnt_os2));
309
0
310
0
    if (os2 && os2->sTypoAscender && yScale > 0.0) {
311
0
        mMetrics.emAscent = os2->sTypoAscender * yScale;
312
0
        mMetrics.emDescent = -os2->sTypoDescender * yScale;
313
0
        FT_Short typoHeight =
314
0
            os2->sTypoAscender - os2->sTypoDescender + os2->sTypoLineGap;
315
0
        lineHeight = typoHeight * yScale;
316
0
317
0
        // If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
318
0
        // set maxAscent/Descent from the sTypo* fields instead of hhea.
319
0
        const uint16_t kUseTypoMetricsMask = 1 << 7;
320
0
        if ((os2->fsSelection & kUseTypoMetricsMask) ||
321
0
            // maxAscent/maxDescent get used for frame heights, and some fonts
322
0
            // don't have the HHEA table ascent/descent set (bug 279032).
323
0
      (mMetrics.maxAscent == 0.0 && mMetrics.maxDescent == 0.0)) {
324
0
            // We use NS_round here to parallel the pixel-rounded values that
325
0
            // freetype gives us for ftMetrics.ascender/descender.
326
0
            mMetrics.maxAscent = NS_round(mMetrics.emAscent);
327
0
            mMetrics.maxDescent = NS_round(mMetrics.emDescent);
328
0
        }
329
0
    } else {
330
0
        mMetrics.emAscent = mMetrics.maxAscent;
331
0
        mMetrics.emDescent = mMetrics.maxDescent;
332
0
    }
333
0
334
0
    // gfxFont::Metrics::underlineOffset is the position of the top of the
335
0
    // underline.
336
0
    //
337
0
    // FT_FaceRec documentation describes underline_position as "the
338
0
    // center of the underlining stem".  This was the original definition
339
0
    // of the PostScript metric, but in the PostScript table of OpenType
340
0
    // fonts the metric is "the top of the underline"
341
0
    // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
342
0
    // (up to version 2.3.7) doesn't make any adjustment.
343
0
    //
344
0
    // Therefore get the underline position directly from the table
345
0
    // ourselves when this table exists.  Use FreeType's metrics for
346
0
    // other (including older PostScript) fonts.
347
0
    if (face->underline_position && face->underline_thickness && yScale > 0.0) {
348
0
        mMetrics.underlineSize = face->underline_thickness * yScale;
349
0
        TT_Postscript *post = static_cast<TT_Postscript*>
350
0
            (FT_Get_Sfnt_Table(face, ft_sfnt_post));
351
0
        if (post && post->underlinePosition) {
352
0
            mMetrics.underlineOffset = post->underlinePosition * yScale;
353
0
        } else {
354
0
            mMetrics.underlineOffset = face->underline_position * yScale
355
0
                + 0.5 * mMetrics.underlineSize;
356
0
        }
357
0
    } else { // No underline info.
358
0
        // Imitate Pango.
359
0
        mMetrics.underlineSize = emHeight / 14.0;
360
0
        mMetrics.underlineOffset = -mMetrics.underlineSize;
361
0
    }
362
0
363
0
    if (os2 && os2->yStrikeoutSize && os2->yStrikeoutPosition && yScale > 0.0) {
364
0
        mMetrics.strikeoutSize = os2->yStrikeoutSize * yScale;
365
0
        mMetrics.strikeoutOffset = os2->yStrikeoutPosition * yScale;
366
0
    } else { // No strikeout info.
367
0
        mMetrics.strikeoutSize = mMetrics.underlineSize;
368
0
        // Use OpenType spec's suggested position for Roman font.
369
0
        mMetrics.strikeoutOffset = emHeight * 409.0 / 2048.0
370
0
            + 0.5 * mMetrics.strikeoutSize;
371
0
    }
372
0
    SnapLineToPixels(mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
373
0
374
0
    if (os2 && os2->sxHeight && yScale > 0.0) {
375
0
        mMetrics.xHeight = os2->sxHeight * yScale;
376
0
    } else {
377
0
        // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
378
0
        // impossible or impractical to determine the x-height, a value of
379
0
        // 0.5em should be used."
380
0
        mMetrics.xHeight = 0.5 * emHeight;
381
0
    }
382
0
383
0
    // aveCharWidth is used for the width of text input elements so be
384
0
    // liberal rather than conservative in the estimate.
385
0
    if (os2 && os2->xAvgCharWidth) {
386
0
        // Round to pixels as this is compared with maxAdvance to guess
387
0
        // whether this is a fixed width font.
388
0
        mMetrics.aveCharWidth =
389
0
            ScaleRoundDesignUnits(os2->xAvgCharWidth, ftMetrics.x_scale);
390
0
    } else {
391
0
        mMetrics.aveCharWidth = 0.0; // updated below
392
0
    }
393
0
394
0
    if (os2 && os2->sCapHeight && yScale > 0.0) {
395
0
        mMetrics.capHeight = os2->sCapHeight * yScale;
396
0
    } else {
397
0
        mMetrics.capHeight = mMetrics.maxAscent;
398
0
    }
399
0
400
0
    // Release the face lock to safely load glyphs with GetCharExtents if
401
0
    // necessary without recursively locking.
402
0
    cairo_ft_scaled_font_unlock_face(GetCairoScaledFont());
403
0
404
0
    gfxFloat width;
405
0
    mSpaceGlyph = GetCharWidth(' ', &width);
406
0
    if (mSpaceGlyph) {
407
0
        mMetrics.spaceWidth = width;
408
0
    } else {
409
0
        mMetrics.spaceWidth = mMetrics.maxAdvance; // guess
410
0
    }
411
0
412
0
    if (GetCharWidth('0', &width)) {
413
0
        mMetrics.zeroOrAveCharWidth = width;
414
0
    } else {
415
0
        mMetrics.zeroOrAveCharWidth = 0.0;
416
0
    }
417
0
418
0
    // Prefering a measured x over sxHeight because sxHeight doesn't consider
419
0
    // hinting, but maybe the x extents are not quite right in some fancy
420
0
    // script fonts.  CSS 2.1 suggests possibly using the height of an "o",
421
0
    // which would have a more consistent glyph across fonts.
422
0
    cairo_text_extents_t extents;
423
0
    if (GetCharExtents('x', &extents) && extents.y_bearing < 0.0) {
424
0
        mMetrics.xHeight = -extents.y_bearing;
425
0
        mMetrics.aveCharWidth =
426
0
            std::max(mMetrics.aveCharWidth, extents.x_advance);
427
0
    }
428
0
429
0
    if (GetCharExtents('H', &extents) && extents.y_bearing < 0.0) {
430
0
        mMetrics.capHeight = -extents.y_bearing;
431
0
    }
432
0
433
0
    mMetrics.aveCharWidth =
434
0
        std::max(mMetrics.aveCharWidth, mMetrics.zeroOrAveCharWidth);
435
0
    if (mMetrics.aveCharWidth == 0.0) {
436
0
        mMetrics.aveCharWidth = mMetrics.spaceWidth;
437
0
    }
438
0
    if (mMetrics.zeroOrAveCharWidth == 0.0) {
439
0
        mMetrics.zeroOrAveCharWidth = mMetrics.aveCharWidth;
440
0
    }
441
0
    // Apparently hinting can mean that max_advance is not always accurate.
442
0
    mMetrics.maxAdvance =
443
0
        std::max(mMetrics.maxAdvance, mMetrics.aveCharWidth);
444
0
445
0
    mMetrics.maxHeight = mMetrics.maxAscent + mMetrics.maxDescent;
446
0
447
0
    // Make the line height an integer number of pixels so that lines will be
448
0
    // equally spaced (rather than just being snapped to pixels, some up and
449
0
    // some down).  Layout calculates line height from the emHeight +
450
0
    // internalLeading + externalLeading, but first each of these is rounded
451
0
    // to layout units.  To ensure that the result is an integer number of
452
0
    // pixels, round each of the components to pixels.
453
0
    mMetrics.emHeight = floor(emHeight + 0.5);
454
0
455
0
    // maxHeight will normally be an integer, but round anyway in case
456
0
    // FreeType is configured differently.
457
0
    mMetrics.internalLeading =
458
0
        floor(mMetrics.maxHeight - mMetrics.emHeight + 0.5);
459
0
460
0
    // Text input boxes currently don't work well with lineHeight
461
0
    // significantly less than maxHeight (with Verdana, for example).
462
0
    lineHeight = floor(std::max(lineHeight, mMetrics.maxHeight) + 0.5);
463
0
    mMetrics.externalLeading =
464
0
        lineHeight - mMetrics.internalLeading - mMetrics.emHeight;
465
0
466
0
    // Ensure emAscent + emDescent == emHeight
467
0
    gfxFloat sum = mMetrics.emAscent + mMetrics.emDescent;
468
0
    mMetrics.emAscent = sum > 0.0 ?
469
0
        mMetrics.emAscent * mMetrics.emHeight / sum : 0.0;
470
0
    mMetrics.emDescent = mMetrics.emHeight - mMetrics.emAscent;
471
0
472
0
    SanitizeMetrics(&mMetrics, false);
473
0
474
#if 0
475
    //    printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
476
    //    printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
477
478
    fprintf (stderr, "Font: %s\n", NS_ConvertUTF16toUTF8(GetName()).get());
479
    fprintf (stderr, "    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
480
    fprintf (stderr, "    maxAscent: %f maxDescent: %f\n", mMetrics.maxAscent, mMetrics.maxDescent);
481
    fprintf (stderr, "    internalLeading: %f externalLeading: %f\n", mMetrics.externalLeading, mMetrics.internalLeading);
482
    fprintf (stderr, "    spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight);
483
    fprintf (stderr, "    uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
484
#endif
485
}
486
487
const gfxFont::Metrics&
488
gfxFT2FontBase::GetHorizontalMetrics()
489
0
{
490
0
    return mMetrics;
491
0
}
492
493
// Get the glyphID of a space
494
uint32_t
495
gfxFT2FontBase::GetSpaceGlyph()
496
0
{
497
0
    return mSpaceGlyph;
498
0
}
499
500
uint32_t
501
gfxFT2FontBase::GetGlyph(uint32_t unicode, uint32_t variation_selector)
502
0
{
503
0
    if (variation_selector) {
504
0
        uint32_t id =
505
0
            gfxFT2LockedFace(this).GetUVSGlyph(unicode, variation_selector);
506
0
        if (id) {
507
0
            return id;
508
0
        }
509
0
        unicode = gfxFontUtils::GetUVSFallback(unicode, variation_selector);
510
0
        if (unicode) {
511
0
            return GetGlyph(unicode);
512
0
        }
513
0
        return 0;
514
0
    }
515
0
516
0
    return GetGlyph(unicode);
517
0
}
518
519
bool
520
gfxFT2FontBase::GetFTGlyphAdvance(uint16_t aGID, int32_t* aAdvance)
521
0
{
522
0
    gfxFT2LockedFace face(this);
523
0
    MOZ_ASSERT(face.get());
524
0
    if (!face.get()) {
525
0
        // Failed to get the FT_Face? Give up already.
526
0
        NS_WARNING("failed to get FT_Face!");
527
0
        return false;
528
0
    }
529
0
530
0
    // Due to bugs like 1435234 and 1440938, we currently prefer to fall back
531
0
    // to reading the advance from cairo extents, unless we're dealing with
532
0
    // a variation font (for which cairo metrics may be wrong, due to FreeType
533
0
    // bug 52683).
534
0
    if (!(face.get()->face_flags & FT_FACE_FLAG_SCALABLE) ||
535
0
        !(face.get()->face_flags & FT_FACE_FLAG_MULTIPLE_MASTERS)) {
536
0
        return false;
537
0
    }
538
0
539
0
    bool hinting = gfxPlatform::GetPlatform()->FontHintingEnabled();
540
0
    int32_t flags =
541
0
        hinting ? FT_LOAD_ADVANCE_ONLY
542
0
                : FT_LOAD_ADVANCE_ONLY | FT_LOAD_NO_AUTOHINT | FT_LOAD_NO_HINTING;
543
0
    FT_Error ftError = Factory::LoadFTGlyph(face.get(), aGID, flags);
544
0
    if (ftError != FT_Err_Ok) {
545
0
        // FT_Face was somehow broken/invalid? Don't try to access glyph slot.
546
0
        // This probably shouldn't happen, but does: see bug 1440938.
547
0
        NS_WARNING("failed to load glyph!");
548
0
        return false;
549
0
    }
550
0
551
0
    // Due to freetype bug 52683 we MUST use the linearHoriAdvance field when
552
0
    // dealing with a variation font. (And other fonts would have returned
553
0
    // earlier, so only variation fonts currently reach here.)
554
0
    FT_Fixed advance = face.get()->glyph->linearHoriAdvance;
555
0
556
0
    // If freetype emboldening is being used, and it's not a zero-width glyph,
557
0
    // adjust the advance to account for the increased width.
558
0
    if (mEmbolden && advance > 0) {
559
0
        // This is the embolden "strength" used by FT_GlyphSlot_Embolden,
560
0
        // converted from 26.6 to 16.16
561
0
        FT_Fixed strength = 1024 *
562
0
            FT_MulFix(face.get()->units_per_EM,
563
0
                      face.get()->size->metrics.y_scale) / 24;
564
0
        advance += strength;
565
0
    }
566
0
567
0
    // Round the 16.16 fixed-point value to whole pixels for better consistency
568
0
    // with how cairo renders the glyphs.
569
0
    *aAdvance = (advance + 0x8000) & 0xffff0000u;
570
0
571
0
    return true;
572
0
}
573
574
int32_t
575
gfxFT2FontBase::GetGlyphWidth(DrawTarget& aDrawTarget, uint16_t aGID)
576
0
{
577
0
    if (!mGlyphWidths) {
578
0
        mGlyphWidths =
579
0
            mozilla::MakeUnique<nsDataHashtable<nsUint32HashKey,int32_t>>(128);
580
0
    }
581
0
582
0
    int32_t width;
583
0
    if (mGlyphWidths->Get(aGID, &width)) {
584
0
        return width;
585
0
    }
586
0
587
0
    if (!GetFTGlyphAdvance(aGID, &width)) {
588
0
        cairo_text_extents_t extents;
589
0
        GetGlyphExtents(aGID, &extents);
590
0
        width = NS_lround(0x10000 * extents.x_advance);
591
0
    }
592
0
    mGlyphWidths->Put(aGID, width);
593
0
594
0
    return width;
595
0
}
596
597
bool
598
gfxFT2FontBase::SetupCairoFont(DrawTarget* aDrawTarget)
599
0
{
600
0
    // The scaled font ctm is not relevant right here because
601
0
    // cairo_set_scaled_font does not record the scaled font itself, but
602
0
    // merely the font_face, font_matrix, font_options.  The scaled_font used
603
0
    // for the target can be different from the scaled_font passed to
604
0
    // cairo_set_scaled_font.  (Unfortunately we have measured only for an
605
0
    // identity ctm.)
606
0
    cairo_scaled_font_t *cairoFont = GetCairoScaledFont();
607
0
608
0
    if (cairo_scaled_font_status(cairoFont) != CAIRO_STATUS_SUCCESS) {
609
0
        // Don't cairo_set_scaled_font as that would propagate the error to
610
0
        // the cairo_t, precluding any further drawing.
611
0
        return false;
612
0
    }
613
0
    // Thoughts on which font_options to set on the context:
614
0
    //
615
0
    // cairoFont has been created for screen rendering.
616
0
    //
617
0
    // When the context is being used for screen rendering, we should set
618
0
    // font_options such that the same scaled_font gets used (when the ctm is
619
0
    // the same).  The use of explicit font_options recorded in
620
0
    // CreateScaledFont ensures that this will happen.
621
0
    //
622
0
    // XXXkt: For pdf and ps surfaces, I don't know whether it's better to
623
0
    // remove surface-specific options, or try to draw with the same
624
0
    // scaled_font that was used to measure.  As the same font_face is being
625
0
    // used, its font_options will often override some values anyway (unless
626
0
    // perhaps we remove those from the FcPattern at face creation).
627
0
    //
628
0
    // I can't see any significant difference in printing, irrespective of
629
0
    // what is set here.  It's too late to change things here as measuring has
630
0
    // already taken place.  We should really be measuring with a different
631
0
    // font for pdf and ps surfaces (bug 403513).
632
0
    cairo_set_scaled_font(gfxFont::RefCairo(aDrawTarget), cairoFont);
633
0
    return true;
634
0
}
635
636
// For variation fonts, figure out the variation coordinates to be applied
637
// for each axis, in freetype's order (which may not match the order of
638
// axes in mStyle.variationSettings, so we need to search by axis tag).
639
/*static*/
640
void
641
gfxFT2FontBase::SetupVarCoords(FT_MM_Var* aMMVar,
642
                               const nsTArray<gfxFontVariation>& aVariations,
643
                               nsTArray<FT_Fixed>* aCoords)
644
0
{
645
0
    aCoords->TruncateLength(0);
646
0
    if (!aMMVar) {
647
0
        return;
648
0
    }
649
0
650
0
    for (unsigned i = 0; i < aMMVar->num_axis; ++i) {
651
0
        aCoords->AppendElement(aMMVar->axis[i].def);
652
0
        for (const auto& v : aVariations) {
653
0
            if (aMMVar->axis[i].tag == v.mTag) {
654
0
                FT_Fixed val = v.mValue * 0x10000;
655
0
                val = std::min(val, aMMVar->axis[i].maximum);
656
0
                val = std::max(val, aMMVar->axis[i].minimum);
657
0
                (*aCoords)[i] = val;
658
0
                break;
659
0
            }
660
0
        }
661
0
    }
662
0
}