Bug 1866777 - Disable test_race_cache_with_network.js on windows opt for frequent...
[gecko.git] / gfx / 2d / ScaledFontMac.cpp
blob9528b3527e0476122bd64c0ad9e7f85a74577426
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ScaledFontMac.h"
8 #include "UnscaledFontMac.h"
9 #include "mozilla/webrender/WebRenderTypes.h"
10 #include "nsCocoaFeatures.h"
11 #include "PathSkia.h"
12 #include "skia/include/core/SkPaint.h"
13 #include "skia/include/core/SkPath.h"
14 #include "skia/include/ports/SkTypeface_mac.h"
15 #include <vector>
16 #include <dlfcn.h>
17 #ifdef MOZ_WIDGET_UIKIT
18 # include <CoreFoundation/CoreFoundation.h>
19 #endif
20 #include "mozilla/gfx/Logging.h"
22 #ifdef MOZ_WIDGET_COCOA
23 // prototype for private API
24 extern "C" {
25 CGPathRef CGFontGetGlyphPath(CGFontRef fontRef,
26 CGAffineTransform* textTransform, int unknown,
27 CGGlyph glyph);
29 #endif
31 #include "cairo-quartz.h"
33 namespace mozilla {
34 namespace gfx {
36 // Simple helper class to automatically release a CFObject when it goes out
37 // of scope.
38 template <class T>
39 class AutoRelease final {
40 public:
41 explicit AutoRelease(T aObject) : mObject(aObject) {}
43 ~AutoRelease() {
44 if (mObject) {
45 CFRelease(mObject);
49 AutoRelease<T>& operator=(const T& aObject) {
50 if (aObject != mObject) {
51 if (mObject) {
52 CFRelease(mObject);
54 mObject = aObject;
56 return *this;
59 operator T() { return mObject; }
61 T forget() {
62 T obj = mObject;
63 mObject = nullptr;
64 return obj;
67 private:
68 T mObject;
71 // Helper to create a CTFont from a CGFont, copying any variations that were
72 // set on the CGFont, and applying attributes from (optional) aFontDesc.
73 CTFontRef CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont, CGFloat aSize,
74 bool aInstalledFont,
75 CTFontDescriptorRef aFontDesc) {
76 // New implementation (see bug 1856035) for macOS 13+.
77 if (nsCocoaFeatures::OnVenturaOrLater()) {
78 // Create CTFont, applying any descriptor that was passed (used by
79 // gfxCoreTextShaper to set features).
80 AutoRelease<CTFontRef> ctFont(
81 CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, aFontDesc));
82 AutoRelease<CFDictionaryRef> vars(CGFontCopyVariations(aCGFont));
83 if (vars) {
84 // Create an attribute dictionary containing the variations.
85 AutoRelease<CFDictionaryRef> attrs(CFDictionaryCreate(
86 nullptr, (const void**)&kCTFontVariationAttribute,
87 (const void**)&vars, 1, &kCFTypeDictionaryKeyCallBacks,
88 &kCFTypeDictionaryValueCallBacks));
89 // Get the original descriptor from the CTFont, then add the variations
90 // attribute to it.
91 AutoRelease<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(ctFont));
92 desc = CTFontDescriptorCreateCopyWithAttributes(desc, attrs);
93 // Return a copy of the font that has the variations added.
94 return CTFontCreateCopyWithAttributes(ctFont, 0.0, nullptr, desc);
96 // No variations to set, just return the default CTFont.
97 return ctFont.forget();
100 // Older implementation used up to macOS 12.
101 CTFontRef ctFont;
102 if (aInstalledFont) {
103 AutoRelease<CFDictionaryRef> vars(CGFontCopyVariations(aCGFont));
104 if (vars) {
105 AutoRelease<CFDictionaryRef> varAttr(CFDictionaryCreate(
106 nullptr, (const void**)&kCTFontVariationAttribute,
107 (const void**)&vars, 1, &kCFTypeDictionaryKeyCallBacks,
108 &kCFTypeDictionaryValueCallBacks));
110 AutoRelease<CTFontDescriptorRef> varDesc(
111 aFontDesc
112 ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc, varAttr)
113 : ::CTFontDescriptorCreateWithAttributes(varAttr));
115 ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, varDesc);
116 } else {
117 ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr);
119 } else {
120 ctFont = CTFontCreateWithGraphicsFont(aCGFont, aSize, nullptr, nullptr);
122 return ctFont;
125 ScaledFontMac::ScaledFontMac(CGFontRef aFont,
126 const RefPtr<UnscaledFont>& aUnscaledFont,
127 Float aSize, bool aOwnsFont,
128 bool aUseFontSmoothing, bool aApplySyntheticBold,
129 bool aHasColorGlyphs)
130 : ScaledFontBase(aUnscaledFont, aSize),
131 mFont(aFont),
132 mUseFontSmoothing(aUseFontSmoothing),
133 mApplySyntheticBold(aApplySyntheticBold),
134 mHasColorGlyphs(aHasColorGlyphs) {
135 if (!aOwnsFont) {
136 // XXX: should we be taking a reference
137 CGFontRetain(aFont);
140 auto unscaledMac = static_cast<UnscaledFontMac*>(aUnscaledFont.get());
141 bool dataFont = unscaledMac->IsDataFont();
142 mCTFont = CreateCTFontFromCGFontWithVariations(aFont, aSize, !dataFont);
145 ScaledFontMac::ScaledFontMac(CTFontRef aFont,
146 const RefPtr<UnscaledFont>& aUnscaledFont,
147 bool aUseFontSmoothing, bool aApplySyntheticBold,
148 bool aHasColorGlyphs)
149 : ScaledFontBase(aUnscaledFont, CTFontGetSize(aFont)),
150 mCTFont(aFont),
151 mUseFontSmoothing(aUseFontSmoothing),
152 mApplySyntheticBold(aApplySyntheticBold),
153 mHasColorGlyphs(aHasColorGlyphs) {
154 mFont = CTFontCopyGraphicsFont(aFont, nullptr);
156 CFRetain(mCTFont);
159 ScaledFontMac::~ScaledFontMac() {
160 CFRelease(mCTFont);
161 CGFontRelease(mFont);
164 SkTypeface* ScaledFontMac::CreateSkTypeface() {
165 return SkMakeTypefaceFromCTFont(mCTFont).release();
168 void ScaledFontMac::SetupSkFontDrawOptions(SkFont& aFont) {
169 aFont.setSubpixel(true);
171 // Normally, Skia enables LCD FontSmoothing which creates thicker fonts
172 // and also enables subpixel AA. CoreGraphics without font smoothing
173 // explicitly creates thinner fonts and grayscale AA.
174 // CoreGraphics doesn't support a configuration that produces thicker
175 // fonts with grayscale AA as LCD Font Smoothing enables or disables
176 // both. However, Skia supports it by enabling font smoothing (producing
177 // subpixel AA) and converts it to grayscale AA. Since Skia doesn't
178 // support subpixel AA on transparent backgrounds, we still want font
179 // smoothing for the thicker fonts, even if it is grayscale AA.
181 // With explicit Grayscale AA (from -moz-osx-font-smoothing:grayscale),
182 // we want to have grayscale AA with no smoothing at all. This means
183 // disabling the LCD font smoothing behaviour.
184 // To accomplish this we have to explicitly disable hinting,
185 // and disable LCDRenderText.
186 if (aFont.getEdging() == SkFont::Edging::kAntiAlias && !mUseFontSmoothing) {
187 aFont.setHinting(SkFontHinting::kNone);
191 // private API here are the public options on OS X
192 // CTFontCreatePathForGlyph
193 // ATSUGlyphGetCubicPaths
194 // we've used this in cairo sucessfully for some time.
195 // Note: cairo dlsyms it. We could do that but maybe it's
196 // safe just to use?
198 already_AddRefed<Path> ScaledFontMac::GetPathForGlyphs(
199 const GlyphBuffer& aBuffer, const DrawTarget* aTarget) {
200 return ScaledFontBase::GetPathForGlyphs(aBuffer, aTarget);
203 static uint32_t CalcTableChecksum(const uint32_t* tableStart, uint32_t length,
204 bool skipChecksumAdjust = false) {
205 uint32_t sum = 0L;
206 const uint32_t* table = tableStart;
207 const uint32_t* end = table + length / sizeof(uint32_t);
208 while (table < end) {
209 if (skipChecksumAdjust && (table - tableStart) == 2) {
210 table++;
211 } else {
212 sum += CFSwapInt32BigToHost(*table++);
216 // The length is not 4-byte aligned, but we still must process the remaining
217 // bytes.
218 if (length & 3) {
219 // Pad with zero before adding to the checksum.
220 uint32_t last = 0;
221 memcpy(&last, end, length & 3);
222 sum += CFSwapInt32BigToHost(last);
225 return sum;
228 struct TableRecord {
229 uint32_t tag;
230 uint32_t checkSum;
231 uint32_t offset;
232 uint32_t length;
233 CFDataRef data;
236 static int maxPow2LessThanEqual(int a) {
237 int x = 1;
238 int shift = 0;
239 while ((x << (shift + 1)) <= a) {
240 shift++;
242 return shift;
245 struct writeBuf final {
246 explicit writeBuf(int size) {
247 this->data = new unsigned char[size];
248 this->offset = 0;
250 ~writeBuf() { delete[] this->data; }
252 template <class T>
253 void writeElement(T a) {
254 *reinterpret_cast<T*>(&this->data[this->offset]) = a;
255 this->offset += sizeof(T);
258 void writeMem(const void* data, unsigned long length) {
259 memcpy(&this->data[this->offset], data, length);
260 this->offset += length;
263 void align() {
264 while (this->offset & 3) {
265 this->data[this->offset] = 0;
266 this->offset++;
270 unsigned char* data;
271 int offset;
274 bool UnscaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback,
275 void* aBaton) {
276 // We'll reconstruct a TTF font from the tables we can get from the CGFont
277 CFArrayRef tags = CGFontCopyTableTags(mFont);
278 CFIndex count = CFArrayGetCount(tags);
280 TableRecord* records = new TableRecord[count];
281 uint32_t offset = 0;
282 offset += sizeof(uint32_t) * 3;
283 offset += sizeof(uint32_t) * 4 * count;
284 bool CFF = false;
285 for (CFIndex i = 0; i < count; i++) {
286 uint32_t tag = (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags, i);
287 if (tag == 0x43464620) { // 'CFF '
288 CFF = true;
290 CFDataRef data = CGFontCopyTableForTag(mFont, tag);
291 // Bug 1602391 suggests CGFontCopyTableForTag can fail, even though we just
292 // got the tag from the font via CGFontCopyTableTags above. If we can catch
293 // this (e.g. in fuzz-testing) it'd be good to understand when it happens,
294 // but in any case we'll handle it safely below by treating the table as
295 // zero-length.
296 MOZ_ASSERT(data, "failed to get font table data");
297 records[i].tag = tag;
298 records[i].offset = offset;
299 records[i].data = data;
300 if (data) {
301 records[i].length = CFDataGetLength(data);
302 bool skipChecksumAdjust = (tag == 0x68656164); // 'head'
303 records[i].checkSum = CalcTableChecksum(
304 reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data)),
305 records[i].length, skipChecksumAdjust);
306 offset += records[i].length;
307 // 32 bit align the tables
308 offset = (offset + 3) & ~3;
309 } else {
310 records[i].length = 0;
311 records[i].checkSum = 0;
314 CFRelease(tags);
316 struct writeBuf buf(offset);
317 // write header/offset table
318 if (CFF) {
319 buf.writeElement(CFSwapInt32HostToBig(0x4f54544f));
320 } else {
321 buf.writeElement(CFSwapInt32HostToBig(0x00010000));
323 buf.writeElement(CFSwapInt16HostToBig(count));
324 int maxPow2Count = maxPow2LessThanEqual(count);
325 buf.writeElement(CFSwapInt16HostToBig((1 << maxPow2Count) * 16));
326 buf.writeElement(CFSwapInt16HostToBig(maxPow2Count));
327 buf.writeElement(CFSwapInt16HostToBig((count - (1 << maxPow2Count)) * 16));
329 // write table record entries
330 for (CFIndex i = 0; i < count; i++) {
331 buf.writeElement(CFSwapInt32HostToBig(records[i].tag));
332 buf.writeElement(CFSwapInt32HostToBig(records[i].checkSum));
333 buf.writeElement(CFSwapInt32HostToBig(records[i].offset));
334 buf.writeElement(CFSwapInt32HostToBig(records[i].length));
337 // write tables
338 int checkSumAdjustmentOffset = 0;
339 for (CFIndex i = 0; i < count; i++) {
340 if (records[i].tag == 0x68656164) {
341 checkSumAdjustmentOffset = buf.offset + 2 * 4;
343 if (records[i].data) {
344 buf.writeMem(CFDataGetBytePtr(records[i].data), records[i].length);
345 buf.align();
346 CFRelease(records[i].data);
349 delete[] records;
351 // clear the checksumAdjust field before checksumming the whole font
352 memset(&buf.data[checkSumAdjustmentOffset], 0, sizeof(uint32_t));
353 uint32_t fontChecksum = CFSwapInt32HostToBig(
354 0xb1b0afba -
355 CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf.data), offset));
356 // set checkSumAdjust to the computed checksum
357 memcpy(&buf.data[checkSumAdjustmentOffset], &fontChecksum,
358 sizeof(fontChecksum));
360 // we always use an index of 0
361 aDataCallback(buf.data, buf.offset, 0, aBaton);
363 return true;
366 bool UnscaledFontMac::GetFontDescriptor(FontDescriptorOutput aCb,
367 void* aBaton) {
368 if (mIsDataFont) {
369 return false;
372 AutoRelease<CFStringRef> psname(CGFontCopyPostScriptName(mFont));
373 if (!psname) {
374 return false;
377 char buf[1024];
378 const char* cstr = CFStringGetCStringPtr(psname, kCFStringEncodingUTF8);
379 if (!cstr) {
380 if (!CFStringGetCString(psname, buf, sizeof(buf), kCFStringEncodingUTF8)) {
381 return false;
383 cstr = buf;
386 nsAutoCString descriptor(cstr);
387 uint32_t psNameLen = descriptor.Length();
389 AutoRelease<CTFontRef> ctFont(
390 CTFontCreateWithGraphicsFont(mFont, 0, nullptr, nullptr));
391 AutoRelease<CFURLRef> fontUrl(
392 (CFURLRef)CTFontCopyAttribute(ctFont, kCTFontURLAttribute));
393 if (fontUrl) {
394 CFStringRef urlStr(CFURLCopyFileSystemPath(fontUrl, kCFURLPOSIXPathStyle));
395 cstr = CFStringGetCStringPtr(urlStr, kCFStringEncodingUTF8);
396 if (!cstr) {
397 if (!CFStringGetCString(urlStr, buf, sizeof(buf),
398 kCFStringEncodingUTF8)) {
399 return false;
401 cstr = buf;
403 descriptor.Append(cstr);
406 aCb(reinterpret_cast<const uint8_t*>(descriptor.get()), descriptor.Length(),
407 psNameLen, aBaton);
408 return true;
411 static void CollectVariationsFromDictionary(const void* aKey,
412 const void* aValue,
413 void* aContext) {
414 auto keyPtr = static_cast<const CFTypeRef>(aKey);
415 auto valuePtr = static_cast<const CFTypeRef>(aValue);
416 auto outVariations = static_cast<std::vector<FontVariation>*>(aContext);
417 if (CFGetTypeID(keyPtr) == CFNumberGetTypeID() &&
418 CFGetTypeID(valuePtr) == CFNumberGetTypeID()) {
419 uint64_t t;
420 double v;
421 if (CFNumberGetValue(static_cast<CFNumberRef>(keyPtr), kCFNumberSInt64Type,
422 &t) &&
423 CFNumberGetValue(static_cast<CFNumberRef>(valuePtr),
424 kCFNumberDoubleType, &v)) {
425 outVariations->push_back(FontVariation{uint32_t(t), float(v)});
430 static bool GetVariationsForCTFont(CTFontRef aCTFont,
431 std::vector<FontVariation>* aOutVariations) {
432 if (!aCTFont) {
433 return true;
435 AutoRelease<CFDictionaryRef> dict(CTFontCopyVariation(aCTFont));
436 CFIndex count = dict ? CFDictionaryGetCount(dict) : 0;
437 if (count > 0) {
438 aOutVariations->reserve(count);
439 CFDictionaryApplyFunction(dict, CollectVariationsFromDictionary,
440 aOutVariations);
442 return true;
445 bool ScaledFontMac::GetFontInstanceData(FontInstanceDataOutput aCb,
446 void* aBaton) {
447 // Collect any variation settings that were incorporated into the CTFont.
448 std::vector<FontVariation> variations;
449 if (!GetVariationsForCTFont(mCTFont, &variations)) {
450 return false;
453 InstanceData instance(this);
454 aCb(reinterpret_cast<uint8_t*>(&instance), sizeof(instance),
455 variations.data(), variations.size(), aBaton);
456 return true;
459 bool ScaledFontMac::GetWRFontInstanceOptions(
460 Maybe<wr::FontInstanceOptions>* aOutOptions,
461 Maybe<wr::FontInstancePlatformOptions>* aOutPlatformOptions,
462 std::vector<FontVariation>* aOutVariations) {
463 GetVariationsForCTFont(mCTFont, aOutVariations);
465 wr::FontInstanceOptions options;
466 options.render_mode = wr::FontRenderMode::Subpixel;
467 options.flags = wr::FontInstanceFlags::SUBPIXEL_POSITION;
468 if (mUseFontSmoothing) {
469 options.flags |= wr::FontInstanceFlags::FONT_SMOOTHING;
471 if (mApplySyntheticBold) {
472 options.flags |= wr::FontInstanceFlags::SYNTHETIC_BOLD;
474 if (mHasColorGlyphs) {
475 options.flags |= wr::FontInstanceFlags::EMBEDDED_BITMAPS;
477 options.synthetic_italics =
478 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
479 *aOutOptions = Some(options);
480 return true;
483 ScaledFontMac::InstanceData::InstanceData(
484 const wr::FontInstanceOptions* aOptions,
485 const wr::FontInstancePlatformOptions* aPlatformOptions)
486 : mUseFontSmoothing(true),
487 mApplySyntheticBold(false),
488 mHasColorGlyphs(false) {
489 if (aOptions) {
490 if (!(aOptions->flags & wr::FontInstanceFlags::FONT_SMOOTHING)) {
491 mUseFontSmoothing = false;
493 if (aOptions->flags & wr::FontInstanceFlags::SYNTHETIC_BOLD) {
494 mApplySyntheticBold = true;
496 if (aOptions->flags & wr::FontInstanceFlags::EMBEDDED_BITMAPS) {
497 mHasColorGlyphs = true;
502 static CFDictionaryRef CreateVariationDictionaryOrNull(
503 CGFontRef aCGFont, CFArrayRef& aCGAxesCache, CFArrayRef& aCTAxesCache,
504 uint32_t aVariationCount, const FontVariation* aVariations) {
505 if (!aCGAxesCache) {
506 aCGAxesCache = CGFontCopyVariationAxes(aCGFont);
507 if (!aCGAxesCache) {
508 return nullptr;
511 if (!aCTAxesCache) {
512 AutoRelease<CTFontRef> ctFont(
513 CTFontCreateWithGraphicsFont(aCGFont, 0, nullptr, nullptr));
514 aCTAxesCache = CTFontCopyVariationAxes(ctFont);
515 if (!aCTAxesCache) {
516 return nullptr;
520 CFIndex axisCount = CFArrayGetCount(aCTAxesCache);
521 if (CFArrayGetCount(aCGAxesCache) != axisCount) {
522 return nullptr;
525 AutoRelease<CFMutableDictionaryRef> dict(CFDictionaryCreateMutable(
526 kCFAllocatorDefault, axisCount, &kCFTypeDictionaryKeyCallBacks,
527 &kCFTypeDictionaryValueCallBacks));
529 // Number of variation settings passed in the aVariations parameter.
530 // This will typically be a very low value, so we just linear-search them.
531 bool allDefaultValues = true;
533 for (CFIndex i = 0; i < axisCount; ++i) {
534 // We sanity-check the axis info found in the CTFont, and bail out
535 // (returning null) if it doesn't have the expected types.
536 CFTypeRef axisInfo = CFArrayGetValueAtIndex(aCTAxesCache, i);
537 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
538 return nullptr;
540 CFDictionaryRef axis = static_cast<CFDictionaryRef>(axisInfo);
542 CFTypeRef axisTag =
543 CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey);
544 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
545 return nullptr;
547 int64_t tagLong;
548 if (!CFNumberGetValue(static_cast<CFNumberRef>(axisTag),
549 kCFNumberSInt64Type, &tagLong)) {
550 return nullptr;
553 axisInfo = CFArrayGetValueAtIndex(aCGAxesCache, i);
554 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
555 return nullptr;
557 CFTypeRef axisName = CFDictionaryGetValue(
558 static_cast<CFDictionaryRef>(axisInfo), kCGFontVariationAxisName);
559 if (!axisName || CFGetTypeID(axisName) != CFStringGetTypeID()) {
560 return nullptr;
563 // Clamp axis values to the supported range.
564 CFTypeRef min =
565 CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey);
566 CFTypeRef max =
567 CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey);
568 CFTypeRef def =
569 CFDictionaryGetValue(axis, kCTFontVariationAxisDefaultValueKey);
570 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || !max ||
571 CFGetTypeID(max) != CFNumberGetTypeID() || !def ||
572 CFGetTypeID(def) != CFNumberGetTypeID()) {
573 return nullptr;
575 double minDouble;
576 double maxDouble;
577 double defDouble;
578 if (!CFNumberGetValue(static_cast<CFNumberRef>(min), kCFNumberDoubleType,
579 &minDouble) ||
580 !CFNumberGetValue(static_cast<CFNumberRef>(max), kCFNumberDoubleType,
581 &maxDouble) ||
582 !CFNumberGetValue(static_cast<CFNumberRef>(def), kCFNumberDoubleType,
583 &defDouble)) {
584 return nullptr;
587 double value = defDouble;
588 for (uint32_t j = 0; j < aVariationCount; ++j) {
589 if (aVariations[j].mTag == tagLong) {
590 value = std::min(std::max<double>(aVariations[j].mValue, minDouble),
591 maxDouble);
592 if (value != defDouble) {
593 allDefaultValues = false;
595 break;
598 AutoRelease<CFNumberRef> valueNumber(
599 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
600 CFDictionaryAddValue(dict, axisName, valueNumber);
603 if (allDefaultValues) {
604 // We didn't actually set any non-default values, so throw away the
605 // variations dictionary and just use the default rendering.
606 return nullptr;
609 return dict.forget();
612 static CFDictionaryRef CreateVariationTagDictionaryOrNull(
613 CTFontRef aCTFont, uint32_t aVariationCount,
614 const FontVariation* aVariations) {
615 AutoRelease<CFArrayRef> axes(CTFontCopyVariationAxes(aCTFont));
616 CFIndex axisCount = CFArrayGetCount(axes);
618 AutoRelease<CFMutableDictionaryRef> dict(CFDictionaryCreateMutable(
619 kCFAllocatorDefault, axisCount, &kCFTypeDictionaryKeyCallBacks,
620 &kCFTypeDictionaryValueCallBacks));
622 // Number of variation settings passed in the aVariations parameter.
623 // This will typically be a very low value, so we just linear-search them.
624 bool allDefaultValues = true;
626 for (CFIndex i = 0; i < axisCount; ++i) {
627 // We sanity-check the axis info found in the CTFont, and bail out
628 // (returning null) if it doesn't have the expected types.
629 CFTypeRef axisInfo = CFArrayGetValueAtIndex(axes, i);
630 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo)) {
631 return nullptr;
633 CFDictionaryRef axis = static_cast<CFDictionaryRef>(axisInfo);
635 CFTypeRef axisTag =
636 CFDictionaryGetValue(axis, kCTFontVariationAxisIdentifierKey);
637 if (!axisTag || CFGetTypeID(axisTag) != CFNumberGetTypeID()) {
638 return nullptr;
640 int64_t tagLong;
641 if (!CFNumberGetValue(static_cast<CFNumberRef>(axisTag),
642 kCFNumberSInt64Type, &tagLong)) {
643 return nullptr;
646 // Clamp axis values to the supported range.
647 CFTypeRef min =
648 CFDictionaryGetValue(axis, kCTFontVariationAxisMinimumValueKey);
649 CFTypeRef max =
650 CFDictionaryGetValue(axis, kCTFontVariationAxisMaximumValueKey);
651 CFTypeRef def =
652 CFDictionaryGetValue(axis, kCTFontVariationAxisDefaultValueKey);
653 if (!min || CFGetTypeID(min) != CFNumberGetTypeID() || !max ||
654 CFGetTypeID(max) != CFNumberGetTypeID() || !def ||
655 CFGetTypeID(def) != CFNumberGetTypeID()) {
656 return nullptr;
658 double minDouble;
659 double maxDouble;
660 double defDouble;
661 if (!CFNumberGetValue(static_cast<CFNumberRef>(min), kCFNumberDoubleType,
662 &minDouble) ||
663 !CFNumberGetValue(static_cast<CFNumberRef>(max), kCFNumberDoubleType,
664 &maxDouble) ||
665 !CFNumberGetValue(static_cast<CFNumberRef>(def), kCFNumberDoubleType,
666 &defDouble)) {
667 return nullptr;
670 double value = defDouble;
671 for (uint32_t j = 0; j < aVariationCount; ++j) {
672 if (aVariations[j].mTag == tagLong) {
673 value = std::min(std::max<double>(aVariations[j].mValue, minDouble),
674 maxDouble);
675 if (value != defDouble) {
676 allDefaultValues = false;
678 break;
681 AutoRelease<CFNumberRef> valueNumber(
682 CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
683 CFDictionaryAddValue(dict, axisTag, valueNumber);
686 if (allDefaultValues) {
687 // We didn't actually set any non-default values, so throw away the
688 // variations dictionary and just use the default rendering.
689 return nullptr;
692 return dict.forget();
695 /* static */
696 CGFontRef UnscaledFontMac::CreateCGFontWithVariations(
697 CGFontRef aFont, CFArrayRef& aCGAxesCache, CFArrayRef& aCTAxesCache,
698 uint32_t aVariationCount, const FontVariation* aVariations) {
699 if (!aVariationCount) {
700 return nullptr;
702 MOZ_ASSERT(aVariations);
704 AutoRelease<CFDictionaryRef> varDict(CreateVariationDictionaryOrNull(
705 aFont, aCGAxesCache, aCTAxesCache, aVariationCount, aVariations));
706 if (!varDict) {
707 return nullptr;
710 return CGFontCreateCopyWithVariations(aFont, varDict);
713 already_AddRefed<ScaledFont> UnscaledFontMac::CreateScaledFont(
714 Float aGlyphSize, const uint8_t* aInstanceData,
715 uint32_t aInstanceDataLength, const FontVariation* aVariations,
716 uint32_t aNumVariations)
719 if (aInstanceDataLength < sizeof(ScaledFontMac::InstanceData)) {
720 gfxWarning() << "Mac scaled font instance data is truncated.";
721 return nullptr;
723 const ScaledFontMac::InstanceData& instanceData =
724 *reinterpret_cast<const ScaledFontMac::InstanceData*>(aInstanceData);
725 RefPtr<ScaledFontMac> scaledFont;
726 if (mFontDesc) {
727 AutoRelease<CTFontRef> font(
728 CTFontCreateWithFontDescriptor(mFontDesc, aGlyphSize, nullptr));
729 if (aNumVariations > 0) {
730 AutoRelease<CFDictionaryRef> varDict(CreateVariationTagDictionaryOrNull(
731 font, aNumVariations, aVariations));
732 if (varDict) {
733 CFDictionaryRef varAttr = CFDictionaryCreate(
734 nullptr, (const void**)&kCTFontVariationAttribute,
735 (const void**)&varDict, 1, &kCFTypeDictionaryKeyCallBacks,
736 &kCFTypeDictionaryValueCallBacks);
737 AutoRelease<CTFontDescriptorRef> fontDesc(
738 CTFontDescriptorCreateCopyWithAttributes(mFontDesc, varAttr));
739 if (!fontDesc) {
740 return nullptr;
742 font = CTFontCreateWithFontDescriptor(fontDesc, aGlyphSize, nullptr);
745 scaledFont = new ScaledFontMac(font, this, instanceData.mUseFontSmoothing,
746 instanceData.mApplySyntheticBold,
747 instanceData.mHasColorGlyphs);
748 } else {
749 CGFontRef fontRef = mFont;
750 if (aNumVariations > 0) {
751 CGFontRef varFont = CreateCGFontWithVariations(
752 mFont, mCGAxesCache, mCTAxesCache, aNumVariations, aVariations);
753 if (varFont) {
754 fontRef = varFont;
758 scaledFont = new ScaledFontMac(fontRef, this, aGlyphSize, fontRef != mFont,
759 instanceData.mUseFontSmoothing,
760 instanceData.mApplySyntheticBold,
761 instanceData.mHasColorGlyphs);
763 return scaledFont.forget();
766 already_AddRefed<ScaledFont> UnscaledFontMac::CreateScaledFontFromWRFont(
767 Float aGlyphSize, const wr::FontInstanceOptions* aOptions,
768 const wr::FontInstancePlatformOptions* aPlatformOptions,
769 const FontVariation* aVariations, uint32_t aNumVariations) {
770 ScaledFontMac::InstanceData instanceData(aOptions, aPlatformOptions);
771 return CreateScaledFont(aGlyphSize, reinterpret_cast<uint8_t*>(&instanceData),
772 sizeof(instanceData), aVariations, aNumVariations);
775 cairo_font_face_t* ScaledFontMac::CreateCairoFontFace(
776 cairo_font_options_t* aFontOptions) {
777 MOZ_ASSERT(mFont);
778 return cairo_quartz_font_face_create_for_cgfont(mFont);
781 already_AddRefed<UnscaledFont> UnscaledFontMac::CreateFromFontDescriptor(
782 const uint8_t* aData, uint32_t aDataLength, uint32_t aIndex) {
783 if (aDataLength == 0) {
784 gfxWarning() << "Mac font descriptor is truncated.";
785 return nullptr;
787 AutoRelease<CFStringRef> name(
788 CFStringCreateWithBytes(kCFAllocatorDefault, (const UInt8*)aData, aIndex,
789 kCFStringEncodingUTF8, false));
790 if (!name) {
791 return nullptr;
793 CGFontRef font = CGFontCreateWithFontName(name);
794 if (!font) {
795 return nullptr;
798 // If the descriptor included a font file path, apply that attribute and
799 // refresh the font in case it changed.
800 if (aIndex < aDataLength) {
801 AutoRelease<CFStringRef> path(CFStringCreateWithBytes(
802 kCFAllocatorDefault, (const UInt8*)aData + aIndex, aDataLength - aIndex,
803 kCFStringEncodingUTF8, false));
804 AutoRelease<CFURLRef> url(CFURLCreateWithFileSystemPath(
805 kCFAllocatorDefault, path, kCFURLPOSIXPathStyle, false));
806 AutoRelease<CFDictionaryRef> attrs(CFDictionaryCreate(
807 nullptr, (const void**)&kCTFontURLAttribute, (const void**)&url, 1,
808 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks));
809 AutoRelease<CTFontRef> ctFont(
810 CTFontCreateWithGraphicsFont(font, 0.0, nullptr, nullptr));
811 AutoRelease<CTFontDescriptorRef> desc(CTFontCopyFontDescriptor(ctFont));
812 AutoRelease<CTFontDescriptorRef> newDesc(
813 CTFontDescriptorCreateCopyWithAttributes(desc, attrs));
814 AutoRelease<CTFontRef> newFont(
815 CTFontCreateWithFontDescriptor(newDesc, 0.0, nullptr));
816 CFRelease(font);
817 font = CTFontCopyGraphicsFont(newFont, nullptr);
820 RefPtr<UnscaledFont> unscaledFont = new UnscaledFontMac(font);
821 CFRelease(font);
822 return unscaledFont.forget();
825 } // namespace gfx
826 } // namespace mozilla