Avoid potential negative array index access to cached text.
[LibreOffice.git] / vcl / quartz / SystemFontList.cxx
blobe068caf80fb66ca0be4b83f28d8f43c21102324b
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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>
28 #ifdef MACOSX
29 #include <osx/saldata.hxx>
30 #include <osx/salinst.h>
31 #endif
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
43 FontAttributes rDFA;
44 rDFA.SetQuality( 0 );
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 );
54 // get font name
55 #ifdef MACOSX
56 CFStringRef pLang = nullptr;
57 CFStringRef pFamilyName = static_cast<CFStringRef>(
58 CTFontDescriptorCopyLocalizedAttribute( pFD, kCTFontFamilyNameAttribute, &pLang ));
60 if ( !pLang )
62 if( pFamilyName )
64 CFRelease( pFamilyName );
66 pFamilyName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute ));
68 #else
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 );
73 #endif
75 rDFA.SetFamilyName( GetOUString( pFamilyName ) );
77 // get font style
78 CFStringRef pStyleName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute ));
79 rDFA.SetStyleName( GetOUString( pStyleName ) );
81 // get font-enabled status
82 if( bFontEnabled )
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.
96 int nFormat;
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;
104 CFRelease(pFormat);
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
119 double fWeight = 0;
120 CFNumberRef pWeightNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait ));
121 // tdf#140401 check if attribute is a nullptr
122 if( pWeightNum )
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") &&
132 fWeight > 0.2)
134 fWeight = 0;
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") &&
143 fWeight > 0.2)
145 fWeight = 0;
148 if( fWeight > 0 )
150 nInt = rint(int(WEIGHT_NORMAL) + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
151 if( nInt > WEIGHT_BLACK )
153 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 )
161 nInt = WEIGHT_THIN;
164 rDFA.SetWeight( static_cast<FontWeight>(nInt) );
166 // get the font slant
167 double fSlant = 0;
168 CFNumberRef pSlantNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait ));
169 // tdf#140401 check if attribute is a nullptr
170 if( pSlantNum )
171 CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
172 if( fSlant >= 0.035 )
174 rDFA.SetItalic( ITALIC_NORMAL );
176 // get width trait
177 double fWidth = 0;
178 CFNumberRef pWidthNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait ));
179 // tdf#140401 check if attribute is a nullptr
180 if( pWidthNum )
181 CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
182 nInt = WIDTH_NORMAL;
184 if( fWidth > 0 )
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 );
208 return rDFA;
211 static void fontEnumCallBack( const void* pValue, void* pContext )
213 CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
215 bool bFontEnabled;
216 FontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled );
218 if( 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();
235 if( mpCTFontArray )
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() )
264 return nullptr;
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,
274 nMaxDictEntries,
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 );
287 return true;
290 std::unique_ptr<SystemFontList> GetCoretextFontList()
292 std::unique_ptr<SystemFontList> pList(new SystemFontList());
293 if( !pList->Init() )
295 return nullptr;
298 return pList;
301 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */