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"
12 #include "skia/include/core/SkPaint.h"
13 #include "skia/include/core/SkPath.h"
14 #include "skia/include/ports/SkTypeface_mac.h"
17 #ifdef MOZ_WIDGET_UIKIT
18 # include <CoreFoundation/CoreFoundation.h>
20 #include "mozilla/gfx/Logging.h"
22 #ifdef MOZ_WIDGET_COCOA
23 // prototype for private API
25 CGPathRef
CGFontGetGlyphPath(CGFontRef fontRef
,
26 CGAffineTransform
* textTransform
, int unknown
,
31 #include "cairo-quartz.h"
36 // Simple helper class to automatically release a CFObject when it goes out
39 class AutoRelease final
{
41 explicit AutoRelease(T aObject
) : mObject(aObject
) {}
49 AutoRelease
<T
>& operator=(const T
& aObject
) {
50 if (aObject
!= mObject
) {
59 operator T() { return 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
,
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
));
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
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.
102 if (aInstalledFont
) {
103 AutoRelease
<CFDictionaryRef
> vars(CGFontCopyVariations(aCGFont
));
105 AutoRelease
<CFDictionaryRef
> varAttr(CFDictionaryCreate(
106 nullptr, (const void**)&kCTFontVariationAttribute
,
107 (const void**)&vars
, 1, &kCFTypeDictionaryKeyCallBacks
,
108 &kCFTypeDictionaryValueCallBacks
));
110 AutoRelease
<CTFontDescriptorRef
> varDesc(
112 ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc
, varAttr
)
113 : ::CTFontDescriptorCreateWithAttributes(varAttr
));
115 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, varDesc
);
117 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, nullptr);
120 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, nullptr);
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
),
132 mUseFontSmoothing(aUseFontSmoothing
),
133 mApplySyntheticBold(aApplySyntheticBold
),
134 mHasColorGlyphs(aHasColorGlyphs
) {
136 // XXX: should we be taking a reference
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
)),
151 mUseFontSmoothing(aUseFontSmoothing
),
152 mApplySyntheticBold(aApplySyntheticBold
),
153 mHasColorGlyphs(aHasColorGlyphs
) {
154 mFont
= CTFontCopyGraphicsFont(aFont
, nullptr);
159 ScaledFontMac::~ScaledFontMac() {
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
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) {
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) {
212 sum
+= CFSwapInt32BigToHost(*table
++);
216 // The length is not 4-byte aligned, but we still must process the remaining
219 // Pad with zero before adding to the checksum.
221 memcpy(&last
, end
, length
& 3);
222 sum
+= CFSwapInt32BigToHost(last
);
236 static int maxPow2LessThanEqual(int a
) {
239 while ((x
<< (shift
+ 1)) <= a
) {
245 struct writeBuf final
{
246 explicit writeBuf(int size
) {
247 this->data
= new unsigned char[size
];
250 ~writeBuf() { delete[] this->data
; }
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
;
264 while (this->offset
& 3) {
265 this->data
[this->offset
] = 0;
274 bool UnscaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback
,
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
];
282 offset
+= sizeof(uint32_t) * 3;
283 offset
+= sizeof(uint32_t) * 4 * count
;
285 for (CFIndex i
= 0; i
< count
; i
++) {
286 uint32_t tag
= (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags
, i
);
287 if (tag
== 0x43464620) { // 'CFF '
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
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
;
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;
310 records
[i
].length
= 0;
311 records
[i
].checkSum
= 0;
316 struct writeBuf
buf(offset
);
317 // write header/offset table
319 buf
.writeElement(CFSwapInt32HostToBig(0x4f54544f));
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
));
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
);
346 CFRelease(records
[i
].data
);
351 // clear the checksumAdjust field before checksumming the whole font
352 memset(&buf
.data
[checkSumAdjustmentOffset
], 0, sizeof(uint32_t));
353 uint32_t fontChecksum
= CFSwapInt32HostToBig(
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
);
366 bool UnscaledFontMac::GetFontDescriptor(FontDescriptorOutput aCb
,
372 AutoRelease
<CFStringRef
> psname(CGFontCopyPostScriptName(mFont
));
378 const char* cstr
= CFStringGetCStringPtr(psname
, kCFStringEncodingUTF8
);
380 if (!CFStringGetCString(psname
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) {
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
));
394 CFStringRef
urlStr(CFURLCopyFileSystemPath(fontUrl
, kCFURLPOSIXPathStyle
));
395 cstr
= CFStringGetCStringPtr(urlStr
, kCFStringEncodingUTF8
);
397 if (!CFStringGetCString(urlStr
, buf
, sizeof(buf
),
398 kCFStringEncodingUTF8
)) {
403 descriptor
.Append(cstr
);
406 aCb(reinterpret_cast<const uint8_t*>(descriptor
.get()), descriptor
.Length(),
411 static void CollectVariationsFromDictionary(const void* aKey
,
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()) {
421 if (CFNumberGetValue(static_cast<CFNumberRef
>(keyPtr
), kCFNumberSInt64Type
,
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
) {
435 AutoRelease
<CFDictionaryRef
> dict(CTFontCopyVariation(aCTFont
));
436 CFIndex count
= dict
? CFDictionaryGetCount(dict
) : 0;
438 aOutVariations
->reserve(count
);
439 CFDictionaryApplyFunction(dict
, CollectVariationsFromDictionary
,
445 bool ScaledFontMac::GetFontInstanceData(FontInstanceDataOutput aCb
,
447 // Collect any variation settings that were incorporated into the CTFont.
448 std::vector
<FontVariation
> variations
;
449 if (!GetVariationsForCTFont(mCTFont
, &variations
)) {
453 InstanceData
instance(this);
454 aCb(reinterpret_cast<uint8_t*>(&instance
), sizeof(instance
),
455 variations
.data(), variations
.size(), aBaton
);
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
);
483 ScaledFontMac::InstanceData::InstanceData(
484 const wr::FontInstanceOptions
* aOptions
,
485 const wr::FontInstancePlatformOptions
* aPlatformOptions
)
486 : mUseFontSmoothing(true),
487 mApplySyntheticBold(false),
488 mHasColorGlyphs(false) {
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
) {
506 aCGAxesCache
= CGFontCopyVariationAxes(aCGFont
);
512 AutoRelease
<CTFontRef
> ctFont(
513 CTFontCreateWithGraphicsFont(aCGFont
, 0, nullptr, nullptr));
514 aCTAxesCache
= CTFontCopyVariationAxes(ctFont
);
520 CFIndex axisCount
= CFArrayGetCount(aCTAxesCache
);
521 if (CFArrayGetCount(aCGAxesCache
) != axisCount
) {
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
)) {
540 CFDictionaryRef axis
= static_cast<CFDictionaryRef
>(axisInfo
);
543 CFDictionaryGetValue(axis
, kCTFontVariationAxisIdentifierKey
);
544 if (!axisTag
|| CFGetTypeID(axisTag
) != CFNumberGetTypeID()) {
548 if (!CFNumberGetValue(static_cast<CFNumberRef
>(axisTag
),
549 kCFNumberSInt64Type
, &tagLong
)) {
553 axisInfo
= CFArrayGetValueAtIndex(aCGAxesCache
, i
);
554 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo
)) {
557 CFTypeRef axisName
= CFDictionaryGetValue(
558 static_cast<CFDictionaryRef
>(axisInfo
), kCGFontVariationAxisName
);
559 if (!axisName
|| CFGetTypeID(axisName
) != CFStringGetTypeID()) {
563 // Clamp axis values to the supported range.
565 CFDictionaryGetValue(axis
, kCTFontVariationAxisMinimumValueKey
);
567 CFDictionaryGetValue(axis
, kCTFontVariationAxisMaximumValueKey
);
569 CFDictionaryGetValue(axis
, kCTFontVariationAxisDefaultValueKey
);
570 if (!min
|| CFGetTypeID(min
) != CFNumberGetTypeID() || !max
||
571 CFGetTypeID(max
) != CFNumberGetTypeID() || !def
||
572 CFGetTypeID(def
) != CFNumberGetTypeID()) {
578 if (!CFNumberGetValue(static_cast<CFNumberRef
>(min
), kCFNumberDoubleType
,
580 !CFNumberGetValue(static_cast<CFNumberRef
>(max
), kCFNumberDoubleType
,
582 !CFNumberGetValue(static_cast<CFNumberRef
>(def
), kCFNumberDoubleType
,
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
),
592 if (value
!= defDouble
) {
593 allDefaultValues
= false;
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.
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
)) {
633 CFDictionaryRef axis
= static_cast<CFDictionaryRef
>(axisInfo
);
636 CFDictionaryGetValue(axis
, kCTFontVariationAxisIdentifierKey
);
637 if (!axisTag
|| CFGetTypeID(axisTag
) != CFNumberGetTypeID()) {
641 if (!CFNumberGetValue(static_cast<CFNumberRef
>(axisTag
),
642 kCFNumberSInt64Type
, &tagLong
)) {
646 // Clamp axis values to the supported range.
648 CFDictionaryGetValue(axis
, kCTFontVariationAxisMinimumValueKey
);
650 CFDictionaryGetValue(axis
, kCTFontVariationAxisMaximumValueKey
);
652 CFDictionaryGetValue(axis
, kCTFontVariationAxisDefaultValueKey
);
653 if (!min
|| CFGetTypeID(min
) != CFNumberGetTypeID() || !max
||
654 CFGetTypeID(max
) != CFNumberGetTypeID() || !def
||
655 CFGetTypeID(def
) != CFNumberGetTypeID()) {
661 if (!CFNumberGetValue(static_cast<CFNumberRef
>(min
), kCFNumberDoubleType
,
663 !CFNumberGetValue(static_cast<CFNumberRef
>(max
), kCFNumberDoubleType
,
665 !CFNumberGetValue(static_cast<CFNumberRef
>(def
), kCFNumberDoubleType
,
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
),
675 if (value
!= defDouble
) {
676 allDefaultValues
= false;
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.
692 return dict
.forget();
696 CGFontRef
UnscaledFontMac::CreateCGFontWithVariations(
697 CGFontRef aFont
, CFArrayRef
& aCGAxesCache
, CFArrayRef
& aCTAxesCache
,
698 uint32_t aVariationCount
, const FontVariation
* aVariations
) {
699 if (!aVariationCount
) {
702 MOZ_ASSERT(aVariations
);
704 AutoRelease
<CFDictionaryRef
> varDict(CreateVariationDictionaryOrNull(
705 aFont
, aCGAxesCache
, aCTAxesCache
, aVariationCount
, aVariations
));
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.";
723 const ScaledFontMac::InstanceData
& instanceData
=
724 *reinterpret_cast<const ScaledFontMac::InstanceData
*>(aInstanceData
);
725 RefPtr
<ScaledFontMac
> scaledFont
;
727 AutoRelease
<CTFontRef
> font(
728 CTFontCreateWithFontDescriptor(mFontDesc
, aGlyphSize
, nullptr));
729 if (aNumVariations
> 0) {
730 AutoRelease
<CFDictionaryRef
> varDict(CreateVariationTagDictionaryOrNull(
731 font
, aNumVariations
, aVariations
));
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
));
742 font
= CTFontCreateWithFontDescriptor(fontDesc
, aGlyphSize
, nullptr);
745 scaledFont
= new ScaledFontMac(font
, this, instanceData
.mUseFontSmoothing
,
746 instanceData
.mApplySyntheticBold
,
747 instanceData
.mHasColorGlyphs
);
749 CGFontRef fontRef
= mFont
;
750 if (aNumVariations
> 0) {
751 CGFontRef varFont
= CreateCGFontWithVariations(
752 mFont
, mCGAxesCache
, mCTAxesCache
, aNumVariations
, aVariations
);
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
) {
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.";
787 AutoRelease
<CFStringRef
> name(
788 CFStringCreateWithBytes(kCFAllocatorDefault
, (const UInt8
*)aData
, aIndex
,
789 kCFStringEncodingUTF8
, false));
793 CGFontRef font
= CGFontCreateWithFontName(name
);
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));
817 font
= CTFontCopyGraphicsFont(newFont
, nullptr);
820 RefPtr
<UnscaledFont
> unscaledFont
= new UnscaledFontMac(font
);
822 return unscaledFont
.forget();
826 } // namespace mozilla