bump product version to 5.0.4.1
[LibreOffice.git] / vcl / quartz / ctfonts.cxx
blob16cde931b6105a6dd4694ff653fafac4f38aa9e8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 "impfont.hxx"
21 #include "outfont.hxx"
22 #include "PhysicalFontCollection.hxx"
23 #include "sallayout.hxx"
25 #ifdef MACOSX
26 #include "osx/salinst.h"
27 #include "osx/saldata.hxx"
28 // kCTForegroundColorFromContextAttributeName is available on 10.5, but it is "hidden"
29 #ifndef kCTForegroundColorFromContextAttributeName
30 extern const CFStringRef kCTForegroundColorFromContextAttributeName;
31 #endif
32 #endif
33 #include "quartz/salgdi.h"
34 #include "quartz/utils.h"
35 #include "ctfonts.hxx"
37 #include <vcl/settings.hxx>
39 #include "basegfx/polygon/b2dpolygon.hxx"
40 #include "basegfx/matrix/b2dhommatrix.hxx"
42 inline double toRadian(int nDegree)
44 return nDegree * (M_PI / 1800.0);
47 CoreTextStyle::CoreTextStyle( const FontSelectPattern& rFSD )
48 : mpFontData( static_cast<CoreTextFontData const *>(rFSD.mpFontData) )
49 , mfFontStretch( 1.0 )
50 , mfFontRotation( 0.0 )
51 , mpStyleDict( NULL )
53 const FontSelectPattern* const pReqFont = &rFSD;
55 double fScaledFontHeight = pReqFont->mfExactHeight;
57 // convert font rotation to radian
58 mfFontRotation = toRadian(pReqFont->mnOrientation);
60 // dummy matrix so we can use CGAffineTransformConcat() below
61 CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0);
63 // handle font stretching if any
64 if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) )
66 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
67 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F));
70 // create the style object for CoreText font attributes
71 static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice?
72 mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize,
73 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
75 CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse;
76 CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool );
78 // fake bold
79 if ( (pReqFont->GetWeight() >= WEIGHT_BOLD) &&
80 ((mpFontData->GetWeight() < WEIGHT_SEMIBOLD) &&
81 (mpFontData->GetWeight() != WEIGHT_DONTKNOW)) )
83 int nStroke = -lrint((3.5F * pReqFont->GetWeight()) / mpFontData->GetWeight());
84 CFNumberRef rStroke = CFNumberCreate(NULL, kCFNumberSInt32Type, &nStroke);
85 CFDictionarySetValue(mpStyleDict, kCTStrokeWidthAttributeName, rStroke);
88 // fake italic
89 if (((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
90 && (mpFontData->GetSlant() == ITALIC_NONE))
92 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMake(1, 0, toRadian(120), 1, 0, 0));
95 CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>(mpFontData->GetFontId());
96 CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, &aMatrix );
97 CFDictionarySetValue( mpStyleDict, kCTFontAttributeName, pNewCTFont );
98 CFRelease( pNewCTFont);
100 // allow delayed setting the font color, i.e. after the text layout
101 CFDictionarySetValue( mpStyleDict, kCTForegroundColorFromContextAttributeName, kCFBooleanTrue );
103 #if 0 // LastResort is implicit in CoreText's font cascading
104 const void* aGFBDescriptors[] = { CTFontDescriptorCreateWithNameAndSize( CFSTR("LastResort"), 0) }; // TODO: use the full GFB list
105 const int nGfbCount = sizeof(aGFBDescriptors) / sizeof(*aGFBDescriptors);
106 CFArrayRef pGfbList = CFArrayCreate( NULL, aGFBDescriptors, nGfbCount, &kCFTypeArrayCallBacks);
107 CFDictionaryAddValue( mpStyleDict, kCTFontCascadeListAttribute, pGfbList);
108 CFRelease( pGfbList);
109 #endif
112 CoreTextStyle::~CoreTextStyle()
114 if( mpStyleDict )
115 CFRelease( mpStyleDict );
118 void CoreTextStyle::GetFontMetric( ImplFontMetricData& rMetric ) const
120 // get the matching CoreText font handle
121 // TODO: is it worth it to cache the CTFontRef in SetFont() and reuse it here?
122 CTFontRef aCTFontRef = static_cast<CTFontRef>(CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ));
124 const CGFloat fAscent = CTFontGetAscent( aCTFontRef );
125 const CGFloat fCapHeight = CTFontGetCapHeight( aCTFontRef );
126 rMetric.mnAscent = lrint( fAscent );
127 rMetric.mnDescent = lrint( CTFontGetDescent( aCTFontRef ));
128 rMetric.mnExtLeading = lrint( CTFontGetLeading( aCTFontRef ));
129 rMetric.mnIntLeading = lrint( fAscent - fCapHeight );
131 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
132 // setting this width to the pixel height of the fontsize is good enough
133 // it also makes the calculation of the stretch factor simple
134 rMetric.mnWidth = lrint( CTFontGetSize( aCTFontRef ) * mfFontStretch);
136 // all CoreText fonts are scalable
137 rMetric.mbScalableFont = true;
138 rMetric.mbKernableFont = true;
141 bool CoreTextStyle::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) const
143 CGGlyph nCGGlyph = aGlyphId & GF_IDXMASK;
144 // XXX: this is broken if the glyph came from fallback font
145 CTFontRef aCTFontRef = static_cast<CTFontRef>(CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ));
147 const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert
148 const CGRect aCGRect = CTFontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 );
150 rRect.Left() = lrint( aCGRect.origin.x );
151 rRect.Top() = lrint( aCGRect.origin.y );
152 rRect.Right() = lrint( aCGRect.origin.x + aCGRect.size.width );
153 rRect.Bottom() = lrint( aCGRect.origin.y + aCGRect.size.height );
154 return true;
157 // callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline()
158 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
160 static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement )
162 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
163 const int nPointCount = rPolygon.count();
165 switch( pElement->type )
167 case kCGPathElementCloseSubpath:
168 case kCGPathElementMoveToPoint:
169 if( nPointCount > 0 ) {
170 static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon );
171 rPolygon.clear();
173 // fall through for kCGPathElementMoveToPoint:
174 if( pElement->type != kCGPathElementMoveToPoint )
175 break;
176 case kCGPathElementAddLineToPoint:
177 rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) );
178 break;
179 case kCGPathElementAddCurveToPoint:
180 rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) );
181 rPolygon.setNextControlPoint( nPointCount-1, basegfx::B2DPoint( pElement->points[0].x, -pElement->points[0].y ) );
182 rPolygon.setPrevControlPoint( nPointCount+0, basegfx::B2DPoint( pElement->points[1].x, -pElement->points[1].y ) );
183 break;
184 case kCGPathElementAddQuadCurveToPoint: {
185 const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 );
186 const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2* pElement->points[0].x) / 3.0,
187 (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 );
188 const basegfx::B2DPoint aCtrPt2( (+2 * +pElement->points[0].x + pElement->points[1].x) / 3.0,
189 (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 );
190 rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) );
191 rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 );
192 rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 );
193 } break;
197 bool CoreTextStyle::GetGlyphOutline( sal_GlyphId aGlyphId, basegfx::B2DPolyPolygon& rResult ) const
199 rResult.clear();
201 CGGlyph nCGGlyph = aGlyphId & GF_IDXMASK;
202 // XXX: this is broken if the glyph came from fallback font
203 CTFontRef pCTFont = static_cast<CTFontRef>(CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName ));
204 CGPathRef xPath = CTFontCreatePathForGlyph( pCTFont, nCGGlyph, NULL );
205 if (!xPath) {
206 return false;
209 GgoData aGgoData;
210 aGgoData.mpPolyPoly = &rResult;
211 CGPathApply( xPath, (void*)&aGgoData, MyCGPathApplierFunc );
212 #if 0 // TODO: does OSX ensure that the last polygon is always closed?
213 const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL };
214 MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement );
215 #endif
216 CFRelease( xPath );
218 return true;
221 PhysicalFontFace* CoreTextFontData::Clone() const
223 return new CoreTextFontData( *this);
226 ImplFontEntry* CoreTextFontData::CreateFontInstance( /*const*/ FontSelectPattern& rFSD ) const
228 return new ImplFontEntry( rFSD);
231 int CoreTextFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const
233 DBG_ASSERT( pTagName[4]=='\0', "CoreTextFontData::GetFontTable with invalid tagname!\n" );
235 const CTFontTableTag nTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0);
237 // get the raw table length
238 CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>( GetFontId());
239 CTFontRef rCTFont = CTFontCreateWithFontDescriptor( pFontDesc, 0.0, NULL);
240 const uint32_t opts( kCTFontTableOptionNoOptions );
241 CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, opts);
242 CFRelease( rCTFont);
243 if( !pDataRef)
244 return 0;
246 const CFIndex nByteLength = CFDataGetLength( pDataRef);
248 // get the raw table data if requested
249 if( pResultBuf && (nByteLength > 0))
251 const CFRange aFullRange = CFRangeMake( 0, nByteLength);
252 CFDataGetBytes( pDataRef, aFullRange, (UInt8*)pResultBuf);
255 CFRelease( pDataRef);
257 return (int)nByteLength;
260 ImplDevFontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled )
262 // all CoreText fonts are device fonts that can rotate just fine
263 ImplDevFontAttributes rDFA;
264 rDFA.mbOrientation = true;
265 rDFA.mbDevice = true;
266 rDFA.mnQuality = 0;
268 // reset the font attributes
269 rDFA.SetFamilyType( FAMILY_DONTKNOW );
270 rDFA.SetPitch( PITCH_VARIABLE );
271 rDFA.SetWidthType( WIDTH_NORMAL );
272 rDFA.SetWeight( WEIGHT_NORMAL );
273 rDFA.SetItalic( ITALIC_NONE );
274 rDFA.SetSymbolFlag( false );
276 // all scalable fonts on this platform are subsettable
277 rDFA.mbEmbeddable = false;
278 rDFA.mbSubsettable = true;
280 // get font name
281 #ifdef MACOSX
282 const OUString aUILang = Application::GetSettings().GetUILanguageTag().getLanguage();
283 CFStringRef pUILang = CFStringCreateWithCharacters( kCFAllocatorDefault, aUILang.getStr(), aUILang.getLength() );
284 CFStringRef pLang = NULL;
285 CFStringRef pFamilyName = static_cast<CFStringRef>(CTFontDescriptorCopyLocalizedAttribute( pFD, kCTFontFamilyNameAttribute, &pLang ));
286 if ( !pLang || ( CFStringCompare( pUILang, pLang, 0 ) != kCFCompareEqualTo ))
288 if(pFamilyName)
290 CFRelease( pFamilyName );
292 pFamilyName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute ));
294 #else
295 // No "Application" on iOS. And it is unclear whether this code
296 // snippet will actually ever get invoked on iOS anyway. So just
297 // use the old code that uses a non-localized font name.
298 CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute );
299 #endif
301 rDFA.SetFamilyName( GetOUString( pFamilyName ) );
303 // get font style
304 CFStringRef pStyleName = static_cast<CFStringRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute ));
305 rDFA.SetStyleName( GetOUString( pStyleName ) );
307 // get font-enabled status
308 if( bFontEnabled ) {
309 int bEnabled = TRUE; // by default (and when we're on OS X < 10.6) it's "enabled"
310 CFNumberRef pEnabled = static_cast<CFNumberRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute ));
311 CFNumberGetValue( pEnabled, kCFNumberIntType, &bEnabled );
312 *bFontEnabled = bEnabled;
315 // get font attributes
316 CFDictionaryRef pAttrDict = static_cast<CFDictionaryRef>(CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute ));
318 // get symbolic trait
319 // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
320 SInt64 nSymbolTrait = 0;
321 CFNumberRef pSymbolNum = NULL;
322 if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, reinterpret_cast<const void**>(&pSymbolNum) ) ) {
323 CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait );
324 rDFA.SetSymbolFlag( ((nSymbolTrait & kCTFontClassMaskTrait) == kCTFontSymbolicClass) );
327 // get the font weight
328 double fWeight = 0;
329 CFNumberRef pWeightNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait ));
330 CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight );
331 int nInt = WEIGHT_NORMAL;
332 if( fWeight > 0 ) {
333 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
334 if( nInt > WEIGHT_BLACK )
335 nInt = WEIGHT_BLACK;
336 } else if( fWeight < 0 ) {
337 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.9));
338 if( nInt < WEIGHT_THIN )
339 nInt = WEIGHT_THIN;
341 rDFA.SetWeight( (FontWeight)nInt );
343 // get the font slant
344 double fSlant = 0;
345 CFNumberRef pSlantNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait ));
346 CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
347 if( fSlant >= 0.035 )
348 rDFA.SetItalic( ITALIC_NORMAL );
350 // get width trait
351 double fWidth = 0;
352 CFNumberRef pWidthNum = static_cast<CFNumberRef>(CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait ));
353 CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
354 nInt = WIDTH_NORMAL;
355 if( fWidth > 0 ) {
356 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4));
357 if( nInt > WIDTH_ULTRA_EXPANDED )
358 nInt = WIDTH_ULTRA_EXPANDED;
359 } else if( fWidth < 0 ) {
360 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5));
361 if( nInt < WIDTH_ULTRA_CONDENSED )
362 nInt = WIDTH_ULTRA_CONDENSED;
364 rDFA.SetWidthType( (FontWidth)nInt );
366 // release the attribute dict that we had copied
367 CFRelease( pAttrDict );
369 // TODO? also use the HEAD table if available to get more attributes
370 // CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
372 return rDFA;
375 static void CTFontEnumCallBack( const void* pValue, void* pContext )
377 CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
379 bool bFontEnabled;
380 ImplDevFontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled );
382 if( bFontEnabled)
384 const sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>(pValue);
385 CoreTextFontData* pFontData = new CoreTextFontData( rDFA, nFontId );
386 SystemFontList* pFontList = static_cast<SystemFontList*>(pContext);
387 pFontList->AddFont( pFontData );
391 SystemFontList::SystemFontList()
392 : mpCTFontCollection( NULL )
393 , mpCTFontArray( NULL )
396 SystemFontList::~SystemFontList()
398 CTFontContainer::const_iterator it = maFontContainer.begin();
399 for(; it != maFontContainer.end(); ++it )
400 delete (*it).second;
401 maFontContainer.clear();
403 if( mpCTFontArray )
404 CFRelease( mpCTFontArray );
405 if( mpCTFontCollection )
406 CFRelease( mpCTFontCollection );
409 void SystemFontList::AddFont( CoreTextFontData* pFontData )
411 sal_IntPtr nFontId = pFontData->GetFontId();
412 maFontContainer[ nFontId ] = pFontData;
415 void SystemFontList::AnnounceFonts( PhysicalFontCollection& rFontCollection ) const
417 CTFontContainer::const_iterator it = maFontContainer.begin();
418 for(; it != maFontContainer.end(); ++it )
419 rFontCollection.Add( (*it).second->Clone() );
422 CoreTextFontData* SystemFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
424 CTFontContainer::const_iterator it = maFontContainer.find( nFontId );
425 if( it == maFontContainer.end() )
426 return NULL;
427 return (*it).second;
430 bool SystemFontList::Init()
432 // enumerate available system fonts
433 static const int nMaxDictEntries = 8;
434 CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL,
435 nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
436 CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue );
437 mpCTFontCollection = CTFontCollectionCreateFromAvailableFonts( pCFDict );
438 CFRelease( pCFDict );
440 mpCTFontArray = CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection );
441 const int nFontCount = CFArrayGetCount( mpCTFontArray );
442 const CFRange aFullRange = CFRangeMake( 0, nFontCount );
443 CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this );
445 return true;
448 SystemFontList* GetCoretextFontList()
450 SystemFontList* pList = new SystemFontList();
451 if( !pList->Init() ) {
452 delete pList;
453 return NULL;
456 return pList;
459 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */