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/. */
5 
6 #include "gfxMacFont.h"
7 
8 #include "mozilla/MemoryReporting.h"
9 #include "mozilla/Sprintf.h"
10 #include "mozilla/StaticPrefs_gfx.h"
11 
12 #include "gfxCoreTextShaper.h"
13 #include <algorithm>
14 #include "gfxPlatformMac.h"
15 #include "gfxContext.h"
16 #include "gfxFontUtils.h"
17 #include "gfxMacPlatformFontList.h"
18 #include "gfxFontConstants.h"
19 #include "gfxTextRun.h"
20 #include "gfxUtils.h"
21 #include "nsCocoaFeatures.h"
22 #include "AppleUtils.h"
23 #include "cairo-quartz.h"
24 
25 using namespace mozilla;
26 using namespace mozilla::gfx;
27 
28 template <class T>
29 struct TagEquals {
EqualsTagEquals30   bool Equals(const T& aIter, uint32_t aTag) const {
31     return aIter.mTag == aTag;
32   }
33 };
34 
gfxMacFont(const RefPtr<UnscaledFontMac> & aUnscaledFont,MacOSFontEntry * aFontEntry,const gfxFontStyle * aFontStyle)35 gfxMacFont::gfxMacFont(const RefPtr<UnscaledFontMac>& aUnscaledFont,
36                        MacOSFontEntry* aFontEntry,
37                        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);
44 
45   if (mVariationFont) {
46     CGFontRef baseFont = aUnscaledFont->GetFont();
47     if (!baseFont) {
48       mIsValid = false;
49       return;
50     }
51 
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);
56 
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;
66 
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;
84         }
85       }
86 
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;
102         }
103       }
104     }
105 
106     mCGFont = UnscaledFontMac::CreateCGFontWithVariations(
107         baseFont, aUnscaledFont->CGAxesCache(), aUnscaledFont->CTAxesCache(),
108         vars.Length(), vars.Elements());
109     if (!mCGFont) {
110       ::CFRetain(baseFont);
111       mCGFont = baseFont;
112     }
113   } else {
114     mCGFont = aUnscaledFont->GetFont();
115     if (!mCGFont) {
116       mIsValid = false;
117       return;
118     }
119     ::CFRetain(mCGFont);
120   }
121 
122   // InitMetrics will handle the sizeAdjust factor and set mAdjustedSize
123   InitMetrics();
124   if (!mIsValid) {
125     return;
126   }
127 
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;
134   }
135 }
136 
~gfxMacFont()137 gfxMacFont::~gfxMacFont() {
138   if (mCGFont) {
139     ::CFRelease(mCGFont);
140   }
141   if (mCTFont) {
142     ::CFRelease(mCTFont);
143   }
144 }
145 
ShapeText(DrawTarget * aDrawTarget,const char16_t * aText,uint32_t aOffset,uint32_t aLength,Script aScript,nsAtom * aLanguage,bool aVertical,RoundingFlags aRounding,gfxShapedText * aShapedText)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;
154   }
155 
156   // Currently, we don't support vertical shaping via CoreText,
157   // so we ignore RequiresAATLayout if vertical is requested.
158   auto macFontEntry = static_cast<MacOSFontEntry*>(GetFontEntry());
159   if (macFontEntry->RequiresAATLayout() && !aVertical &&
160       StaticPrefs::gfx_font_rendering_coretext_enabled()) {
161     if (!mCoreTextShaper) {
162       mCoreTextShaper = MakeUnique<gfxCoreTextShaper>(this);
163     }
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 (GetFontEntry()->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             GetFontEntry()->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);
182       }
183       return true;
184     }
185   }
186 
187   return gfxFont::ShapeText(aDrawTarget, aText, aOffset, aLength, aScript,
188                             aLanguage, aVertical, aRounding, aShapedText);
189 }
190 
Measure(const gfxTextRun * aTextRun,uint32_t aStart,uint32_t aEnd,BoundingBoxType aBoundingBoxType,DrawTarget * aRefDrawTarget,Spacing * aSpacing,gfx::ShapedTextFlags aOrientation)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);
200 
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;
208   }
209 
210   return metrics;
211 }
212 
InitMetrics()213 void gfxMacFont::InitMetrics() {
214   mIsValid = false;
215   ::memset(&mMetrics, 0, sizeof(mMetrics));
216 
217   uint32_t upem = 0;
218 
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;
231     }
232   }
233   if (!upem) {
234     upem = ::CGFontGetUnitsPerEm(mCGFont);
235   }
236 
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;
247   }
248 
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 = std::max(GetAdjustedSize(), 1.0);
252   mFUnitsConvFactor = mAdjustedSize / upem;
253 
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<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
259     cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
260   } else {
261     cgConvFactor = mFUnitsConvFactor;
262   }
263 
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();
269   }
270   if (!mIsValid) {
271     return;
272   }
273 
274   if (mMetrics.xHeight == 0.0) {
275     mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
276   }
277   if (mMetrics.capHeight == 0.0) {
278     mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
279   }
280 
281   AutoCFRelease<CFDataRef> cmap =
282       ::CGFontCopyTableForTag(mCGFont, TRUETYPE_TAG('c', 'm', 'a', 'p'));
283 
284   uint32_t glyphID;
285   mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
286   if (glyphID == 0) {
287     mMetrics.zeroWidth = -1.0;  // indicates not found
288   }
289 
290   if (FontSizeAdjust::Tag(mStyle.sizeAdjustBasis) !=
291           FontSizeAdjust::Tag::None &&
292       mStyle.sizeAdjust >= 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(0x6C34, vertical);
315         aspect = advance > 0.0 ? advance / mAdjustedSize : 1.0;
316         break;
317       }
318     }
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       mHarfBuzzShaper = nullptr;
323       mAdjustedSize = mStyle.GetAdjustedSize(aspect);
324       mFUnitsConvFactor = mAdjustedSize / upem;
325       if (static_cast<MacOSFontEntry*>(mFontEntry.get())->IsCFF()) {
326         cgConvFactor = mAdjustedSize / ::CGFontGetUnitsPerEm(mCGFont);
327       } else {
328         cgConvFactor = mFUnitsConvFactor;
329       }
330       mMetrics.xHeight = 0.0;
331       if (!InitMetricsFromSfntTables(mMetrics) &&
332           (!mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont())) {
333         InitMetricsFromPlatform();
334       }
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;
339       }
340       // Update metrics from the re-scaled font.
341       if (mMetrics.xHeight == 0.0) {
342         mMetrics.xHeight = ::CGFontGetXHeight(mCGFont) * cgConvFactor;
343       }
344       if (mMetrics.capHeight == 0.0) {
345         mMetrics.capHeight = ::CGFontGetCapHeight(mCGFont) * cgConvFactor;
346       }
347       mMetrics.zeroWidth = GetCharWidth(cmap, '0', &glyphID, cgConvFactor);
348       if (glyphID == 0) {
349         mMetrics.zeroWidth = -1.0;  // indicates not found
350       }
351     }
352   }
353 
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!)
357 
358   mMetrics.emHeight = mAdjustedSize;
359 
360   // Measure/calculate additional metrics, independent of whether we used
361   // the tables directly or ATS metrics APIs
362 
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;
368     }
369   }
370 
371   mMetrics.spaceWidth = GetCharWidth(cmap, ' ', &glyphID, cgConvFactor);
372   if (glyphID == 0) {
373     // no space glyph?!
374     mMetrics.spaceWidth = mMetrics.aveCharWidth;
375   }
376   mSpaceGlyph = glyphID;
377 
378   if (IsSyntheticBold()) {
379     mMetrics.spaceWidth += GetSyntheticBoldOffset();
380     mMetrics.aveCharWidth += GetSyntheticBoldOffset();
381     mMetrics.maxAdvance += GetSyntheticBoldOffset();
382     if (mMetrics.zeroWidth > 0) {
383       mMetrics.zeroWidth += GetSyntheticBoldOffset();
384     }
385   }
386 
387   CalculateDerivedMetrics(mMetrics);
388 
389   SanitizeMetrics(&mMetrics, mFontEntry->mIsBadUnderlineFont);
390 
391 #if 0
392     fprintf (stderr, "Font: %p (%s) size: %f\n", this,
393              NS_ConvertUTF16toUTF8(GetName()).get(), mStyle.size);
394 //    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);
395     fprintf (stderr, "    emHeight: %f emAscent: %f emDescent: %f\n", mMetrics.emHeight, mMetrics.emAscent, mMetrics.emDescent);
396     fprintf (stderr, "    maxAscent: %f maxDescent: %f maxAdvance: %f\n", mMetrics.maxAscent, mMetrics.maxDescent, mMetrics.maxAdvance);
397     fprintf (stderr, "    internalLeading: %f externalLeading: %f\n", mMetrics.internalLeading, mMetrics.externalLeading);
398     fprintf (stderr, "    spaceWidth: %f aveCharWidth: %f xHeight: %f capHeight: %f\n", mMetrics.spaceWidth, mMetrics.aveCharWidth, mMetrics.xHeight, mMetrics.capHeight);
399     fprintf (stderr, "    uOff: %f uSize: %f stOff: %f stSize: %f\n", mMetrics.underlineOffset, mMetrics.underlineSize, mMetrics.strikeoutOffset, mMetrics.strikeoutSize);
400 #endif
401 }
402 
GetCharWidth(CFDataRef aCmap,char16_t aUniChar,uint32_t * aGlyphID,gfxFloat aConvFactor)403 gfxFloat gfxMacFont::GetCharWidth(CFDataRef aCmap, char16_t aUniChar,
404                                   uint32_t* aGlyphID, gfxFloat aConvFactor) {
405   CGGlyph glyph = 0;
406 
407   if (aCmap) {
408     glyph = gfxFontUtils::MapCharToGlyph(::CFDataGetBytePtr(aCmap),
409                                          ::CFDataGetLength(aCmap), aUniChar);
410   }
411 
412   if (aGlyphID) {
413     *aGlyphID = glyph;
414   }
415 
416   if (glyph) {
417     int advance;
418     if (::CGFontGetGlyphAdvances(mCGFont, &glyph, 1, &advance)) {
419       return advance * aConvFactor;
420     }
421   }
422 
423   return 0;
424 }
425 
426 /* static */
CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont,CGFloat aSize,bool aInstalledFont,CTFontDescriptorRef aFontDesc)427 CTFontRef gfxMacFont::CreateCTFontFromCGFontWithVariations(
428     CGFontRef aCGFont, CGFloat aSize, bool aInstalledFont,
429     CTFontDescriptorRef aFontDesc) {
430   // Avoid calling potentially buggy variation APIs on pre-Sierra macOS
431   // versions (see bug 1331683).
432   //
433   // And on HighSierra, CTFontCreateWithGraphicsFont properly carries over
434   // variation settings from the CGFont to CTFont, so we don't need to do
435   // the extra work here -- and this seems to avoid Core Text crashiness
436   // seen in bug 1454094.
437   //
438   // However, for installed fonts it seems we DO need to copy the variations
439   // explicitly even on 10.13, otherwise fonts fail to render (as in bug
440   // 1455494) when non-default values are used. Fortunately, the crash
441   // mentioned above occurs with data fonts, not (AFAICT) with system-
442   // installed fonts.
443   //
444   // So we only need to do this "the hard way" on Sierra, and on HighSierra
445   // for system-installed fonts; in other cases just let the standard CTFont
446   // function do its thing.
447   //
448   // NOTE in case this ever needs further adjustment: there is similar logic
449   // in four places in the tree (sadly):
450   //    CreateCTFontFromCGFontWithVariations in gfxMacFont.cpp
451   //    CreateCTFontFromCGFontWithVariations in ScaledFontMac.cpp
452   //    CreateCTFontFromCGFontWithVariations in cairo-quartz-font.c
453   //    ctfont_create_exact_copy in SkFontHost_mac.cpp
454 
455   CTFontRef ctFont;
456   if (nsCocoaFeatures::OnSierraExactly() ||
457       (aInstalledFont && nsCocoaFeatures::OnHighSierraOrLater())) {
458     AutoCFRelease<CFDictionaryRef> variations = ::CGFontCopyVariations(aCGFont);
459     if (variations) {
460       AutoCFRelease<CFDictionaryRef> varAttr = ::CFDictionaryCreate(
461           nullptr, (const void**)&kCTFontVariationAttribute,
462           (const void**)&variations, 1, &kCFTypeDictionaryKeyCallBacks,
463           &kCFTypeDictionaryValueCallBacks);
464 
465       AutoCFRelease<CTFontDescriptorRef> varDesc =
466           aFontDesc
467               ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr)
468               : ::CTFontDescriptorCreateWithAttributes(varAttr);
469 
470       ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc);
471     } else {
472       ctFont =
473           ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
474     }
475   } else {
476     ctFont = ::CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc);
477   }
478 
479   return ctFont;
480 }
481 
GetGlyphWidth(uint16_t aGID)482 int32_t gfxMacFont::GetGlyphWidth(uint16_t aGID) {
483   if (mVariationFont) {
484     // Avoid a potential Core Text crash (bug 1450209) by using
485     // CoreGraphics glyph advance API. This is inferior for 'sbix'
486     // fonts, but those won't have variations, so it's OK.
487     int cgAdvance;
488     if (::CGFontGetGlyphAdvances(mCGFont, &aGID, 1, &cgAdvance)) {
489       return cgAdvance * mFUnitsConvFactor * 0x10000;
490     }
491   }
492 
493   if (!mCTFont) {
494     bool isInstalledFont =
495         !mFontEntry->IsUserFont() || mFontEntry->IsLocalUserFont();
496     mCTFont = CreateCTFontFromCGFontWithVariations(mCGFont, mAdjustedSize,
497                                                    isInstalledFont);
498     if (!mCTFont) {  // shouldn't happen, but let's be safe
499       NS_WARNING("failed to create CTFontRef to measure glyph width");
500       return 0;
501     }
502   }
503 
504   CGSize advance;
505   ::CTFontGetAdvancesForGlyphs(mCTFont, kCTFontOrientationDefault, &aGID,
506                                &advance, 1);
507   return advance.width * 0x10000;
508 }
509 
GetGlyphBounds(uint16_t aGID,gfxRect * aBounds,bool aTight)510 bool gfxMacFont::GetGlyphBounds(uint16_t aGID, gfxRect* aBounds, bool aTight) {
511   CGRect bb;
512   if (!::CGFontGetGlyphBBoxes(mCGFont, &aGID, 1, &bb)) {
513     return false;
514   }
515 
516   // broken fonts can return incorrect bounds for some null characters,
517   // see https://bugzilla.mozilla.org/show_bug.cgi?id=534260
518   if (bb.origin.x == -32767 && bb.origin.y == -32767 &&
519       bb.size.width == 65534 && bb.size.height == 65534) {
520     *aBounds = gfxRect(0, 0, 0, 0);
521     return true;
522   }
523 
524   gfxRect bounds(bb.origin.x, -(bb.origin.y + bb.size.height), bb.size.width,
525                  bb.size.height);
526   bounds.Scale(mFUnitsConvFactor);
527   *aBounds = bounds;
528   return true;
529 }
530 
531 // Try to initialize font metrics via platform APIs (CG/CT),
532 // and set mIsValid = TRUE on success.
533 // We ONLY call this for local (platform) fonts that are not sfnt format;
534 // for sfnts, including ALL downloadable fonts, we prefer to use
535 // InitMetricsFromSfntTables and avoid platform APIs.
InitMetricsFromPlatform()536 void gfxMacFont::InitMetricsFromPlatform() {
537   AutoCFRelease<CTFontRef> ctFont =
538       ::CTFontCreateWithGraphicsFont(mCGFont, mAdjustedSize, nullptr, nullptr);
539   if (!ctFont) {
540     return;
541   }
542 
543   mMetrics.underlineOffset = ::CTFontGetUnderlinePosition(ctFont);
544   mMetrics.underlineSize = ::CTFontGetUnderlineThickness(ctFont);
545 
546   mMetrics.externalLeading = ::CTFontGetLeading(ctFont);
547 
548   mMetrics.maxAscent = ::CTFontGetAscent(ctFont);
549   mMetrics.maxDescent = ::CTFontGetDescent(ctFont);
550 
551   // this is not strictly correct, but neither CTFont nor CGFont seems to
552   // provide maxAdvance, unless we were to iterate over all the glyphs
553   // (which isn't worth the cost here)
554   CGRect r = ::CTFontGetBoundingBox(ctFont);
555   mMetrics.maxAdvance = r.size.width;
556 
557   // aveCharWidth is also not provided, so leave it at zero
558   // (fallback code in gfxMacFont::InitMetrics will then try measuring 'x');
559   // this could lead to less-than-"perfect" text field sizing when width is
560   // specified as a number of characters, and the font in use is a non-sfnt
561   // legacy font, but that's a sufficiently obscure edge case that we can
562   // ignore the potential discrepancy.
563   mMetrics.aveCharWidth = 0;
564 
565   mMetrics.xHeight = ::CTFontGetXHeight(ctFont);
566   mMetrics.capHeight = ::CTFontGetCapHeight(ctFont);
567 
568   mIsValid = true;
569 }
570 
GetScaledFont(DrawTarget * aTarget)571 already_AddRefed<ScaledFont> gfxMacFont::GetScaledFont(DrawTarget* aTarget) {
572   if (!mAzureScaledFont) {
573     mAzureScaledFont = Factory::CreateScaledFontForMacFont(
574         GetCGFontRef(), GetUnscaledFont(), GetAdjustedSize(),
575         ToDeviceColor(mFontSmoothingBackgroundColor),
576         !mStyle.useGrayscaleAntialiasing, IsSyntheticBold());
577     if (!mAzureScaledFont) {
578       return nullptr;
579     }
580     InitializeScaledFont();
581   }
582 
583   RefPtr<ScaledFont> scaledFont(mAzureScaledFont);
584   return scaledFont.forget();
585 }
586 
ShouldRoundXOffset(cairo_t * aCairo) const587 bool gfxMacFont::ShouldRoundXOffset(cairo_t* aCairo) const {
588   // Quartz surfaces implement show_glyphs for Quartz fonts
589   return aCairo && cairo_surface_get_type(cairo_get_target(aCairo)) !=
590                        CAIRO_SURFACE_TYPE_QUARTZ;
591 }
592 
AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const593 void gfxMacFont::AddSizeOfExcludingThis(MallocSizeOf aMallocSizeOf,
594                                         FontCacheSizes* aSizes) const {
595   gfxFont::AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
596   // mCGFont is shared with the font entry, so not counted here;
597 }
598 
AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,FontCacheSizes * aSizes) const599 void gfxMacFont::AddSizeOfIncludingThis(MallocSizeOf aMallocSizeOf,
600                                         FontCacheSizes* aSizes) const {
601   aSizes->mFontInstances += aMallocSizeOf(this);
602   AddSizeOfExcludingThis(aMallocSizeOf, aSizes);
603 }
604