1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "gfxFT2FontBase.h"
7 #include "gfxFT2Utils.h"
8 #include "harfbuzz/hb.h"
9 #include "mozilla/Likely.h"
10 #include "mozilla/StaticPrefs_gfx.h"
11 #include "gfxFontConstants.h"
12 #include "gfxFontUtils.h"
13 #include "gfxHarfBuzzShaper.h"
17 #include FT_TRUETYPE_TAGS_H
18 #include FT_TRUETYPE_TABLES_H
19 #include FT_ADVANCES_H
20 #include FT_MULTIPLE_MASTERS_H
23 # define FT_LOAD_COLOR (1L << 20)
25 #ifndef FT_FACE_FLAG_COLOR
26 # define FT_FACE_FLAG_COLOR (1L << 14)
29 using namespace mozilla
;
30 using namespace mozilla::gfx
;
32 gfxFT2FontBase::gfxFT2FontBase(
33 const RefPtr
<UnscaledFontFreeType
>& aUnscaledFont
,
34 RefPtr
<mozilla::gfx::SharedFTFace
>&& aFTFace
, gfxFontEntry
* aFontEntry
,
35 const gfxFontStyle
* aFontStyle
, int aLoadFlags
, bool aEmbolden
)
36 : gfxFont(aUnscaledFont
, aFontEntry
, aFontStyle
, kAntialiasDefault
),
37 mFTFace(std::move(aFTFace
)),
38 mFTLoadFlags(aLoadFlags
| FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH
|
43 gfxFT2FontBase::~gfxFT2FontBase() { mFTFace
->ForgetLockOwner(this); }
45 FT_Face
gfxFT2FontBase::LockFTFace() const
46 MOZ_CAPABILITY_ACQUIRE(mFTFace
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
47 if (!mFTFace
->Lock(this)) {
48 FT_Set_Transform(mFTFace
->GetFace(), nullptr, nullptr);
50 FT_F26Dot6 charSize
= NS_lround(mFTSize
* 64.0);
51 FT_Set_Char_Size(mFTFace
->GetFace(), charSize
, charSize
, 0, 0);
53 return mFTFace
->GetFace();
56 void gfxFT2FontBase::UnlockFTFace() const
57 MOZ_CAPABILITY_RELEASE(mFTFace
) MOZ_NO_THREAD_SAFETY_ANALYSIS
{
61 static FT_ULong
GetTableSizeFromFTFace(SharedFTFace
* aFace
,
67 if (FT_Load_Sfnt_Table(aFace
->GetFace(), aTableTag
, 0, nullptr, &len
) != 0) {
73 bool gfxFT2FontEntryBase::FaceHasTable(SharedFTFace
* aFace
,
75 return GetTableSizeFromFTFace(aFace
, aTableTag
) > 0;
78 nsresult
gfxFT2FontEntryBase::CopyFaceTable(SharedFTFace
* aFace
,
80 nsTArray
<uint8_t>& aBuffer
) {
81 FT_ULong length
= GetTableSizeFromFTFace(aFace
, aTableTag
);
83 return NS_ERROR_NOT_AVAILABLE
;
85 if (!aBuffer
.SetLength(length
, fallible
)) {
86 return NS_ERROR_OUT_OF_MEMORY
;
88 if (FT_Load_Sfnt_Table(aFace
->GetFace(), aTableTag
, 0, aBuffer
.Elements(),
91 return NS_ERROR_FAILURE
;
96 uint32_t gfxFT2FontEntryBase::GetGlyph(uint32_t aCharCode
,
97 gfxFT2FontBase
* aFont
) {
98 const uint32_t slotIndex
= aCharCode
% kNumCmapCacheSlots
;
100 // Try to read a cached entry without taking an exclusive lock.
101 AutoReadLock
lock(mLock
);
103 const auto& slot
= mCmapCache
[slotIndex
];
104 if (slot
.mCharCode
== aCharCode
) {
105 return slot
.mGlyphIndex
;
110 // Create/update the charcode-to-glyphid cache.
111 AutoWriteLock
lock(mLock
);
113 // This cache algorithm and size is based on what is done in
114 // cairo_scaled_font_text_to_glyphs and pango_fc_font_real_get_glyph. I
115 // think the concept is that adjacent characters probably come mostly from
116 // one Unicode block. This assumption is probably not so valid with
117 // scripts with large character sets as used for East Asian languages.
119 mCmapCache
= mozilla::MakeUnique
<CmapCacheSlot
[]>(kNumCmapCacheSlots
);
121 // Invalidate slot 0 by setting its char code to something that would
122 // never end up in slot 0. All other slots are already invalid
123 // because they have mCharCode = 0 and a glyph for char code 0 will
124 // always be in the slot 0.
125 mCmapCache
[0].mCharCode
= 1;
128 auto& slot
= mCmapCache
[slotIndex
];
129 if (slot
.mCharCode
!= aCharCode
) {
130 slot
.mCharCode
= aCharCode
;
131 slot
.mGlyphIndex
= gfxFT2LockedFace(aFont
).GetGlyph(aCharCode
);
133 return slot
.mGlyphIndex
;
136 // aScale is intended for a 16.16 x/y_scale of an FT_Size_Metrics
137 static inline FT_Long
ScaleRoundDesignUnits(FT_Short aDesignMetric
,
139 FT_Long fixed26dot6
= FT_MulFix(aDesignMetric
, aScale
);
140 return ROUND_26_6_TO_INT(fixed26dot6
);
143 // Snap a line to pixels while keeping the center and size of the line as
144 // close to the original position as possible.
146 // Pango does similar snapping for underline and strikethrough when fonts are
147 // hinted, but nsCSSRendering::GetTextDecorationRectInternal always snaps the
148 // top and size of lines. Optimizing the distance between the line and
149 // baseline is probably good for the gap between text and underline, but
150 // optimizing the center of the line is better for positioning strikethough.
151 static void SnapLineToPixels(gfxFloat
& aOffset
, gfxFloat
& aSize
) {
152 gfxFloat snappedSize
= std::max(floor(aSize
+ 0.5), 1.0);
153 // Correct offset for change in size
154 gfxFloat offset
= aOffset
- 0.5 * (aSize
- snappedSize
);
156 aOffset
= floor(offset
+ 0.5);
160 static inline gfxRect
ScaleGlyphBounds(const IntRect
& aBounds
,
162 return gfxRect(FLOAT_FROM_26_6(aBounds
.x
) * aScale
,
163 FLOAT_FROM_26_6(aBounds
.y
) * aScale
,
164 FLOAT_FROM_26_6(aBounds
.width
) * aScale
,
165 FLOAT_FROM_26_6(aBounds
.height
) * aScale
);
169 * Get extents for a simple character representable by a single glyph.
170 * The return value is the glyph id of that glyph or zero if no such glyph
171 * exists. aWidth/aBounds is only set when this returns a non-zero glyph id.
172 * This is just for use during initialization, and doesn't use the width cache.
174 uint32_t gfxFT2FontBase::GetCharExtents(uint32_t aChar
, gfxFloat
* aWidth
,
176 FT_UInt gid
= GetGlyph(aChar
);
179 if (gid
&& GetFTGlyphExtents(gid
, aWidth
? &width
: nullptr,
180 aBounds
? &bounds
: nullptr)) {
182 *aWidth
= FLOAT_FROM_16_16(width
);
185 *aBounds
= ScaleGlyphBounds(bounds
, GetAdjustedSize() / mFTSize
);
194 * Find the closest available fixed strike size, if applicable, to the
197 static double FindClosestSize(FT_Face aFace
, double aSize
) {
198 // FT size selection does not actually support sizes smaller than 1 and will
199 // clamp this internally, regardless of what is requested. Do the clamp here
200 // instead so that glyph extents/font matrix scaling will compensate it, as
201 // Cairo normally would.
205 if (FT_IS_SCALABLE(aFace
)) {
208 double bestDist
= DBL_MAX
;
209 FT_Int bestSize
= -1;
210 for (FT_Int i
= 0; i
< aFace
->num_fixed_sizes
; i
++) {
211 double dist
= aFace
->available_sizes
[i
].y_ppem
/ 64.0 - aSize
;
212 // If the previous best is smaller than the desired size, prefer
213 // a bigger size. Otherwise, just choose whatever size is closest.
214 if (bestDist
< 0 ? dist
>= bestDist
: fabs(dist
) <= bestDist
) {
222 return aFace
->available_sizes
[bestSize
].y_ppem
/ 64.0;
225 void gfxFT2FontBase::InitMetrics() {
226 mFUnitsConvFactor
= 0.0;
228 if (MOZ_UNLIKELY(mStyle
.AdjustedSizeMustBeZero())) {
229 memset(&mMetrics
, 0, sizeof(mMetrics
)); // zero initialize
230 mSpaceGlyph
= GetGlyph(' ');
234 if (FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
) !=
235 FontSizeAdjust::Tag::None
&&
236 mStyle
.sizeAdjust
>= 0.0 && GetAdjustedSize() > 0.0 && mFTSize
== 0.0) {
237 // If font-size-adjust is in effect, we need to get metrics in order to
238 // determine the aspect ratio, then compute the final adjusted size and
239 // re-initialize metrics.
240 // Setting mFTSize nonzero here ensures we will not recurse again; the
241 // actual value will be overridden by FindClosestSize below.
244 // Now do the font-size-adjust calculation and set the final size.
246 switch (FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
)) {
248 MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
251 case FontSizeAdjust::Tag::ExHeight
:
252 aspect
= mMetrics
.xHeight
/ mAdjustedSize
;
254 case FontSizeAdjust::Tag::CapHeight
:
255 aspect
= mMetrics
.capHeight
/ mAdjustedSize
;
257 case FontSizeAdjust::Tag::ChWidth
:
259 mMetrics
.zeroWidth
> 0.0 ? mMetrics
.zeroWidth
/ mAdjustedSize
: 0.5;
261 case FontSizeAdjust::Tag::IcWidth
:
262 case FontSizeAdjust::Tag::IcHeight
: {
263 bool vertical
= FontSizeAdjust::Tag(mStyle
.sizeAdjustBasis
) ==
264 FontSizeAdjust::Tag::IcHeight
;
265 gfxFloat advance
= GetCharAdvance(kWaterIdeograph
, vertical
);
266 aspect
= advance
> 0.0 ? advance
/ mAdjustedSize
: 1.0;
271 // If we created a shaper above (to measure glyphs), discard it so we
272 // get a new one for the adjusted scaling.
273 delete mHarfBuzzShaper
.exchange(nullptr);
274 mAdjustedSize
= mStyle
.GetAdjustedSize(aspect
);
275 // Ensure the FT_Face will be reconfigured for the new size next time we
277 mFTFace
->ForgetLockOwner(this);
281 // Set mAdjustedSize if it hasn't already been set by a font-size-adjust
283 mAdjustedSize
= GetAdjustedSize();
285 // Cairo metrics are normalized to em-space, so that whatever fixed size
286 // might actually be chosen is factored out. They are then later scaled by
287 // the font matrix to the target adjusted size. Stash the chosen closest
288 // size here for later scaling of the metrics.
289 mFTSize
= FindClosestSize(mFTFace
->GetFace(), GetAdjustedSize());
291 // Explicitly lock the face so we can release it early before calling
292 // back into Cairo below.
293 FT_Face face
= LockFTFace();
295 if (MOZ_UNLIKELY(!face
)) {
296 // No face. This unfortunate situation might happen if the font
297 // file is (re)moved at the wrong time.
298 const gfxFloat emHeight
= GetAdjustedSize();
299 mMetrics
.emHeight
= emHeight
;
300 mMetrics
.maxAscent
= mMetrics
.emAscent
= 0.8 * emHeight
;
301 mMetrics
.maxDescent
= mMetrics
.emDescent
= 0.2 * emHeight
;
302 mMetrics
.maxHeight
= emHeight
;
303 mMetrics
.internalLeading
= 0.0;
304 mMetrics
.externalLeading
= 0.2 * emHeight
;
305 const gfxFloat spaceWidth
= 0.5 * emHeight
;
306 mMetrics
.spaceWidth
= spaceWidth
;
307 mMetrics
.maxAdvance
= spaceWidth
;
308 mMetrics
.aveCharWidth
= spaceWidth
;
309 mMetrics
.zeroWidth
= spaceWidth
;
310 mMetrics
.ideographicWidth
= emHeight
;
311 const gfxFloat xHeight
= 0.5 * emHeight
;
312 mMetrics
.xHeight
= xHeight
;
313 mMetrics
.capHeight
= mMetrics
.maxAscent
;
314 const gfxFloat underlineSize
= emHeight
/ 14.0;
315 mMetrics
.underlineSize
= underlineSize
;
316 mMetrics
.underlineOffset
= -underlineSize
;
317 mMetrics
.strikeoutOffset
= 0.25 * emHeight
;
318 mMetrics
.strikeoutSize
= underlineSize
;
320 SanitizeMetrics(&mMetrics
, false);
325 const FT_Size_Metrics
& ftMetrics
= face
->size
->metrics
;
327 mMetrics
.maxAscent
= FLOAT_FROM_26_6(ftMetrics
.ascender
);
328 mMetrics
.maxDescent
= -FLOAT_FROM_26_6(ftMetrics
.descender
);
329 mMetrics
.maxAdvance
= FLOAT_FROM_26_6(ftMetrics
.max_advance
);
330 gfxFloat lineHeight
= FLOAT_FROM_26_6(ftMetrics
.height
);
333 // Scale for vertical design metric conversion: pixels per design unit.
334 // If this remains at 0.0, we can't use metrics from OS/2 etc.
335 gfxFloat yScale
= 0.0;
336 if (FT_IS_SCALABLE(face
)) {
337 // Prefer FT_Size_Metrics::x_scale to x_ppem as x_ppem does not
338 // have subpixel accuracy.
340 // FT_Size_Metrics::y_scale is in 16.16 fixed point format. Its
341 // (fractional) value is a factor that converts vertical metrics from
342 // design units to units of 1/64 pixels, so that the result may be
343 // interpreted as pixels in 26.6 fixed point format.
344 mFUnitsConvFactor
= FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics
.x_scale
));
345 yScale
= FLOAT_FROM_26_6(FLOAT_FROM_16_16(ftMetrics
.y_scale
));
346 emHeight
= face
->units_per_EM
* yScale
;
347 } else { // Not scalable.
348 emHeight
= ftMetrics
.y_ppem
;
349 // FT_Face doc says units_per_EM and a bunch of following fields
350 // are "only relevant to scalable outlines". If it's an sfnt,
351 // we can get units_per_EM from the 'head' table instead; otherwise,
352 // we don't have a unitsPerEm value so we can't compute/use yScale or
353 // mFUnitsConvFactor (x scale).
354 const TT_Header
* head
=
355 static_cast<TT_Header
*>(FT_Get_Sfnt_Table(face
, ft_sfnt_head
));
357 // Bug 1267909 - Even if the font is not explicitly scalable,
358 // if the face has color bitmaps, it should be treated as scalable
359 // and scaled to the desired size. Metrics based on y_ppem need
360 // to be rescaled for the adjusted size. This makes metrics agree
361 // with the scales we pass to Cairo for Fontconfig fonts.
362 if (face
->face_flags
& FT_FACE_FLAG_COLOR
) {
363 emHeight
= GetAdjustedSize();
364 gfxFloat adjustScale
= emHeight
/ ftMetrics
.y_ppem
;
365 mMetrics
.maxAscent
*= adjustScale
;
366 mMetrics
.maxDescent
*= adjustScale
;
367 mMetrics
.maxAdvance
*= adjustScale
;
368 lineHeight
*= adjustScale
;
370 gfxFloat emUnit
= head
->Units_Per_EM
;
371 mFUnitsConvFactor
= ftMetrics
.x_ppem
/ emUnit
;
372 yScale
= emHeight
/ emUnit
;
376 TT_OS2
* os2
= static_cast<TT_OS2
*>(FT_Get_Sfnt_Table(face
, ft_sfnt_os2
));
378 if (os2
&& os2
->sTypoAscender
&& yScale
> 0.0) {
379 mMetrics
.emAscent
= os2
->sTypoAscender
* yScale
;
380 mMetrics
.emDescent
= -os2
->sTypoDescender
* yScale
;
381 FT_Short typoHeight
=
382 os2
->sTypoAscender
- os2
->sTypoDescender
+ os2
->sTypoLineGap
;
383 lineHeight
= typoHeight
* yScale
;
385 // If the OS/2 fsSelection USE_TYPO_METRICS bit is set,
386 // set maxAscent/Descent from the sTypo* fields instead of hhea.
387 const uint16_t kUseTypoMetricsMask
= 1 << 7;
388 if ((os2
->fsSelection
& kUseTypoMetricsMask
) ||
389 // maxAscent/maxDescent get used for frame heights, and some fonts
390 // don't have the HHEA table ascent/descent set (bug 279032).
391 (mMetrics
.maxAscent
== 0.0 && mMetrics
.maxDescent
== 0.0)) {
392 // We use NS_round here to parallel the pixel-rounded values that
393 // freetype gives us for ftMetrics.ascender/descender.
394 mMetrics
.maxAscent
= NS_round(mMetrics
.emAscent
);
395 mMetrics
.maxDescent
= NS_round(mMetrics
.emDescent
);
398 mMetrics
.emAscent
= mMetrics
.maxAscent
;
399 mMetrics
.emDescent
= mMetrics
.maxDescent
;
402 // gfxFont::Metrics::underlineOffset is the position of the top of the
405 // FT_FaceRec documentation describes underline_position as "the
406 // center of the underlining stem". This was the original definition
407 // of the PostScript metric, but in the PostScript table of OpenType
408 // fonts the metric is "the top of the underline"
409 // (http://www.microsoft.com/typography/otspec/post.htm), and FreeType
410 // (up to version 2.3.7) doesn't make any adjustment.
412 // Therefore get the underline position directly from the table
413 // ourselves when this table exists. Use FreeType's metrics for
414 // other (including older PostScript) fonts.
415 if (face
->underline_position
&& face
->underline_thickness
&& yScale
> 0.0) {
416 mMetrics
.underlineSize
= face
->underline_thickness
* yScale
;
417 TT_Postscript
* post
=
418 static_cast<TT_Postscript
*>(FT_Get_Sfnt_Table(face
, ft_sfnt_post
));
419 if (post
&& post
->underlinePosition
) {
420 mMetrics
.underlineOffset
= post
->underlinePosition
* yScale
;
422 mMetrics
.underlineOffset
=
423 face
->underline_position
* yScale
+ 0.5 * mMetrics
.underlineSize
;
425 } else { // No underline info.
427 mMetrics
.underlineSize
= emHeight
/ 14.0;
428 mMetrics
.underlineOffset
= -mMetrics
.underlineSize
;
431 if (os2
&& os2
->yStrikeoutSize
&& os2
->yStrikeoutPosition
&& yScale
> 0.0) {
432 mMetrics
.strikeoutSize
= os2
->yStrikeoutSize
* yScale
;
433 mMetrics
.strikeoutOffset
= os2
->yStrikeoutPosition
* yScale
;
434 } else { // No strikeout info.
435 mMetrics
.strikeoutSize
= mMetrics
.underlineSize
;
436 // Use OpenType spec's suggested position for Roman font.
437 mMetrics
.strikeoutOffset
=
438 emHeight
* 409.0 / 2048.0 + 0.5 * mMetrics
.strikeoutSize
;
440 SnapLineToPixels(mMetrics
.strikeoutOffset
, mMetrics
.strikeoutSize
);
442 if (os2
&& os2
->sxHeight
&& yScale
> 0.0) {
443 mMetrics
.xHeight
= os2
->sxHeight
* yScale
;
445 // CSS 2.1, section 4.3.2 Lengths: "In the cases where it is
446 // impossible or impractical to determine the x-height, a value of
447 // 0.5em should be used."
448 mMetrics
.xHeight
= 0.5 * emHeight
;
451 // aveCharWidth is used for the width of text input elements so be
452 // liberal rather than conservative in the estimate.
453 if (os2
&& os2
->xAvgCharWidth
) {
454 // Round to pixels as this is compared with maxAdvance to guess
455 // whether this is a fixed width font.
456 mMetrics
.aveCharWidth
=
457 ScaleRoundDesignUnits(os2
->xAvgCharWidth
, ftMetrics
.x_scale
);
459 mMetrics
.aveCharWidth
= 0.0; // updated below
462 if (os2
&& os2
->sCapHeight
&& yScale
> 0.0) {
463 mMetrics
.capHeight
= os2
->sCapHeight
* yScale
;
465 mMetrics
.capHeight
= mMetrics
.maxAscent
;
468 // Release the face lock to safely load glyphs with GetCharExtents if
469 // necessary without recursively locking.
473 mSpaceGlyph
= GetCharExtents(' ', &width
);
475 mMetrics
.spaceWidth
= width
;
477 mMetrics
.spaceWidth
= mMetrics
.maxAdvance
; // guess
480 if (GetCharExtents('0', &width
)) {
481 mMetrics
.zeroWidth
= width
;
483 mMetrics
.zeroWidth
= -1.0; // indicates not found
486 if (GetCharExtents(kWaterIdeograph
, &width
)) {
487 mMetrics
.ideographicWidth
= width
;
489 mMetrics
.ideographicWidth
= -1.0;
492 // If we didn't get a usable x-height or cap-height above, try measuring
493 // specific glyphs. This can be affected by hinting, leading to erratic
494 // behavior across font sizes and system configuration, so we prefer to
495 // use the metrics directly from the font if possible.
496 // Using glyph bounds for x-height or cap-height may not really be right,
497 // if fonts have fancy swashes etc. For x-height, CSS 2.1 suggests possibly
498 // using the height of an "o", which may be more consistent across fonts,
499 // but then curve-overshoot should also be accounted for.
502 if (mMetrics
.xHeight
== 0.0) {
503 if (GetCharExtents('x', &xWidth
, &xBounds
) && xBounds
.y
< 0.0) {
504 mMetrics
.xHeight
= -xBounds
.y
;
505 mMetrics
.aveCharWidth
= std::max(mMetrics
.aveCharWidth
, xWidth
);
509 if (mMetrics
.capHeight
== 0.0) {
510 if (GetCharExtents('H', nullptr, &xBounds
) && xBounds
.y
< 0.0) {
511 mMetrics
.capHeight
= -xBounds
.y
;
515 mMetrics
.aveCharWidth
= std::max(mMetrics
.aveCharWidth
, mMetrics
.zeroWidth
);
516 if (mMetrics
.aveCharWidth
== 0.0) {
517 mMetrics
.aveCharWidth
= mMetrics
.spaceWidth
;
519 // Apparently hinting can mean that max_advance is not always accurate.
520 mMetrics
.maxAdvance
= std::max(mMetrics
.maxAdvance
, mMetrics
.aveCharWidth
);
522 mMetrics
.maxHeight
= mMetrics
.maxAscent
+ mMetrics
.maxDescent
;
524 // Make the line height an integer number of pixels so that lines will be
525 // equally spaced (rather than just being snapped to pixels, some up and
526 // some down). Layout calculates line height from the emHeight +
527 // internalLeading + externalLeading, but first each of these is rounded
528 // to layout units. To ensure that the result is an integer number of
529 // pixels, round each of the components to pixels.
530 mMetrics
.emHeight
= floor(emHeight
+ 0.5);
532 // maxHeight will normally be an integer, but round anyway in case
533 // FreeType is configured differently.
534 mMetrics
.internalLeading
=
535 floor(mMetrics
.maxHeight
- mMetrics
.emHeight
+ 0.5);
537 // Text input boxes currently don't work well with lineHeight
538 // significantly less than maxHeight (with Verdana, for example).
539 lineHeight
= floor(std::max(lineHeight
, mMetrics
.maxHeight
) + 0.5);
540 mMetrics
.externalLeading
=
541 lineHeight
- mMetrics
.internalLeading
- mMetrics
.emHeight
;
543 // Ensure emAscent + emDescent == emHeight
544 gfxFloat sum
= mMetrics
.emAscent
+ mMetrics
.emDescent
;
546 sum
> 0.0 ? mMetrics
.emAscent
* mMetrics
.emHeight
/ sum
: 0.0;
547 mMetrics
.emDescent
= mMetrics
.emHeight
- mMetrics
.emAscent
;
549 SanitizeMetrics(&mMetrics
, false);
552 // printf("font name: %s %f\n", NS_ConvertUTF16toUTF8(GetName()).get(), GetStyle()->size);
553 // printf ("pango font %s\n", pango_font_description_to_string (pango_font_describe (font)));
555 fprintf (stderr
, "Font: %s\n", GetName().get());
556 fprintf (stderr
, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics
.emHeight
, mMetrics
.emAscent
, mMetrics
.emDescent
);
557 fprintf (stderr
, " maxAscent: %f maxDescent: %f\n", mMetrics
.maxAscent
, mMetrics
.maxDescent
);
558 fprintf (stderr
, " internalLeading: %f externalLeading: %f\n", mMetrics
.externalLeading
, mMetrics
.internalLeading
);
559 fprintf (stderr
, " spaceWidth: %f aveCharWidth: %f xHeight: %f\n", mMetrics
.spaceWidth
, mMetrics
.aveCharWidth
, mMetrics
.xHeight
);
560 fprintf (stderr
, " ideographicWidth: %f\n", mMetrics
.ideographicWidth
);
561 fprintf (stderr
, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics
.underlineOffset
, mMetrics
.underlineSize
, mMetrics
.strikeoutOffset
, mMetrics
.strikeoutSize
);
565 uint32_t gfxFT2FontBase::GetGlyph(uint32_t unicode
,
566 uint32_t variation_selector
) {
567 if (variation_selector
) {
569 gfxFT2LockedFace(this).GetUVSGlyph(unicode
, variation_selector
);
573 unicode
= gfxFontUtils::GetUVSFallback(unicode
, variation_selector
);
575 return GetGlyph(unicode
);
580 return GetGlyph(unicode
);
583 bool gfxFT2FontBase::ShouldRoundXOffset(cairo_t
* aCairo
) const {
584 // Force rounding if outputting to a Cairo context or if requested by pref to
585 // disable subpixel positioning. Otherwise, allow subpixel positioning (no
586 // rounding) if rendering a scalable outline font with anti-aliasing.
587 // Monochrome rendering or some bitmap fonts can become too distorted with
588 // subpixel positioning, so force rounding in those cases. Also be careful not
589 // to use subpixel positioning if the user requests full hinting via
590 // Fontconfig, which we detect by checking that neither hinting was disabled
591 // nor light hinting was requested. Allow pref to force subpixel positioning
592 // on even if full hinting was requested.
595 gfx_text_subpixel_position_force_disabled_AtStartup()) ||
596 aCairo
!= nullptr || !mFTFace
|| !FT_IS_SCALABLE(mFTFace
->GetFace()) ||
597 (mFTLoadFlags
& FT_LOAD_MONOCHROME
) ||
598 !((mFTLoadFlags
& FT_LOAD_NO_HINTING
) ||
599 FT_LOAD_TARGET_MODE(mFTLoadFlags
) == FT_RENDER_MODE_LIGHT
||
602 gfx_text_subpixel_position_force_enabled_AtStartup()));
605 FT_Vector
gfxFT2FontBase::GetEmboldenStrength(FT_Face aFace
) const {
606 FT_Vector strength
= {0, 0};
611 // If it's an outline glyph, we'll be using mozilla_glyphslot_embolden_less
612 // (see gfx/wr/webrender/src/platform/unix/font.rs), so we need to match its
613 // emboldening strength here.
614 if (aFace
->glyph
->format
== FT_GLYPH_FORMAT_OUTLINE
) {
616 FT_MulFix(aFace
->units_per_EM
, aFace
->size
->metrics
.y_scale
) / 48;
617 strength
.y
= strength
.x
;
621 // This is the embolden "strength" used by FT_GlyphSlot_Embolden.
623 FT_MulFix(aFace
->units_per_EM
, aFace
->size
->metrics
.y_scale
) / 24;
624 strength
.y
= strength
.x
;
625 if (aFace
->glyph
->format
== FT_GLYPH_FORMAT_BITMAP
) {
635 bool gfxFT2FontBase::GetFTGlyphExtents(uint16_t aGID
, int32_t* aAdvance
,
637 gfxFT2LockedFace
face(this);
638 MOZ_ASSERT(face
.get());
640 // Failed to get the FT_Face? Give up already.
641 NS_WARNING("failed to get FT_Face!");
645 FT_Int32 flags
= mFTLoadFlags
;
647 flags
|= FT_LOAD_ADVANCE_ONLY
;
650 // Whether to disable subpixel positioning
651 bool roundX
= ShouldRoundXOffset(nullptr);
653 // Workaround for FT_Load_Glyph not setting linearHoriAdvance for SVG glyphs.
654 // See https://gitlab.freedesktop.org/freetype/freetype/-/issues/1156.
656 GetFontEntry()->HasFontTable(TRUETYPE_TAG('S', 'V', 'G', ' '))) {
657 flags
&= ~FT_LOAD_COLOR
;
660 if (Factory::LoadFTGlyph(face
.get(), aGID
, flags
) != FT_Err_Ok
) {
661 // FT_Face was somehow broken/invalid? Don't try to access glyph slot.
662 // This probably shouldn't happen, but does: see bug 1440938.
663 NS_WARNING("failed to load glyph!");
667 // Whether to interpret hinting settings (i.e. not printing)
668 bool hintMetrics
= ShouldHintMetrics();
669 // No hinting disables X and Y hinting. Light disables only X hinting.
670 bool unhintedY
= (mFTLoadFlags
& FT_LOAD_NO_HINTING
) != 0;
672 unhintedY
|| FT_LOAD_TARGET_MODE(mFTLoadFlags
) == FT_RENDER_MODE_LIGHT
;
674 // Normalize out the loaded FT glyph size and then scale to the actually
675 // desired size, in case these two sizes differ.
676 gfxFloat extentsScale
= GetAdjustedSize() / mFTSize
;
678 FT_Vector bold
= GetEmboldenStrength(face
.get());
680 // Due to freetype bug 52683 we MUST use the linearHoriAdvance field when
681 // dealing with a variation font; also use it for scalable fonts when not
682 // applying hinting. Otherwise, prefer hinted width from glyph->advance.x.
685 if (!roundX
|| FT_HAS_MULTIPLE_MASTERS(face
.get())) {
686 advance
= face
.get()->glyph
->linearHoriAdvance
;
688 advance
= face
.get()->glyph
->advance
.x
<< 10; // convert 26.6 to 16.16
691 advance
+= bold
.x
<< 10; // convert 26.6 to 16.16
693 // Hinting was requested, but FT did not apply any hinting to the metrics.
694 // Round the advance here to approximate hinting as Cairo does. This must
695 // happen BEFORE we apply the glyph extents scale, just like FT hinting
697 if (hintMetrics
&& roundX
&& unhintedX
) {
698 advance
= (advance
+ 0x8000) & 0xffff0000u
;
700 *aAdvance
= NS_lround(advance
* extentsScale
);
704 const FT_Glyph_Metrics
& metrics
= face
.get()->glyph
->metrics
;
705 FT_F26Dot6 x
= metrics
.horiBearingX
;
706 FT_F26Dot6 y
= -metrics
.horiBearingY
;
707 FT_F26Dot6 x2
= x
+ metrics
.width
;
708 FT_F26Dot6 y2
= y
+ metrics
.height
;
709 // Synthetic bold moves the glyph top and right boundaries.
713 if (roundX
&& unhintedX
) {
715 x2
= (x2
+ 63) & -64;
719 y2
= (y2
+ 63) & -64;
722 *aBounds
= IntRect(x
, y
, x2
- x
, y2
- y
);
724 // Color fonts may not have reported the right bounds here, if there wasn't
725 // an outline for the nominal glyph ID.
726 // In principle we could use COLRFonts::GetColorGlyphBounds to retrieve the
727 // true bounds of the rendering, but that's more expensive; probably better
728 // to just use the font-wide ascent/descent as a heuristic that will
729 // generally ensure everything gets rendered.
730 if (aBounds
->IsEmpty() &&
731 GetFontEntry()->HasFontTable(TRUETYPE_TAG('C', 'O', 'L', 'R'))) {
732 const auto& fm
= GetMetrics(nsFontMetrics::eHorizontal
);
733 // aBounds is stored as FT_F26Dot6, so scale values from `fm` by 64.
734 aBounds
->y
= int32_t(-NS_round(fm
.maxAscent
* 64.0));
736 int32_t(NS_round((fm
.maxAscent
+ fm
.maxDescent
) * 64.0));
739 int32_t(aAdvance
? *aAdvance
: NS_round(fm
.maxAdvance
* 64.0));
747 * Get the cached glyph metrics for the glyph id if available. Otherwise, query
748 * FreeType for the glyph extents and initialize the glyph metrics.
750 const gfxFT2FontBase::GlyphMetrics
& gfxFT2FontBase::GetCachedGlyphMetrics(
751 uint16_t aGID
, IntRect
* aBounds
) {
753 // Try to read cached metrics without exclusive locking.
754 AutoReadLock
lock(mLock
);
756 if (auto metrics
= mGlyphMetrics
->Lookup(aGID
)) {
757 return metrics
.Data();
762 // We need to create/update the cache.
763 AutoWriteLock
lock(mLock
);
764 if (!mGlyphMetrics
) {
766 mozilla::MakeUnique
<nsTHashMap
<nsUint32HashKey
, GlyphMetrics
>>(128);
769 return mGlyphMetrics
->LookupOrInsertWith(aGID
, [&] {
770 GlyphMetrics metrics
;
772 if (GetFTGlyphExtents(aGID
, &metrics
.mAdvance
, &bounds
)) {
773 metrics
.SetBounds(bounds
);
782 bool gfxFT2FontBase::GetGlyphBounds(uint16_t aGID
, gfxRect
* aBounds
,
785 const GlyphMetrics
& metrics
= GetCachedGlyphMetrics(aGID
, &bounds
);
786 if (!metrics
.HasValidBounds()) {
789 // Check if there are cached bounds and use those if available. Otherwise,
790 // fall back to directly querying the glyph extents.
791 if (metrics
.HasCachedBounds()) {
792 bounds
= metrics
.GetBounds();
793 } else if (bounds
.IsEmpty() && !GetFTGlyphExtents(aGID
, nullptr, &bounds
)) {
796 // The bounds are stored unscaled, so must be scaled to the adjusted size.
797 *aBounds
= ScaleGlyphBounds(bounds
, GetAdjustedSize() / mFTSize
);
801 // For variation fonts, figure out the variation coordinates to be applied
802 // for each axis, in freetype's order (which may not match the order of
803 // axes in mStyle.variationSettings, so we need to search by axis tag).
805 void gfxFT2FontBase::SetupVarCoords(
806 FT_MM_Var
* aMMVar
, const nsTArray
<gfxFontVariation
>& aVariations
,
812 nsTArray
<FT_Fixed
> coords
;
813 for (unsigned i
= 0; i
< aMMVar
->num_axis
; ++i
) {
814 coords
.AppendElement(aMMVar
->axis
[i
].def
);
815 for (const auto& v
: aVariations
) {
816 if (aMMVar
->axis
[i
].tag
== v
.mTag
) {
817 FT_Fixed val
= v
.mValue
* 0x10000;
818 val
= std::min(val
, aMMVar
->axis
[i
].maximum
);
819 val
= std::max(val
, aMMVar
->axis
[i
].minimum
);
826 if (!coords
.IsEmpty()) {
827 #if MOZ_TREE_FREETYPE
828 FT_Set_Var_Design_Coordinates(aFTFace
, coords
.Length(), coords
.Elements());
830 typedef FT_Error (*SetCoordsFunc
)(FT_Face
, FT_UInt
, FT_Fixed
*);
831 static SetCoordsFunc setCoords
;
832 static bool firstTime
= true;
836 (SetCoordsFunc
)dlsym(RTLD_DEFAULT
, "FT_Set_Var_Design_Coordinates");
839 (*setCoords
)(aFTFace
, coords
.Length(), coords
.Elements());