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 #ifdef MOZ_WIDGET_COCOA
11 # include "nsCocoaFeatures.h"
14 #include "skia/include/core/SkPaint.h"
15 #include "skia/include/core/SkPath.h"
16 #include "skia/include/ports/SkTypeface_mac.h"
19 #ifdef MOZ_WIDGET_UIKIT
20 # include <CoreFoundation/CoreFoundation.h>
22 #include "mozilla/gfx/Logging.h"
24 #ifdef MOZ_WIDGET_COCOA
25 // prototype for private API
27 CGPathRef
CGFontGetGlyphPath(CGFontRef fontRef
,
28 CGAffineTransform
* textTransform
, int unknown
,
33 #include "cairo-quartz.h"
38 // Simple helper class to automatically release a CFObject when it goes out
41 class AutoRelease final
{
43 explicit AutoRelease(T aObject
) : mObject(aObject
) {}
51 AutoRelease
<T
>& operator=(const T
& aObject
) {
52 if (aObject
!= mObject
) {
61 operator T() { return mObject
; }
73 // Helper to create a CTFont from a CGFont, copying any variations that were
74 // set on the CGFont, and applying attributes from (optional) aFontDesc.
75 CTFontRef
CreateCTFontFromCGFontWithVariations(CGFontRef aCGFont
, CGFloat aSize
,
77 CTFontDescriptorRef aFontDesc
) {
78 #ifdef MOZ_WIDGET_COCOA
79 // New implementation (see bug 1856035) for macOS 13+.
80 if (nsCocoaFeatures::OnVenturaOrLater()) {
81 // Create CTFont, applying any descriptor that was passed (used by
82 // gfxCoreTextShaper to set features).
83 AutoRelease
<CTFontRef
> ctFont(
84 CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, aFontDesc
));
85 AutoRelease
<CFDictionaryRef
> vars(CGFontCopyVariations(aCGFont
));
87 // Create an attribute dictionary containing the variations.
88 AutoRelease
<CFDictionaryRef
> attrs(CFDictionaryCreate(
89 nullptr, (const void**)&kCTFontVariationAttribute
,
90 (const void**)&vars
, 1, &kCFTypeDictionaryKeyCallBacks
,
91 &kCFTypeDictionaryValueCallBacks
));
92 // Get the original descriptor from the CTFont, then add the variations
94 AutoRelease
<CTFontDescriptorRef
> desc(CTFontCopyFontDescriptor(ctFont
));
95 desc
= CTFontDescriptorCreateCopyWithAttributes(desc
, attrs
);
96 // Return a copy of the font that has the variations added.
97 return CTFontCreateCopyWithAttributes(ctFont
, 0.0, nullptr, desc
);
99 // No variations to set, just return the default CTFont.
100 return ctFont
.forget();
104 // Older implementation used up to macOS 12.
106 if (aInstalledFont
) {
107 AutoRelease
<CFDictionaryRef
> vars(CGFontCopyVariations(aCGFont
));
109 AutoRelease
<CFDictionaryRef
> varAttr(CFDictionaryCreate(
110 nullptr, (const void**)&kCTFontVariationAttribute
,
111 (const void**)&vars
, 1, &kCFTypeDictionaryKeyCallBacks
,
112 &kCFTypeDictionaryValueCallBacks
));
114 AutoRelease
<CTFontDescriptorRef
> varDesc(
116 ? ::CTFontDescriptorCreateCopyWithAttributes(aFontDesc
, varAttr
)
117 : ::CTFontDescriptorCreateWithAttributes(varAttr
));
119 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, varDesc
);
121 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, nullptr);
124 ctFont
= CTFontCreateWithGraphicsFont(aCGFont
, aSize
, nullptr, nullptr);
129 ScaledFontMac::ScaledFontMac(CGFontRef aFont
,
130 const RefPtr
<UnscaledFont
>& aUnscaledFont
,
131 Float aSize
, bool aOwnsFont
,
132 bool aUseFontSmoothing
, bool aApplySyntheticBold
,
133 bool aHasColorGlyphs
)
134 : ScaledFontBase(aUnscaledFont
, aSize
),
136 mUseFontSmoothing(aUseFontSmoothing
),
137 mApplySyntheticBold(aApplySyntheticBold
),
138 mHasColorGlyphs(aHasColorGlyphs
) {
140 // XXX: should we be taking a reference
144 auto unscaledMac
= static_cast<UnscaledFontMac
*>(aUnscaledFont
.get());
145 bool dataFont
= unscaledMac
->IsDataFont();
146 mCTFont
= CreateCTFontFromCGFontWithVariations(aFont
, aSize
, !dataFont
);
149 ScaledFontMac::ScaledFontMac(CTFontRef aFont
,
150 const RefPtr
<UnscaledFont
>& aUnscaledFont
,
151 bool aUseFontSmoothing
, bool aApplySyntheticBold
,
152 bool aHasColorGlyphs
)
153 : ScaledFontBase(aUnscaledFont
, CTFontGetSize(aFont
)),
155 mUseFontSmoothing(aUseFontSmoothing
),
156 mApplySyntheticBold(aApplySyntheticBold
),
157 mHasColorGlyphs(aHasColorGlyphs
) {
158 mFont
= CTFontCopyGraphicsFont(aFont
, nullptr);
163 ScaledFontMac::~ScaledFontMac() {
165 CGFontRelease(mFont
);
168 SkTypeface
* ScaledFontMac::CreateSkTypeface() {
169 return SkMakeTypefaceFromCTFont(mCTFont
).release();
172 void ScaledFontMac::SetupSkFontDrawOptions(SkFont
& aFont
) {
173 aFont
.setSubpixel(true);
175 // Normally, Skia enables LCD FontSmoothing which creates thicker fonts
176 // and also enables subpixel AA. CoreGraphics without font smoothing
177 // explicitly creates thinner fonts and grayscale AA.
178 // CoreGraphics doesn't support a configuration that produces thicker
179 // fonts with grayscale AA as LCD Font Smoothing enables or disables
180 // both. However, Skia supports it by enabling font smoothing (producing
181 // subpixel AA) and converts it to grayscale AA. Since Skia doesn't
182 // support subpixel AA on transparent backgrounds, we still want font
183 // smoothing for the thicker fonts, even if it is grayscale AA.
185 // With explicit Grayscale AA (from -moz-osx-font-smoothing:grayscale),
186 // we want to have grayscale AA with no smoothing at all. This means
187 // disabling the LCD font smoothing behaviour.
188 // To accomplish this we have to explicitly disable hinting,
189 // and disable LCDRenderText.
190 if (aFont
.getEdging() == SkFont::Edging::kAntiAlias
&& !mUseFontSmoothing
) {
191 aFont
.setHinting(SkFontHinting::kNone
);
195 // private API here are the public options on OS X
196 // CTFontCreatePathForGlyph
197 // ATSUGlyphGetCubicPaths
198 // we've used this in cairo sucessfully for some time.
199 // Note: cairo dlsyms it. We could do that but maybe it's
202 already_AddRefed
<Path
> ScaledFontMac::GetPathForGlyphs(
203 const GlyphBuffer
& aBuffer
, const DrawTarget
* aTarget
) {
204 return ScaledFontBase::GetPathForGlyphs(aBuffer
, aTarget
);
207 static uint32_t CalcTableChecksum(const uint32_t* tableStart
, uint32_t length
,
208 bool skipChecksumAdjust
= false) {
210 const uint32_t* table
= tableStart
;
211 const uint32_t* end
= table
+ length
/ sizeof(uint32_t);
212 while (table
< end
) {
213 if (skipChecksumAdjust
&& (table
- tableStart
) == 2) {
216 sum
+= CFSwapInt32BigToHost(*table
++);
220 // The length is not 4-byte aligned, but we still must process the remaining
223 // Pad with zero before adding to the checksum.
225 memcpy(&last
, end
, length
& 3);
226 sum
+= CFSwapInt32BigToHost(last
);
240 static int maxPow2LessThanEqual(int a
) {
243 while ((x
<< (shift
+ 1)) <= a
) {
249 struct writeBuf final
{
250 explicit writeBuf(int size
) {
251 this->data
= new unsigned char[size
];
254 ~writeBuf() { delete[] this->data
; }
257 void writeElement(T a
) {
258 *reinterpret_cast<T
*>(&this->data
[this->offset
]) = a
;
259 this->offset
+= sizeof(T
);
262 void writeMem(const void* data
, unsigned long length
) {
263 memcpy(&this->data
[this->offset
], data
, length
);
264 this->offset
+= length
;
268 while (this->offset
& 3) {
269 this->data
[this->offset
] = 0;
278 bool UnscaledFontMac::GetFontFileData(FontFileDataOutput aDataCallback
,
280 // We'll reconstruct a TTF font from the tables we can get from the CGFont
281 CFArrayRef tags
= CGFontCopyTableTags(mFont
);
282 CFIndex count
= CFArrayGetCount(tags
);
284 TableRecord
* records
= new TableRecord
[count
];
286 offset
+= sizeof(uint32_t) * 3;
287 offset
+= sizeof(uint32_t) * 4 * count
;
289 for (CFIndex i
= 0; i
< count
; i
++) {
290 uint32_t tag
= (uint32_t)(uintptr_t)CFArrayGetValueAtIndex(tags
, i
);
291 if (tag
== 0x43464620) { // 'CFF '
294 CFDataRef data
= CGFontCopyTableForTag(mFont
, tag
);
295 // Bug 1602391 suggests CGFontCopyTableForTag can fail, even though we just
296 // got the tag from the font via CGFontCopyTableTags above. If we can catch
297 // this (e.g. in fuzz-testing) it'd be good to understand when it happens,
298 // but in any case we'll handle it safely below by treating the table as
300 MOZ_ASSERT(data
, "failed to get font table data");
301 records
[i
].tag
= tag
;
302 records
[i
].offset
= offset
;
303 records
[i
].data
= data
;
305 records
[i
].length
= CFDataGetLength(data
);
306 bool skipChecksumAdjust
= (tag
== 0x68656164); // 'head'
307 records
[i
].checkSum
= CalcTableChecksum(
308 reinterpret_cast<const uint32_t*>(CFDataGetBytePtr(data
)),
309 records
[i
].length
, skipChecksumAdjust
);
310 offset
+= records
[i
].length
;
311 // 32 bit align the tables
312 offset
= (offset
+ 3) & ~3;
314 records
[i
].length
= 0;
315 records
[i
].checkSum
= 0;
320 struct writeBuf
buf(offset
);
321 // write header/offset table
323 buf
.writeElement(CFSwapInt32HostToBig(0x4f54544f));
325 buf
.writeElement(CFSwapInt32HostToBig(0x00010000));
327 buf
.writeElement(CFSwapInt16HostToBig(count
));
328 int maxPow2Count
= maxPow2LessThanEqual(count
);
329 buf
.writeElement(CFSwapInt16HostToBig((1 << maxPow2Count
) * 16));
330 buf
.writeElement(CFSwapInt16HostToBig(maxPow2Count
));
331 buf
.writeElement(CFSwapInt16HostToBig((count
- (1 << maxPow2Count
)) * 16));
333 // write table record entries
334 for (CFIndex i
= 0; i
< count
; i
++) {
335 buf
.writeElement(CFSwapInt32HostToBig(records
[i
].tag
));
336 buf
.writeElement(CFSwapInt32HostToBig(records
[i
].checkSum
));
337 buf
.writeElement(CFSwapInt32HostToBig(records
[i
].offset
));
338 buf
.writeElement(CFSwapInt32HostToBig(records
[i
].length
));
342 int checkSumAdjustmentOffset
= 0;
343 for (CFIndex i
= 0; i
< count
; i
++) {
344 if (records
[i
].tag
== 0x68656164) {
345 checkSumAdjustmentOffset
= buf
.offset
+ 2 * 4;
347 if (records
[i
].data
) {
348 buf
.writeMem(CFDataGetBytePtr(records
[i
].data
), records
[i
].length
);
350 CFRelease(records
[i
].data
);
355 // clear the checksumAdjust field before checksumming the whole font
356 memset(&buf
.data
[checkSumAdjustmentOffset
], 0, sizeof(uint32_t));
357 uint32_t fontChecksum
= CFSwapInt32HostToBig(
359 CalcTableChecksum(reinterpret_cast<const uint32_t*>(buf
.data
), offset
));
360 // set checkSumAdjust to the computed checksum
361 memcpy(&buf
.data
[checkSumAdjustmentOffset
], &fontChecksum
,
362 sizeof(fontChecksum
));
364 // we always use an index of 0
365 aDataCallback(buf
.data
, buf
.offset
, 0, aBaton
);
370 bool UnscaledFontMac::GetFontDescriptor(FontDescriptorOutput aCb
,
376 AutoRelease
<CFStringRef
> psname(CGFontCopyPostScriptName(mFont
));
382 const char* cstr
= CFStringGetCStringPtr(psname
, kCFStringEncodingUTF8
);
384 if (!CFStringGetCString(psname
, buf
, sizeof(buf
), kCFStringEncodingUTF8
)) {
390 nsAutoCString
descriptor(cstr
);
391 uint32_t psNameLen
= descriptor
.Length();
393 AutoRelease
<CTFontRef
> ctFont(
394 CTFontCreateWithGraphicsFont(mFont
, 0, nullptr, nullptr));
395 AutoRelease
<CFURLRef
> fontUrl(
396 (CFURLRef
)CTFontCopyAttribute(ctFont
, kCTFontURLAttribute
));
398 CFStringRef
urlStr(CFURLCopyFileSystemPath(fontUrl
, kCFURLPOSIXPathStyle
));
399 cstr
= CFStringGetCStringPtr(urlStr
, kCFStringEncodingUTF8
);
401 if (!CFStringGetCString(urlStr
, buf
, sizeof(buf
),
402 kCFStringEncodingUTF8
)) {
407 descriptor
.Append(cstr
);
410 aCb(reinterpret_cast<const uint8_t*>(descriptor
.get()), descriptor
.Length(),
415 static void CollectVariationsFromDictionary(const void* aKey
,
418 auto keyPtr
= static_cast<const CFTypeRef
>(aKey
);
419 auto valuePtr
= static_cast<const CFTypeRef
>(aValue
);
420 auto outVariations
= static_cast<std::vector
<FontVariation
>*>(aContext
);
421 if (CFGetTypeID(keyPtr
) == CFNumberGetTypeID() &&
422 CFGetTypeID(valuePtr
) == CFNumberGetTypeID()) {
425 if (CFNumberGetValue(static_cast<CFNumberRef
>(keyPtr
), kCFNumberSInt64Type
,
427 CFNumberGetValue(static_cast<CFNumberRef
>(valuePtr
),
428 kCFNumberDoubleType
, &v
)) {
429 outVariations
->push_back(FontVariation
{uint32_t(t
), float(v
)});
434 static bool GetVariationsForCTFont(CTFontRef aCTFont
,
435 std::vector
<FontVariation
>* aOutVariations
) {
439 AutoRelease
<CFDictionaryRef
> dict(CTFontCopyVariation(aCTFont
));
440 CFIndex count
= dict
? CFDictionaryGetCount(dict
) : 0;
442 aOutVariations
->reserve(count
);
443 CFDictionaryApplyFunction(dict
, CollectVariationsFromDictionary
,
449 bool ScaledFontMac::GetFontInstanceData(FontInstanceDataOutput aCb
,
451 // Collect any variation settings that were incorporated into the CTFont.
452 std::vector
<FontVariation
> variations
;
453 if (!GetVariationsForCTFont(mCTFont
, &variations
)) {
457 InstanceData
instance(this);
458 aCb(reinterpret_cast<uint8_t*>(&instance
), sizeof(instance
),
459 variations
.data(), variations
.size(), aBaton
);
463 bool ScaledFontMac::GetWRFontInstanceOptions(
464 Maybe
<wr::FontInstanceOptions
>* aOutOptions
,
465 Maybe
<wr::FontInstancePlatformOptions
>* aOutPlatformOptions
,
466 std::vector
<FontVariation
>* aOutVariations
) {
467 GetVariationsForCTFont(mCTFont
, aOutVariations
);
469 wr::FontInstanceOptions options
;
470 options
.render_mode
= wr::FontRenderMode::Subpixel
;
471 options
.flags
= wr::FontInstanceFlags::SUBPIXEL_POSITION
;
472 if (mUseFontSmoothing
) {
473 options
.flags
|= wr::FontInstanceFlags::FONT_SMOOTHING
;
475 if (mApplySyntheticBold
) {
476 options
.flags
|= wr::FontInstanceFlags::SYNTHETIC_BOLD
;
478 if (mHasColorGlyphs
) {
479 options
.flags
|= wr::FontInstanceFlags::EMBEDDED_BITMAPS
;
481 options
.synthetic_italics
=
482 wr::DegreesToSyntheticItalics(GetSyntheticObliqueAngle());
483 *aOutOptions
= Some(options
);
487 ScaledFontMac::InstanceData::InstanceData(
488 const wr::FontInstanceOptions
* aOptions
,
489 const wr::FontInstancePlatformOptions
* aPlatformOptions
)
490 : mUseFontSmoothing(true),
491 mApplySyntheticBold(false),
492 mHasColorGlyphs(false) {
494 if (!(aOptions
->flags
& wr::FontInstanceFlags::FONT_SMOOTHING
)) {
495 mUseFontSmoothing
= false;
497 if (aOptions
->flags
& wr::FontInstanceFlags::SYNTHETIC_BOLD
) {
498 mApplySyntheticBold
= true;
500 if (aOptions
->flags
& wr::FontInstanceFlags::EMBEDDED_BITMAPS
) {
501 mHasColorGlyphs
= true;
506 static CFDictionaryRef
CreateVariationDictionaryOrNull(
507 CGFontRef aCGFont
, CFArrayRef
& aCGAxesCache
, CFArrayRef
& aCTAxesCache
,
508 uint32_t aVariationCount
, const FontVariation
* aVariations
) {
510 aCGAxesCache
= CGFontCopyVariationAxes(aCGFont
);
516 AutoRelease
<CTFontRef
> ctFont(
517 CTFontCreateWithGraphicsFont(aCGFont
, 0, nullptr, nullptr));
518 aCTAxesCache
= CTFontCopyVariationAxes(ctFont
);
524 CFIndex axisCount
= CFArrayGetCount(aCTAxesCache
);
525 if (CFArrayGetCount(aCGAxesCache
) != axisCount
) {
529 AutoRelease
<CFMutableDictionaryRef
> dict(CFDictionaryCreateMutable(
530 kCFAllocatorDefault
, axisCount
, &kCFTypeDictionaryKeyCallBacks
,
531 &kCFTypeDictionaryValueCallBacks
));
533 // Number of variation settings passed in the aVariations parameter.
534 // This will typically be a very low value, so we just linear-search them.
535 bool allDefaultValues
= true;
537 for (CFIndex i
= 0; i
< axisCount
; ++i
) {
538 // We sanity-check the axis info found in the CTFont, and bail out
539 // (returning null) if it doesn't have the expected types.
540 CFTypeRef axisInfo
= CFArrayGetValueAtIndex(aCTAxesCache
, i
);
541 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo
)) {
544 CFDictionaryRef axis
= static_cast<CFDictionaryRef
>(axisInfo
);
547 CFDictionaryGetValue(axis
, kCTFontVariationAxisIdentifierKey
);
548 if (!axisTag
|| CFGetTypeID(axisTag
) != CFNumberGetTypeID()) {
552 if (!CFNumberGetValue(static_cast<CFNumberRef
>(axisTag
),
553 kCFNumberSInt64Type
, &tagLong
)) {
557 axisInfo
= CFArrayGetValueAtIndex(aCGAxesCache
, i
);
558 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo
)) {
561 CFTypeRef axisName
= CFDictionaryGetValue(
562 static_cast<CFDictionaryRef
>(axisInfo
), kCGFontVariationAxisName
);
563 if (!axisName
|| CFGetTypeID(axisName
) != CFStringGetTypeID()) {
567 // Clamp axis values to the supported range.
569 CFDictionaryGetValue(axis
, kCTFontVariationAxisMinimumValueKey
);
571 CFDictionaryGetValue(axis
, kCTFontVariationAxisMaximumValueKey
);
573 CFDictionaryGetValue(axis
, kCTFontVariationAxisDefaultValueKey
);
574 if (!min
|| CFGetTypeID(min
) != CFNumberGetTypeID() || !max
||
575 CFGetTypeID(max
) != CFNumberGetTypeID() || !def
||
576 CFGetTypeID(def
) != CFNumberGetTypeID()) {
582 if (!CFNumberGetValue(static_cast<CFNumberRef
>(min
), kCFNumberDoubleType
,
584 !CFNumberGetValue(static_cast<CFNumberRef
>(max
), kCFNumberDoubleType
,
586 !CFNumberGetValue(static_cast<CFNumberRef
>(def
), kCFNumberDoubleType
,
591 double value
= defDouble
;
592 for (uint32_t j
= 0; j
< aVariationCount
; ++j
) {
593 if (aVariations
[j
].mTag
== tagLong
) {
594 value
= std::min(std::max
<double>(aVariations
[j
].mValue
, minDouble
),
596 if (value
!= defDouble
) {
597 allDefaultValues
= false;
602 AutoRelease
<CFNumberRef
> valueNumber(
603 CFNumberCreate(kCFAllocatorDefault
, kCFNumberDoubleType
, &value
));
604 CFDictionaryAddValue(dict
, axisName
, valueNumber
);
607 if (allDefaultValues
) {
608 // We didn't actually set any non-default values, so throw away the
609 // variations dictionary and just use the default rendering.
613 return dict
.forget();
616 static CFDictionaryRef
CreateVariationTagDictionaryOrNull(
617 CTFontRef aCTFont
, uint32_t aVariationCount
,
618 const FontVariation
* aVariations
) {
619 AutoRelease
<CFArrayRef
> axes(CTFontCopyVariationAxes(aCTFont
));
620 CFIndex axisCount
= CFArrayGetCount(axes
);
622 AutoRelease
<CFMutableDictionaryRef
> dict(CFDictionaryCreateMutable(
623 kCFAllocatorDefault
, axisCount
, &kCFTypeDictionaryKeyCallBacks
,
624 &kCFTypeDictionaryValueCallBacks
));
626 // Number of variation settings passed in the aVariations parameter.
627 // This will typically be a very low value, so we just linear-search them.
628 bool allDefaultValues
= true;
630 for (CFIndex i
= 0; i
< axisCount
; ++i
) {
631 // We sanity-check the axis info found in the CTFont, and bail out
632 // (returning null) if it doesn't have the expected types.
633 CFTypeRef axisInfo
= CFArrayGetValueAtIndex(axes
, i
);
634 if (CFDictionaryGetTypeID() != CFGetTypeID(axisInfo
)) {
637 CFDictionaryRef axis
= static_cast<CFDictionaryRef
>(axisInfo
);
640 CFDictionaryGetValue(axis
, kCTFontVariationAxisIdentifierKey
);
641 if (!axisTag
|| CFGetTypeID(axisTag
) != CFNumberGetTypeID()) {
645 if (!CFNumberGetValue(static_cast<CFNumberRef
>(axisTag
),
646 kCFNumberSInt64Type
, &tagLong
)) {
650 // Clamp axis values to the supported range.
652 CFDictionaryGetValue(axis
, kCTFontVariationAxisMinimumValueKey
);
654 CFDictionaryGetValue(axis
, kCTFontVariationAxisMaximumValueKey
);
656 CFDictionaryGetValue(axis
, kCTFontVariationAxisDefaultValueKey
);
657 if (!min
|| CFGetTypeID(min
) != CFNumberGetTypeID() || !max
||
658 CFGetTypeID(max
) != CFNumberGetTypeID() || !def
||
659 CFGetTypeID(def
) != CFNumberGetTypeID()) {
665 if (!CFNumberGetValue(static_cast<CFNumberRef
>(min
), kCFNumberDoubleType
,
667 !CFNumberGetValue(static_cast<CFNumberRef
>(max
), kCFNumberDoubleType
,
669 !CFNumberGetValue(static_cast<CFNumberRef
>(def
), kCFNumberDoubleType
,
674 double value
= defDouble
;
675 for (uint32_t j
= 0; j
< aVariationCount
; ++j
) {
676 if (aVariations
[j
].mTag
== tagLong
) {
677 value
= std::min(std::max
<double>(aVariations
[j
].mValue
, minDouble
),
679 if (value
!= defDouble
) {
680 allDefaultValues
= false;
685 AutoRelease
<CFNumberRef
> valueNumber(
686 CFNumberCreate(kCFAllocatorDefault
, kCFNumberDoubleType
, &value
));
687 CFDictionaryAddValue(dict
, axisTag
, valueNumber
);
690 if (allDefaultValues
) {
691 // We didn't actually set any non-default values, so throw away the
692 // variations dictionary and just use the default rendering.
696 return dict
.forget();
700 CGFontRef
UnscaledFontMac::CreateCGFontWithVariations(
701 CGFontRef aFont
, CFArrayRef
& aCGAxesCache
, CFArrayRef
& aCTAxesCache
,
702 uint32_t aVariationCount
, const FontVariation
* aVariations
) {
703 if (!aVariationCount
) {
706 MOZ_ASSERT(aVariations
);
708 AutoRelease
<CFDictionaryRef
> varDict(CreateVariationDictionaryOrNull(
709 aFont
, aCGAxesCache
, aCTAxesCache
, aVariationCount
, aVariations
));
714 return CGFontCreateCopyWithVariations(aFont
, varDict
);
717 already_AddRefed
<ScaledFont
> UnscaledFontMac::CreateScaledFont(
718 Float aGlyphSize
, const uint8_t* aInstanceData
,
719 uint32_t aInstanceDataLength
, const FontVariation
* aVariations
,
720 uint32_t aNumVariations
)
723 if (aInstanceDataLength
< sizeof(ScaledFontMac::InstanceData
)) {
724 gfxWarning() << "Mac scaled font instance data is truncated.";
727 const ScaledFontMac::InstanceData
& instanceData
=
728 *reinterpret_cast<const ScaledFontMac::InstanceData
*>(aInstanceData
);
729 RefPtr
<ScaledFontMac
> scaledFont
;
731 AutoRelease
<CTFontRef
> font(
732 CTFontCreateWithFontDescriptor(mFontDesc
, aGlyphSize
, nullptr));
733 if (aNumVariations
> 0) {
734 AutoRelease
<CFDictionaryRef
> varDict(CreateVariationTagDictionaryOrNull(
735 font
, aNumVariations
, aVariations
));
737 CFDictionaryRef varAttr
= CFDictionaryCreate(
738 nullptr, (const void**)&kCTFontVariationAttribute
,
739 (const void**)&varDict
, 1, &kCFTypeDictionaryKeyCallBacks
,
740 &kCFTypeDictionaryValueCallBacks
);
741 AutoRelease
<CTFontDescriptorRef
> fontDesc(
742 CTFontDescriptorCreateCopyWithAttributes(mFontDesc
, varAttr
));
746 font
= CTFontCreateWithFontDescriptor(fontDesc
, aGlyphSize
, nullptr);
749 scaledFont
= new ScaledFontMac(font
, this, instanceData
.mUseFontSmoothing
,
750 instanceData
.mApplySyntheticBold
,
751 instanceData
.mHasColorGlyphs
);
753 CGFontRef fontRef
= mFont
;
754 if (aNumVariations
> 0) {
755 CGFontRef varFont
= CreateCGFontWithVariations(
756 mFont
, mCGAxesCache
, mCTAxesCache
, aNumVariations
, aVariations
);
762 scaledFont
= new ScaledFontMac(fontRef
, this, aGlyphSize
, fontRef
!= mFont
,
763 instanceData
.mUseFontSmoothing
,
764 instanceData
.mApplySyntheticBold
,
765 instanceData
.mHasColorGlyphs
);
767 return scaledFont
.forget();
770 already_AddRefed
<ScaledFont
> UnscaledFontMac::CreateScaledFontFromWRFont(
771 Float aGlyphSize
, const wr::FontInstanceOptions
* aOptions
,
772 const wr::FontInstancePlatformOptions
* aPlatformOptions
,
773 const FontVariation
* aVariations
, uint32_t aNumVariations
) {
774 ScaledFontMac::InstanceData
instanceData(aOptions
, aPlatformOptions
);
775 return CreateScaledFont(aGlyphSize
, reinterpret_cast<uint8_t*>(&instanceData
),
776 sizeof(instanceData
), aVariations
, aNumVariations
);
779 cairo_font_face_t
* ScaledFontMac::CreateCairoFontFace(
780 cairo_font_options_t
* aFontOptions
) {
782 return cairo_quartz_font_face_create_for_cgfont(mFont
);
785 already_AddRefed
<UnscaledFont
> UnscaledFontMac::CreateFromFontDescriptor(
786 const uint8_t* aData
, uint32_t aDataLength
, uint32_t aIndex
) {
787 if (aDataLength
== 0) {
788 gfxWarning() << "Mac font descriptor is truncated.";
791 AutoRelease
<CFStringRef
> name(
792 CFStringCreateWithBytes(kCFAllocatorDefault
, (const UInt8
*)aData
, aIndex
,
793 kCFStringEncodingUTF8
, false));
797 CGFontRef font
= CGFontCreateWithFontName(name
);
802 // If the descriptor included a font file path, apply that attribute and
803 // refresh the font in case it changed.
804 if (aIndex
< aDataLength
) {
805 AutoRelease
<CFStringRef
> path(CFStringCreateWithBytes(
806 kCFAllocatorDefault
, (const UInt8
*)aData
+ aIndex
, aDataLength
- aIndex
,
807 kCFStringEncodingUTF8
, false));
808 AutoRelease
<CFURLRef
> url(CFURLCreateWithFileSystemPath(
809 kCFAllocatorDefault
, path
, kCFURLPOSIXPathStyle
, false));
810 AutoRelease
<CFDictionaryRef
> attrs(CFDictionaryCreate(
811 nullptr, (const void**)&kCTFontURLAttribute
, (const void**)&url
, 1,
812 &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
));
813 AutoRelease
<CTFontRef
> ctFont(
814 CTFontCreateWithGraphicsFont(font
, 0.0, nullptr, nullptr));
815 AutoRelease
<CTFontDescriptorRef
> desc(CTFontCopyFontDescriptor(ctFont
));
816 AutoRelease
<CTFontDescriptorRef
> newDesc(
817 CTFontDescriptorCreateCopyWithAttributes(desc
, attrs
));
818 AutoRelease
<CTFontRef
> newFont(
819 CTFontCreateWithFontDescriptor(newDesc
, 0.0, nullptr));
821 font
= CTFontCopyGraphicsFont(newFont
, nullptr);
824 RefPtr
<UnscaledFont
> unscaledFont
= new UnscaledFontMac(font
);
826 return unscaledFont
.forget();
830 } // namespace mozilla