1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
21 #include <sal/log.hxx>
23 #include <tools/long.hxx>
26 #include <quartz/SystemFontList.hxx>
27 #include <impfont.hxx>
29 #include <osx/saldata.hxx>
30 #include <osx/salinst.h>
32 #include <fontattributes.hxx>
33 #include <font/PhysicalFontCollection.hxx>
34 #include <quartz/CoreTextFontFace.hxx>
35 #include <quartz/salgdi.h>
36 #include <quartz/utils.h>
37 #include <sallayout.hxx>
40 FontAttributes
DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD
, bool* bFontEnabled
)
42 // all CoreText fonts are device fonts that can rotate just fine
46 // reset the font attributes
47 rDFA
.SetFamilyType( FAMILY_DONTKNOW
);
48 rDFA
.SetPitch( PITCH_VARIABLE
);
49 rDFA
.SetWidthType( WIDTH_NORMAL
);
50 rDFA
.SetWeight( WEIGHT_NORMAL
);
51 rDFA
.SetItalic( ITALIC_NONE
);
52 rDFA
.SetMicrosoftSymbolEncoded( false );
56 CFStringRef pLang
= nullptr;
57 CFStringRef pFamilyName
= static_cast<CFStringRef
>(
58 CTFontDescriptorCopyLocalizedAttribute( pFD
, kCTFontFamilyNameAttribute
, &pLang
));
64 CFRelease( pFamilyName
);
66 pFamilyName
= static_cast<CFStringRef
>(CTFontDescriptorCopyAttribute( pFD
, kCTFontFamilyNameAttribute
));
69 // No "Application" on iOS. And it is unclear whether this code
70 // snippet will actually ever get invoked on iOS anyway. So just
71 // use the old code that uses a non-localized font name.
72 CFStringRef pFamilyName
= (CFStringRef
)CTFontDescriptorCopyAttribute( pFD
, kCTFontFamilyNameAttribute
);
75 rDFA
.SetFamilyName( GetOUString( pFamilyName
) );
78 CFStringRef pStyleName
= static_cast<CFStringRef
>(CTFontDescriptorCopyAttribute( pFD
, kCTFontStyleNameAttribute
));
79 rDFA
.SetStyleName( GetOUString( pStyleName
) );
81 // get font-enabled status
84 int bEnabled
= TRUE
; // by default (and when we're on macOS < 10.6) it's "enabled"
85 CFNumberRef pEnabled
= static_cast<CFNumberRef
>(CTFontDescriptorCopyAttribute( pFD
, kCTFontEnabledAttribute
));
86 CFNumberGetValue( pEnabled
, kCFNumberIntType
, &bEnabled
);
87 *bFontEnabled
= bEnabled
;
90 // get font attributes
91 CFDictionaryRef pAttrDict
= static_cast<CFDictionaryRef
>(CTFontDescriptorCopyAttribute( pFD
, kCTFontTraitsAttribute
));
93 if (bFontEnabled
&& *bFontEnabled
)
95 // Ignore font formats not supported.
97 CFNumberRef pFormat
= static_cast<CFNumberRef
>(CTFontDescriptorCopyAttribute(pFD
, kCTFontFormatAttribute
));
98 CFNumberGetValue(pFormat
, kCFNumberIntType
, &nFormat
);
99 if (nFormat
== kCTFontFormatUnrecognized
|| nFormat
== kCTFontFormatPostScript
|| nFormat
== kCTFontFormatBitmap
)
101 SAL_INFO("vcl.fonts", "Ignoring font with unsupported format: " << rDFA
.GetFamilyName());
102 *bFontEnabled
= false;
107 // get symbolic trait
108 // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
109 SInt64 nSymbolTrait
= 0;
110 CFNumberRef pSymbolNum
= nullptr;
111 if( CFDictionaryGetValueIfPresent( pAttrDict
, kCTFontSymbolicTrait
, reinterpret_cast<const void**>(&pSymbolNum
) ) )
113 CFNumberGetValue( pSymbolNum
, kCFNumberSInt64Type
, &nSymbolTrait
);
114 if (nSymbolTrait
& kCTFontMonoSpaceTrait
)
115 rDFA
.SetPitch(PITCH_FIXED
);
118 // get the font weight
120 CFNumberRef pWeightNum
= static_cast<CFNumberRef
>(CFDictionaryGetValue( pAttrDict
, kCTFontWeightTrait
));
121 // tdf#140401 check if attribute is a nullptr
123 CFNumberGetValue( pWeightNum
, kCFNumberDoubleType
, &fWeight
);
124 int nInt
= WEIGHT_NORMAL
;
126 // Special case fixes
128 // tdf#67744: Courier Std Medium is always bold. We get a kCTFontWeightTrait of 0.23 which
129 // surely must be wrong.
130 if (rDFA
.GetFamilyName() == "Courier Std" &&
131 (rDFA
.GetStyleName() == "Medium" || rDFA
.GetStyleName() == "Medium Oblique") &&
137 // tdf#68889: Ditto for Gill Sans MT Pro. Here I can kinda understand it, maybe the
138 // kCTFontWeightTrait is intended to give a subjective "optical" impression of how the font
139 // looks, and Gill Sans MT Pro Medium is kinda heavy. But with the way LibreOffice uses fonts,
140 // we still should think of it as being "medium" weight.
141 if (rDFA
.GetFamilyName() == "Gill Sans MT Pro" &&
142 (rDFA
.GetStyleName() == "Medium" || rDFA
.GetStyleName() == "Medium Italic") &&
150 nInt
= rint(int(WEIGHT_NORMAL
) + fWeight
* ((WEIGHT_BLACK
- WEIGHT_NORMAL
)/0.68));
151 if( nInt
> WEIGHT_BLACK
)
156 else if( fWeight
< 0 )
158 nInt
= rint(int(WEIGHT_NORMAL
) + fWeight
* ((WEIGHT_NORMAL
- WEIGHT_THIN
)/0.8));
159 if( nInt
< WEIGHT_THIN
)
164 rDFA
.SetWeight( static_cast<FontWeight
>(nInt
) );
166 // get the font slant
168 CFNumberRef pSlantNum
= static_cast<CFNumberRef
>(CFDictionaryGetValue( pAttrDict
, kCTFontSlantTrait
));
169 // tdf#140401 check if attribute is a nullptr
171 CFNumberGetValue( pSlantNum
, kCFNumberDoubleType
, &fSlant
);
172 if( fSlant
>= 0.035 )
174 rDFA
.SetItalic( ITALIC_NORMAL
);
178 CFNumberRef pWidthNum
= static_cast<CFNumberRef
>(CFDictionaryGetValue( pAttrDict
, kCTFontWidthTrait
));
179 // tdf#140401 check if attribute is a nullptr
181 CFNumberGetValue( pWidthNum
, kCFNumberDoubleType
, &fWidth
);
186 nInt
= rint( int(WIDTH_NORMAL
) + fWidth
* ((WIDTH_ULTRA_EXPANDED
- WIDTH_NORMAL
)/0.4));
187 if( nInt
> WIDTH_ULTRA_EXPANDED
)
189 nInt
= WIDTH_ULTRA_EXPANDED
;
192 else if( fWidth
< 0 )
194 nInt
= rint( int(WIDTH_NORMAL
) + fWidth
* ((WIDTH_NORMAL
- WIDTH_ULTRA_CONDENSED
)/0.5));
195 if( nInt
< WIDTH_ULTRA_CONDENSED
)
197 nInt
= WIDTH_ULTRA_CONDENSED
;
200 rDFA
.SetWidthType( static_cast<FontWidth
>(nInt
) );
202 // release the attribute dict that we had copied
203 CFRelease( pAttrDict
);
205 // TODO? also use the HEAD table if available to get more attributes
206 // CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
211 static void fontEnumCallBack( const void* pValue
, void* pContext
)
213 CTFontDescriptorRef pFD
= static_cast<CTFontDescriptorRef
>(pValue
);
216 FontAttributes rDFA
= DevFontFromCTFontDescriptor( pFD
, &bFontEnabled
);
220 rtl::Reference
<CoreTextFontFace
> pFontData
= new CoreTextFontFace( rDFA
, pFD
);
221 SystemFontList
* pFontList
= static_cast<SystemFontList
*>(pContext
);
222 pFontList
->AddFont( pFontData
.get() );
226 SystemFontList::SystemFontList()
227 : mpCTFontCollection( nullptr )
228 , mpCTFontArray( nullptr )
231 SystemFontList::~SystemFontList()
233 maFontContainer
.clear();
237 CFRelease( mpCTFontArray
);
239 if( mpCTFontCollection
)
241 CFRelease( mpCTFontCollection
);
245 void SystemFontList::AddFont( CoreTextFontFace
* pFontData
)
247 sal_IntPtr nFontId
= pFontData
->GetFontId();
248 maFontContainer
[ nFontId
] = pFontData
;
251 void SystemFontList::AnnounceFonts( vcl::font::PhysicalFontCollection
& rFontCollection
) const
253 for(const auto& rEntry
: maFontContainer
)
255 rFontCollection
.Add( rEntry
.second
.get() );
259 CoreTextFontFace
* SystemFontList::GetFontDataFromId( sal_IntPtr nFontId
) const
261 auto it
= maFontContainer
.find( nFontId
);
262 if( it
== maFontContainer
.end() )
266 return (*it
).second
.get();
269 bool SystemFontList::Init()
271 // enumerate available system fonts
272 static const int nMaxDictEntries
= 8;
273 CFMutableDictionaryRef pCFDict
= CFDictionaryCreateMutable( nullptr,
275 &kCFTypeDictionaryKeyCallBacks
,
276 &kCFTypeDictionaryValueCallBacks
);
278 CFDictionaryAddValue( pCFDict
, kCTFontCollectionRemoveDuplicatesOption
, kCFBooleanTrue
);
279 mpCTFontCollection
= CTFontCollectionCreateFromAvailableFonts( pCFDict
);
280 CFRelease( pCFDict
);
281 mpCTFontArray
= CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection
);
283 const int nFontCount
= CFArrayGetCount( mpCTFontArray
);
284 const CFRange aFullRange
= CFRangeMake( 0, nFontCount
);
285 CFArrayApplyFunction( mpCTFontArray
, aFullRange
, fontEnumCallBack
, this );
290 std::unique_ptr
<SystemFontList
> GetCoretextFontList()
292 std::unique_ptr
<SystemFontList
> pList(new SystemFontList());
301 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */