Bug 1856663 - Add more chunks for Android mochitest-plain. r=jmaher,taskgraph-reviewe...
[gecko.git] / gfx / thebes / gfxMacFont.cpp
blob2e57f410c362c435d4414be7d7af6dfe9bfbea71
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 "gfxMacFont.h"
8 #include "mozilla/MemoryReporting.h"
9 #include "mozilla/Sprintf.h"
10 #include "mozilla/StaticPrefs_gfx.h"
12 #include <algorithm>
14 #include "CoreTextFontList.h"
15 #include "gfxCoreTextShaper.h"
16 #include "gfxPlatformMac.h"
17 #include "gfxContext.h"
18 #include "gfxFontUtils.h"
19 #include "gfxHarfBuzzShaper.h"
20 #include "gfxFontConstants.h"
21 #include "gfxTextRun.h"
22 #include "gfxUtils.h"
23 #include "AppleUtils.h"
24 #include "cairo-quartz.h"
26 using namespace mozilla;
27 using namespace mozilla::gfx;
29 template <class T>
30 struct TagEquals {
31 bool Equals(const T& aIter, uint32_t aTag) const {
32 return aIter.mTag == aTag;
36 gfxMacFont::gfxMacFont(const RefPtr<UnscaledFontMac>& aUnscaledFont,
37 CTFontEntry* aFontEntry, const gfxFontStyle* aFontStyle)
38 : gfxFont(aUnscaledFont, aFontEntry, aFontStyle),
39 mCGFont(nullptr),
40 mCTFont(nullptr),
41 mFontSmoothingBackgroundColor(aFontStyle->fontSmoothingBackgroundColor),
42 mVariationFont(aFontEntry->HasVariations()) {
43 mApplySyntheticBold = aFontStyle->NeedsSyntheticBold(aFontEntry);
45 if (mVariationFont) {
46 CGFontRef baseFont = aUnscaledFont->GetFont();
47 if (!baseFont) {
48 mIsValid = false;
49 return;
52 // Get the variation settings needed to instantiate the fontEntry
53 // for a particular fontStyle.
54 AutoTArray<gfxFontVariation, 4> vars;
55 aFontEntry->GetVariationsForStyle(vars, *aFontStyle);
57 if (aFontEntry->HasOpticalSize()) {
58 // Because of a Core Text bug, we need to ensure that if the font has
59 // an 'opsz' axis, it is always explicitly set, and NOT to the font's
60 // default value. (See bug 1457417, bug 1478720.)
61 // We record the result of searching the font's axes in the font entry,
62 // so that this only has to be done by the first instance created for
63 // a given font resource.
64 const uint32_t kOpszTag = HB_TAG('o', 'p', 's', 'z');
65 const float kOpszFudgeAmount = 0.01f;
67 // Record the opsz axis details in the font entry, if not already done.
68 if (!aFontEntry->mOpszAxis.mTag) {
69 AutoTArray<gfxFontVariationAxis, 4> axes;
70 aFontEntry->GetVariationAxes(axes);
71 auto index =
72 axes.IndexOf(kOpszTag, 0, TagEquals<gfxFontVariationAxis>());
73 MOZ_ASSERT(index != axes.NoIndex);
74 if (index != axes.NoIndex) {
75 const auto& axis = axes[index];
76 aFontEntry->mOpszAxis = axis;
77 // Pick a slightly-adjusted version of the default that we'll
78 // use to work around Core Text's habit of ignoring any attempt
79 // to explicitly set the default value.
80 aFontEntry->mAdjustedDefaultOpsz =
81 axis.mDefaultValue == axis.mMinValue
82 ? axis.mDefaultValue + kOpszFudgeAmount
83 : axis.mDefaultValue - kOpszFudgeAmount;
87 // Add 'opsz' if not present, or tweak its value if it looks too close
88 // to the default (after clamping to the font's available range).
89 auto index = vars.IndexOf(kOpszTag, 0, TagEquals<gfxFontVariation>());
90 if (index == vars.NoIndex) {
91 // No explicit opsz; set to the font's default.
92 vars.AppendElement(
93 gfxFontVariation{kOpszTag, aFontEntry->mAdjustedDefaultOpsz});
94 } else {
95 // An 'opsz' value was already present; use it, but adjust if necessary
96 // to a "safe" value that Core Text won't ignore.
97 auto& value = vars[index].mValue;
98 auto& axis = aFontEntry->mOpszAxis;
99 value = fmin(fmax(value, axis.mMinValue), axis.mMaxValue);
100 if (std::abs(value - axis.mDefaultValue) < kOpszFudgeAmount) {
101 value = aFontEntry->mAdjustedDefaultOpsz;
106 mCGFont = UnscaledFontMac::CreateCGFontWithVariations(
107 baseFont, aUnscaledFont->CGAxesCache(), aUnscaledFont->CTAxesCache(),
108 vars.Length(), vars.Elements());
109 if (!mCGFont) {
110 ::CFRetain(baseFont);
111 mCGFont = baseFont;
113 } else {
114 mCGFont = aUnscaledFont->GetFont();
115 if (!mCGFont) {
116 mIsValid = false;
117 return;
119 ::CFRetain(mCGFont);
122 // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
123 InitMetrics();
124 if (!mIsValid) {
125 return;
128 // turn off font anti-aliasing based on user pref setting
129 if (mAdjustedSize <=
130 (gfxFloat)gfxPlatformMac::GetPlatform()->GetAntiAliasingThreshold()) {
131 mAntialiasOption = kAntialiasNone;
132 } else if (mStyle.useGrayscaleAntialiasing) {
133 mAntialiasOption = kAntialiasGrayscale;
137 gfxMacFont::~gfxMacFont() {
138 if (mCGFont) {
139 ::CFRelease(mCGFont);
141 if (mCTFont) {
142 ::CFRelease(mCTFont);
146 bool gfxMacFont::ShapeText(DrawTarget* aDrawTarget, const char16_t* aText,
147 uint32_t aOffset, uint32_t aLength, Script aScript,
148 nsAtom* aLanguage, bool aVertical,
149 RoundingFlags aRounding,
150 gfxShapedText* aShapedText) {
151 if (!mIsValid) {
152 NS_WARNING("invalid font! expect incorrect text rendering");
153 return false;
156 // Currently, we don't support vertical shaping via CoreText,
157 // so we ignore RequiresAATLayout if vertical is requested.
158 auto ctFontEntry = static_cast<CTFontEntry*>(GetFontEntry());
159 if (ctFontEntry->RequiresAATLayout() && !aVertical &&
160 StaticPrefs::gfx_font_rendering_coretext_enabled()) {
161 if (!mCoreTextShaper) {
162 mCoreTextShaper = MakeUnique<gfxCoreTextShaper>(this);
164 if (mCoreTextShaper->ShapeText(aDrawTarget, aText, aOffset, aLength,
165 aScript, aLanguage, aVertical, aRounding,
166 aShapedText)) {
167 PostShapingFixup(aDrawTarget, aText, aOffset, aLength, aVertical,
168 aShapedText);
169 if (ctFontEntry->HasTrackingTable()) {
170 // Convert font size from device pixels back to CSS px
171 // to use in selecting tracking value
172 float trackSize = GetAdjustedSize() *
173 aShapedText->GetAppUnitsPerDevUnit() /
174 AppUnitsPerCSSPixel();
175 float tracking =
176 ctFontEntry->TrackingForCSSPx(trackSize) * mFUnitsConvFactor;
177 // Applying tracking is a lot like the adjustment we do for
178 // synthetic bold: we want to apply between clusters, not to
179 // non-spacing glyphs within a cluster. So we can reuse that
180 // helper here.
181 aShapedText->AdjustAdvancesForSyntheticBold(tracking, aOffset, aLength);
183 return true;
187 return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
188 aLanguage, aVertical, aRounding, aShapedText);
191 gfxFont::RunMetrics gfxMacFont::Measure(const gfxTextRun* aTextRun,
192 uint32_t aStart, uint32_t aEnd,
193 BoundingBoxType aBoundingBoxType,
194 DrawTarget* aRefDrawTarget,
195 Spacing* aSpacing,
196 gfx::ShapedTextFlags aOrientation) {
197 gfxFont::RunMetrics metrics =
198 gfxFont::Measure(aTextRun, aStart, aEnd, aBoundingBoxType, aRefDrawTarget,
199 aSpacing, aOrientation);
201 // if aBoundingBoxType is not TIGHT_HINTED_OUTLINE_EXTENTS then we need to add
202 // a pixel column each side of the bounding box in case of antialiasing
203 // "bleed"
204 if (aBoundingBoxType != TIGHT_HINTED_OUTLINE_EXTENTS &&
205 metrics.mBoundingBox.width > 0) {
206 metrics.mBoundingBox.x -= aTextRun->GetAppUnitsPerDevUnit();
207 metrics.mBoundingBox.width += aTextRun->GetAppUnitsPerDevUnit() * 2;
210 return metrics;
213 void gfxMacFont::InitMetrics() {
214 mIsValid = false;
215 ::memset(&mMetrics, 0, sizeof(mMetrics));
217 uint32_t upem = 0;
219 // try to get unitsPerEm from sfnt head table, to avoid calling CGFont
220 // if possible (bug 574368) and because CGFontGetUnitsPerEm does not
221 // return the true value for OpenType/CFF fonts (it normalizes to 1000,
222 // which then leads to metrics errors when we read the 'hmtx' table to
223 // get glyph advances for HarfBuzz, see bug 580863)
224 AutoCFRelease<CFDataRef> headData =
225 ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('h', 'e', 'a', 'd'));
226 if (headData) {
227 if (size_t(::CFDataGetLength(headData)) >= sizeof(HeadTable)) {
228 const HeadTable* head =
229 reinterpret_cast<const HeadTable*>(::CFDataGetBytePtr(headData));
230 upem = head->unitsPerEm;
233 if (!upem) {
234 upem = ::CGFontGetUnitsPerEm(mCGFont);
237 if (upem < 16 || upem > 16384) {
238 // See http://www.microsoft.com/typography/otspec/head.htm
239 #ifdef DEBUG
240 char warnBuf[1024];
241 SprintfLiteral(warnBuf,
242 "Bad font metrics for: %s (invalid unitsPerEm value)",
243 mFontEntry->Name().get());
244 NS_WARNING(warnBuf);
245 #endif
246 return;
249 // Apply any size-adjust from the font enty to the given size; this may be
250 // re-adjusted below if font-size-adjust is in effect.
251 mAdjustedSize = GetAdjustedSize();
252 mFUnitsConvFactor = mAdjustedSize / upem;
254 // For CFF fonts, when scaling values read from CGFont* APIs, we need to
255 // use CG's idea of unitsPerEm, which may differ from the "true" value in
256 // the head table of the font (see bug 580863)
257 gfxFloat cgConvFactor;
258 if (static_cast<CTFontEntry*>(mFontEntry.get())->IsCFF()) {
259 cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
260 } else {
261 cgConvFactor = mFUnitsConvFactor;
264 // Try to read 'sfnt' metrics; for local, non-sfnt fonts ONLY, fall back to
265 // platform APIs. The InitMetrics...() functions will set mIsValid on success.
266 if (!InitMetricsFromSfntTables(mMetrics) &&
267 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
268 InitMetricsFromPlatform();
270 if (!mIsValid) {
271 return;
274 if (mMetrics.xHeight == 0.0) {
275 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
277 if (mMetrics.capHeight == 0.0) {
278 mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
281 AutoCFRelease<CFDataRef> cmap =
282 ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c', 'm', 'a', 'p'));
284 uint32_t glyphID;
285 mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
286 if (glyphID == 0) {
287 mMetrics.zeroWidth = -1.0; // indicates not found
290 if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
291 FontSizeAdjust::Tag::None &&
292 mStyle.sizeAdjust >= 0.0 && GetAdjustedSize() > 0.0) {
293 // apply font-size-adjust, and recalculate metrics
294 gfxFloat aspect;
295 switch (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis)) {
296 default:
297 MOZ_ASSERT_UNREACHABLE("unhandled sizeAdjustBasis?");
298 aspect = 0.0;
299 break;
300 case FontSizeAdjust::Tag::ExHeight:
301 aspect = mMetrics.xHeight / mAdjustedSize;
302 break;
303 case FontSizeAdjust::Tag::CapHeight:
304 aspect = mMetrics.capHeight / mAdjustedSize;
305 break;
306 case FontSizeAdjust::Tag::ChWidth:
307 aspect =
308 mMetrics.zeroWidth < 0.0 ? 0.5 : mMetrics.zeroWidth / mAdjustedSize;
309 break;
310 case FontSizeAdjust::Tag::IcWidth:
311 case FontSizeAdjust::Tag::IcHeight: {
312 bool vertical = FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) ==
313 FontSizeAdjust::Tag::IcHeight;
314 gfxFloat advance = GetCharAdvance(kWaterIdeograph, vertical);
315 aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0;
316 break;
319 if (aspect > 0.0) {
320 // If we created a shaper above (to measure glyphs), discard it so we
321 // get a new one for the adjusted scaling.
322 delete mHarfBuzzShaper.exchange(nullptr);
323 mAdjustedSize = mStyle.GetAdjustedSize(aspect);
324 mFUnitsConvFactor = mAdjustedSize / upem;
325 if (static_cast<CTFontEntry*>(mFontEntry.get())->IsCFF()) {
326 cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
327 } else {
328 cgConvFactor = mFUnitsConvFactor;
330 mMetrics.xHeight = 0.0;
331 if (!InitMetricsFromSfntTables(mMetrics) &&
332 (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
333 InitMetricsFromPlatform();
335 if (!mIsValid) {
336 // this shouldn't happen, as we succeeded earlier before applying
337 // the size-adjust factor! But check anyway, for paranoia's sake.
338 return;
340 // Update metrics from the re-scaled font.
341 if (mMetrics.xHeight == 0.0) {
342 mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
344 if (mMetrics.capHeight == 0.0) {
345 mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
347 mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
348 if (glyphID == 0) {
349 mMetrics.zeroWidth = -1.0; // indicates not found
354 // Once we reach here, we've got basic metrics and set mIsValid = TRUE;
355 // there should be no further points of actual failure in InitMetrics().
356 // (If one is introduced, be sure to reset mIsValid to FALSE!)
358 mMetrics.emHeight = mAdjustedSize;
360 // Measure/calculate additional metrics, independent of whether we used
361 // the tables directly or ATS metrics APIs
363 if (mMetrics.aveCharWidth <= 0) {
364 mMetrics.aveCharWidth = GetCharWidth(cmap, 'x', &glyphID, cgConvFactor);
365 if (glyphID == 0) {
366 // we didn't find 'x', so use maxAdvance rather than zero
367 mMetrics.aveCharWidth = mMetrics.maxAdvance;
371 mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor);
372 if (glyphID == 0) {
373 // no space glyph?!
374 mMetrics.spaceWidth = mMetrics.aveCharWidth;
376 mSpaceGlyph = glyphID;
378 mMetrics.ideographicWidth =
379 GetCharWidth(cmap, kWaterIdeograph, &glyphID, cgConvFactor);
380 if (glyphID == 0) {
381 // Indicate "not found".
382 mMetrics.ideographicWidth = -1.0;
385 CalculateDerivedMetrics(mMetrics);
387 SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
389 if (ApplySyntheticBold()) {
390 auto delta = GetSyntheticBoldOffset();
391 mMetrics.spaceWidth += delta;
392 mMetrics.aveCharWidth += delta;
393 mMetrics.maxAdvance += delta;
394 if (mMetrics.zeroWidth > 0) {
395 mMetrics.zeroWidth += delta;
397 if (mMetrics.ideographicWidth > 0) {
398 mMetrics.ideographicWidth += delta;
402 #if 0
403 fprintf (stderr, "Font: %p (%s) size: %f\n", this,
404 NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
405 // fprintf (stderr, " fbounds.origin.x %f y %f size.width %f height %f\n", fbounds.origin.x, fbounds.origin.y, fbounds.size.width, fbounds.size.height);
406 fprintf (stderr, " emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
407 fprintf (stderr, " maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
408 fprintf (stderr, " internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
409 fprintf (stderr, " spaceWidth: %f aveCharWidth: %f xHeight: %f capHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight, mMetrics.capHeight);
410 fprintf (stderr, " uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
411 #endif
414 gfxFloat gfxMacFont::GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
415 uint32_t* aGlyphID, gfxFloat aConvFactor) {
416 CGGlyph glyph = 0;
418 if (aCmap) {
419 glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
420 ::CFDataGetLength(aCmap), aUniChar);
423 if (aGlyphID) {
424 *aGlyphID = glyph;
427 if (glyph) {
428 int advance;
429 if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
430 return advance * aConvFactor;
434 return 0;
437 /* static */
438 CTFontRef gfxMacFont::CreateCTFontFromCGFontWithVariations(
439 CGFontRef aCGFont, CGFloat aSize, bool aInstalledFont,
440 CTFontDescriptorRef aFontDesc) {
441 // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
442 // versions (see bug 1331683).
444 // And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
445 // variation settings from the CGFont to CTFont, so we don't need to do
446 // the extra work here -- and this seems to avoid Core Text crashiness
447 // seen in bug 1454094.
449 // However, for installed fonts it seems we DO need to copy the variations
450 // explicitly even on 10.13, otherwise fonts fail to render (as in bug
451 // 1455494) when non-default values are used. Fortunately, the crash
452 // mentioned above occurs with data fonts, not (AFAICT) with system-
453 // installed fonts.
455 // So we only need to do this "the hard way" on Sierra, and on HighSierra
456 // for system-installed fonts; in other cases just let the standard CTFont
457 // function do its thing.
459 // NOTE in case this ever needs further adjustment: there is similar logic
460 // in four places in the tree (sadly):
461 // CreateCTFontFromCGFontWithVariations in gfxMacFont.cpp
462 // CreateCTFontFromCGFontWithVariations in ScaledFontMac.cpp
463 // CreateCTFontFromCGFontWithVariations in cairo-quartz-font.c
464 // ctfont_create_exact_copy in SkFontHost_mac.cpp
466 CTFontRef ctFont;
467 if (aInstalledFont) {
468 AutoCFRelease<CFDictionaryRef> variations = ::CGFontCopyVariations(aCGFont);
469 if (variations) {
470 AutoCFRelease<CFDictionaryRef> varAttr = ::CFDictionaryCreate(
471 nullptr, (const void**)&kCTFontVariationAttribute,
472 (const void**)&variations, 1, &kCFTypeDictionaryKeyCallBacks,
473 &kCFTypeDictionaryValueCallBacks);
475 AutoCFRelease<CTFontDescriptorRef> varDesc =
476 aFontDesc
477 ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr)
478 : ::CTFontDescriptorCreateWithAttributes(varAttr);
480 ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc);
481 } else {
482 ctFont =
483 ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
485 } else {
486 ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
489 return ctFont;
492 int32_t gfxMacFont::GetGlyphWidth(uint16_t aGID) {
493 if (mVariationFont) {
494 // Avoid a potential Core Text crash (bug 1450209) by using
495 // CoreGraphics glyph advance API. This is inferior for 'sbix'
496 // fonts, but those won't have variations, so it's OK.
497 int cgAdvance;
498 if (::CGFontGetGlyphAdvances(mCGFont, &aGID, 1, &cgAdvance)) {
499 return cgAdvance * mFUnitsConvFactor * 0x10000;
503 if (!mCTFont) {
504 bool isInstalledFont =
505 !mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont();
506 mCTFont = CreateCTFontFromCGFontWithVariations(mCGFont, mAdjustedSize,
507 isInstalledFont);
508 if (!mCTFont) { // shouldn't happen, but let's be safe
509 NS_WARNING("failed to create CTFontRef to measure glyph width");
510 return 0;
514 CGSize advance;
515 ::CTFontGetAdvancesForGlyphs(mCTFont, kCTFontOrientationDefault, &aGID,
516 &advance, 1);
517 return advance.width * 0x10000;
520 bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
521 CGRect bb;
522 if (!::CGFontGetGlyphBBoxes(mCGFont, &aGID, 1, &bb)) {
523 return false;
526 // broken fonts can return incorrect bounds for some null characters,
527 // see https://bugzilla.mozilla.org/show_bug.cgi?id=534260
528 if (bb.origin.x == -32767 && bb.origin.y == -32767 &&
529 bb.size.width == 65534 && bb.size.height == 65534) {
530 *aBounds = gfxRect(0, 0, 0, 0);
531 return true;
534 gfxRect bounds(bb.origin.x, -(bb.origin.y + bb.size.height), bb.size.width,
535 bb.size.height);
536 bounds.Scale(mFUnitsConvFactor);
537 *aBounds = bounds;
538 return true;
541 // Try to initialize font metrics via platform APIs (CG/CT),
542 // and set mIsValid = TRUE on success.
543 // We ONLY call this for local (platform) fonts that are not sfnt format;
544 // for sfnts, including ALL downloadable fonts, we prefer to use
545 // InitMetricsFromSfntTables and avoid platform APIs.
546 void gfxMacFont::InitMetricsFromPlatform() {
547 AutoCFRelease<CTFontRef> ctFont =
548 ::CTFontCreateWithGraphicsFont(mCGFont, mAdjustedSize, nullptr, nullptr);
549 if (!ctFont) {
550 return;
553 mMetrics.underlineOffset = ::CTFontGetUnderlinePosition(ctFont);
554 mMetrics.underlineSize = ::CTFontGetUnderlineThickness(ctFont);
556 mMetrics.externalLeading = ::CTFontGetLeading(ctFont);
558 mMetrics.maxAscent = ::CTFontGetAscent(ctFont);
559 mMetrics.maxDescent = ::CTFontGetDescent(ctFont);
561 // this is not strictly correct, but neither CTFont nor CGFont seems to
562 // provide maxAdvance, unless we were to iterate over all the glyphs
563 // (which isn't worth the cost here)
564 CGRect r = ::CTFontGetBoundingBox(ctFont);
565 mMetrics.maxAdvance = r.size.width;
567 // aveCharWidth is also not provided, so leave it at zero
568 // (fallback code in gfxMacFont::InitMetrics will then try measuring 'x');
569 // this could lead to less-than-"perfect" text field sizing when width is
570 // specified as a number of characters, and the font in use is a non-sfnt
571 // legacy font, but that's a sufficiently obscure edge case that we can
572 // ignore the potential discrepancy.
573 mMetrics.aveCharWidth = 0;
575 mMetrics.xHeight = ::CTFontGetXHeight(ctFont);
576 mMetrics.capHeight = ::CTFontGetCapHeight(ctFont);
578 mIsValid = true;
581 already_AddRefed<ScaledFont> gfxMacFont::GetScaledFont(
582 const TextRunDrawParams& aRunParams) {
583 if (ScaledFont* scaledFont = mAzureScaledFont) {
584 return do_AddRef(scaledFont);
587 gfxFontEntry* fe = GetFontEntry();
588 bool hasColorGlyphs = fe->HasColorBitmapTable() || fe->TryGetColorGlyphs();
589 RefPtr<ScaledFont> newScaledFont = Factory::CreateScaledFontForMacFont(
590 GetCGFontRef(), GetUnscaledFont(), GetAdjustedSize(),
591 ToDeviceColor(mFontSmoothingBackgroundColor),
592 !mStyle.useGrayscaleAntialiasing, ApplySyntheticBold(), hasColorGlyphs);
593 if (!newScaledFont) {
594 return nullptr;
597 InitializeScaledFont(newScaledFont);
599 if (mAzureScaledFont.compareExchange(nullptr, newScaledFont.get())) {
600 Unused << newScaledFont.forget();
602 ScaledFont* scaledFont = mAzureScaledFont;
603 return do_AddRef(scaledFont);
606 bool gfxMacFont::ShouldRoundXOffset(cairo_t* aCairo) const {
607 // Quartz surfaces implement show_glyphs for Quartz fonts
608 return aCairo && cairo_surface_get_type(cairo_get_target(aCairo)) !=
609 CAIRO_SURFACE_TYPE_QUARTZ;
612 bool gfxMacFont::UseNativeColrFontSupport() const {
614 auto* colr = GetFontEntry()->GetCOLR();
615 if (colr && COLRFonts::GetColrTableVersion(colr) == 0) {
616 return true;
619 return false;
622 void gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
623 FontCacheSizes* aSizes) const {
624 gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
625 // mCGFont is shared with the font entry, so not counted here;
628 void gfxMacFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
629 FontCacheSizes* aSizes) const {
630 aSizes->mFontInstances += aMallocSizeOf(this);
631 AddSizeOfExcludingThis(aMallocSizeOf, aSizes);