Bump version to 4.1-6
[LibreOffice.git] / vcl / coretext / ctfonts.cxx
bloba0cc98a7cd28396c4427f27b853e452b06ac5223
1 /*
2 * This file is part of the LibreOffice project.
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 * This file incorporates work covered by the following license notice:
10 * Licensed to the Apache Software Foundation (ASF) under one or more
11 * contributor license agreements. See the NOTICE file distributed
12 * with this work for additional information regarding copyright
13 * ownership. The ASF licenses this file to you under the Apache
14 * License, Version 2.0 (the "License"); you may not use this file
15 * except in compliance with the License. You may obtain a copy of
16 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
19 #include <boost/unordered_map.hpp>
21 #include "impfont.hxx"
22 #include "outfont.hxx"
23 #include "sallayout.hxx"
25 #ifdef MACOSX
26 #include "aqua/salinst.h"
27 #include "aqua/saldata.hxx"
28 #endif
29 #include "coretext/salgdi2.h"
30 #include "quartz/utils.h"
31 #include "ctfonts.hxx"
33 #include "basegfx/polygon/b2dpolygon.hxx"
34 #include "basegfx/matrix/b2dhommatrix.hxx"
36 // =======================================================================
38 class CTFontList
39 : public SystemFontList
41 public:
42 explicit CTFontList( void );
43 virtual ~CTFontList( void );
45 bool Init( void );
46 void AddFont( CTFontData* );
48 virtual void AnnounceFonts( ImplDevFontList& ) const;
49 virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr ) const;
51 private:
52 CTFontCollectionRef mpCTFontCollection;
53 CFArrayRef mpCTFontArray;
55 typedef boost::unordered_map<sal_IntPtr,CTFontData*> CTFontContainer;
56 CTFontContainer maFontContainer;
59 // =======================================================================
61 inline double toRadian(int nDegree)
63 return nDegree * (M_PI / 1800.0);
66 CTTextStyle::CTTextStyle( const FontSelectPattern& rFSD )
67 : ImplMacTextStyle( rFSD )
68 , mpStyleDict( NULL )
70 mpFontData = (CTFontData*)rFSD.mpFontData;
71 const FontSelectPattern* const pReqFont = &rFSD;
73 double fScaledFontHeight = pReqFont->mfExactHeight;
75 // convert font rotation to radian
76 mfFontRotation = toRadian(pReqFont->mnOrientation);
78 // dummy matrix so we can use CGAffineTransformConcat() below
79 CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0);
81 // handle font stretching if any
82 if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) )
84 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
85 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F));
88 // create the style object for CoreText font attributes
89 static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice?
90 mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize,
91 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
93 CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse;
94 CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool );
96 // fake bold
97 if ((pReqFont->GetWeight() >= WEIGHT_BOLD) && (mpFontData->GetWeight() < WEIGHT_SEMIBOLD))
99 int nStroke = -10.0;
100 CFNumberRef rStroke = CFNumberCreate(NULL, kCFNumberSInt32Type, &nStroke);
101 CFDictionarySetValue(mpStyleDict, kCTStrokeWidthAttributeName, rStroke);
104 // fake italic
105 if (((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
106 && !((mpFontData->GetSlant() == ITALIC_NORMAL) || (mpFontData->GetSlant() == ITALIC_OBLIQUE)))
108 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMake(1, 0, toRadian(120), 1, 0, 0));
111 CTFontDescriptorRef pFontDesc = (CTFontDescriptorRef)mpFontData->GetFontId();
112 CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, &aMatrix );
113 CFDictionarySetValue( mpStyleDict, kCTFontAttributeName, pNewCTFont );
114 CFRelease( pNewCTFont);
116 #if 0 // LastResort is implicit in CoreText's font cascading
117 const void* aGFBDescriptors[] = { CTFontDescriptorCreateWithNameAndSize( CFSTR("LastResort"), 0) }; // TODO: use the full GFB list
118 const int nGfbCount = sizeof(aGFBDescriptors) / sizeof(*aGFBDescriptors);
119 CFArrayRef pGfbList = CFArrayCreate( NULL, aGFBDescriptors, nGfbCount, &kCFTypeArrayCallBacks);
120 CFDictionaryAddValue( mpStyleDict, kCTFontCascadeListAttribute, pGfbList);
121 CFRelease( pGfbList);
122 #endif
125 // -----------------------------------------------------------------------
127 CTTextStyle::~CTTextStyle( void )
129 if( mpStyleDict )
130 CFRelease( mpStyleDict );
133 // -----------------------------------------------------------------------
135 void CTTextStyle::GetFontMetric( float fPixelSize, ImplFontMetricData& rMetric ) const
137 // get the matching CoreText font handle
138 // TODO: is it worth it to cache the CTFontRef in SetFont() and reuse it here?
139 CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
141 rMetric.mnAscent = lrint( CTFontGetAscent( aCTFontRef ) * fPixelSize);
142 rMetric.mnDescent = lrint( CTFontGetDescent( aCTFontRef ) * fPixelSize);
143 rMetric.mnExtLeading = lrint( CTFontGetLeading( aCTFontRef ) * fPixelSize);
144 rMetric.mnIntLeading = 0;
145 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
146 // setting this width to the pixel height of the fontsize is good enough
147 // it also makes the calculation of the stretch factor simple
148 rMetric.mnWidth = lrint( CTFontGetSize( aCTFontRef ) * fPixelSize * mfFontStretch);
150 // all CoreText fonts are scalable
151 rMetric.mbScalableFont = true;
152 rMetric.mbKernableFont = true;
155 // -----------------------------------------------------------------------
157 bool CTTextStyle::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect ) const
159 CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK;
160 // XXX: this is broken if the glyph came from fallback font
161 CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
163 const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert
164 const CGRect aCGRect = CTFontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 );
166 rRect.Left() = lrint( aCGRect.origin.x );
167 rRect.Top() = lrint( aCGRect.origin.y );
168 rRect.Right() = lrint( aCGRect.origin.x + aCGRect.size.width );
169 rRect.Bottom() = lrint( aCGRect.origin.y + aCGRect.size.height );
170 return true;
173 // -----------------------------------------------------------------------
175 // callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline()
176 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
178 static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement )
180 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
181 const int nPointCount = rPolygon.count();
183 switch( pElement->type )
185 case kCGPathElementCloseSubpath:
186 case kCGPathElementMoveToPoint:
187 if( nPointCount > 0 ) {
188 static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon );
189 rPolygon.clear();
191 // fall through for kCGPathElementMoveToPoint:
192 if( pElement->type != kCGPathElementMoveToPoint )
193 break;
194 case kCGPathElementAddLineToPoint:
195 rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) );
196 break;
197 case kCGPathElementAddCurveToPoint:
198 rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) );
199 rPolygon.setNextControlPoint( nPointCount-1, basegfx::B2DPoint( pElement->points[0].x, -pElement->points[0].y ) );
200 rPolygon.setPrevControlPoint( nPointCount+0, basegfx::B2DPoint( pElement->points[1].x, -pElement->points[1].y ) );
201 break;
202 case kCGPathElementAddQuadCurveToPoint: {
203 const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 );
204 const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2* pElement->points[0].x) / 3.0,
205 (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 );
206 const basegfx::B2DPoint aCtrPt2( (+2 * +pElement->points[0].x + pElement->points[1].x) / 3.0,
207 (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 );
208 rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) );
209 rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 );
210 rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 );
211 } break;
215 bool CTTextStyle::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rResult ) const
217 rResult.clear();
219 CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK;
220 // XXX: this is broken if the glyph came from fallback font
221 CTFontRef pCTFont = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
222 CGPathRef xPath = CTFontCreatePathForGlyph( pCTFont, nCGGlyph, NULL );
224 GgoData aGgoData;
225 aGgoData.mpPolyPoly = &rResult;
226 CGPathApply( xPath, (void*)&aGgoData, MyCGPathApplierFunc );
227 #if 0 // TODO: does OSX ensure that the last polygon is always closed?
228 const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL };
229 MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement );
230 #endif
232 return true;
235 // -----------------------------------------------------------------------
237 void CTTextStyle::SetTextColor( const RGBAColor& rColor )
239 CGFloat aColor[] = { rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue(), rColor.GetAlpha() };
240 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
241 CGColorRef pCGColor = CGColorCreate( cs, aColor );
242 CGColorSpaceRelease( cs );
243 CFDictionarySetValue( mpStyleDict, kCTForegroundColorAttributeName, pCGColor );
244 CFRelease( pCGColor);
247 // =======================================================================
249 CTFontData::CTFontData( const ImplDevFontAttributes& rDFA, sal_IntPtr nFontId )
250 : ImplMacFontData( rDFA, nFontId )
253 // -----------------------------------------------------------------------
255 CTFontData::~CTFontData( void )
257 // TODO: any resources to release?
260 // -----------------------------------------------------------------------
262 PhysicalFontFace* CTFontData::Clone( void ) const
264 return new CTFontData( *this);
267 // -----------------------------------------------------------------------
269 ImplMacTextStyle* CTFontData::CreateMacTextStyle( const FontSelectPattern& rFSD ) const
271 return new CTTextStyle( rFSD);
274 // -----------------------------------------------------------------------
276 ImplFontEntry* CTFontData::CreateFontInstance( /*const*/ FontSelectPattern& rFSD ) const
278 return new ImplFontEntry( rFSD);
281 // -----------------------------------------------------------------------
283 int CTFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const
285 DBG_ASSERT( pTagName[4]=='\0', "CTFontData::GetFontTable with invalid tagname!\n" );
287 const CTFontTableTag nTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0);
289 // get the raw table length
290 CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>( GetFontId());
291 CTFontRef rCTFont = CTFontCreateWithFontDescriptor( pFontDesc, 0.0, NULL);
292 #if defined(MACOSX) && MACOSX_SDK_VERSION < 1080
293 const uint32_t opts( kCTFontTableOptionExcludeSynthetic );
294 #else
295 const uint32_t opts( kCTFontTableOptionNoOptions );
296 #endif
297 CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, opts);
298 CFRelease( rCTFont);
299 if( !pDataRef)
300 return 0;
302 const CFIndex nByteLength = CFDataGetLength( pDataRef);
304 // get the raw table data if requested
305 if( pResultBuf && (nByteLength > 0))
307 const CFRange aFullRange = CFRangeMake( 0, nByteLength);
308 CFDataGetBytes( pDataRef, aFullRange, (UInt8*)pResultBuf);
311 CFRelease( pDataRef);
313 return (int)nByteLength;
316 // =======================================================================
318 ImplDevFontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled )
320 // all CoreText fonts are device fonts that can rotate just fine
321 ImplDevFontAttributes rDFA;
322 rDFA.mbOrientation = true;
323 rDFA.mbDevice = true;
324 rDFA.mnQuality = 0;
326 // reset the font attributes
327 rDFA.SetFamilyType( FAMILY_DONTKNOW );
328 rDFA.SetPitch( PITCH_VARIABLE );
329 rDFA.SetWidthType( WIDTH_NORMAL );
330 rDFA.SetWeight( WEIGHT_NORMAL );
331 rDFA.SetItalic( ITALIC_NONE );
332 rDFA.SetSymbolFlag( false );
334 // all scalable fonts on this platform are subsettable
335 rDFA.mbEmbeddable = false;
336 rDFA.mbSubsettable = true;
338 // get font name
339 CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute );
340 rDFA.SetFamilyName( GetOUString( pFamilyName ) );
341 // get font style
342 CFStringRef pStyleName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute );
343 rDFA.SetStyleName( GetOUString( pStyleName ) );
345 // get font-enabled status
346 if( bFontEnabled ) {
347 int bEnabled = FALSE;
348 CFNumberRef pEnabled = (CFNumberRef)CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute );
349 CFNumberGetValue( pEnabled, kCFNumberIntType, &bEnabled );
350 *bFontEnabled = bEnabled;
353 // get font attributes
354 CFDictionaryRef pAttrDict = (CFDictionaryRef)CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute );
356 // get symbolic trait
357 // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
358 SInt64 nSymbolTrait = 0;
359 CFNumberRef pSymbolNum = NULL;
360 if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, (const void**)&pSymbolNum ) ) {
361 CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait );
362 rDFA.SetSymbolFlag( ((nSymbolTrait & kCTFontClassMaskTrait) == kCTFontSymbolicClass) );
365 // get the font weight
366 double fWeight = 0;
367 CFNumberRef pWeightNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait );
368 CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight );
369 int nInt = WEIGHT_NORMAL;
370 if( fWeight > 0 ) {
371 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
372 if( nInt > WEIGHT_BLACK )
373 nInt = WEIGHT_BLACK;
374 } else if( fWeight < 0 ) {
375 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.9));
376 if( nInt < WEIGHT_THIN )
377 nInt = WEIGHT_THIN;
379 rDFA.SetWeight( (FontWeight)nInt );
381 // get the font slant
382 double fSlant = 0;
383 CFNumberRef pSlantNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait );
384 CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
385 if( fSlant >= 0.035 )
386 rDFA.SetItalic( ITALIC_NORMAL );
388 // get width trait
389 double fWidth = 0;
390 CFNumberRef pWidthNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait );
391 CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
392 nInt = WIDTH_NORMAL;
393 if( fWidth > 0 ) {
394 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4));
395 if( nInt > WIDTH_ULTRA_EXPANDED )
396 nInt = WIDTH_ULTRA_EXPANDED;
397 } else if( fWidth < 0 ) {
398 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5));
399 if( nInt < WIDTH_ULTRA_CONDENSED )
400 nInt = WIDTH_ULTRA_CONDENSED;
402 rDFA.SetWidthType( (FontWidth)nInt );
404 // release the attribute dict that we had copied
405 CFRelease( pAttrDict );
407 // TODO? also use the HEAD table if available to get more attributes
408 // CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
410 return rDFA;
413 static void CTFontEnumCallBack( const void* pValue, void* pContext )
415 CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
417 bool bFontEnabled;
418 ImplDevFontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled );
420 if( bFontEnabled)
422 const sal_IntPtr nFontId = (sal_IntPtr)pValue;
423 CTFontData* pFontData = new CTFontData( rDFA, nFontId );
424 CTFontList* pFontList = (CTFontList*)pContext;
425 pFontList->AddFont( pFontData );
429 // =======================================================================
431 CTFontList::CTFontList()
432 : mpCTFontCollection( NULL )
433 , mpCTFontArray( NULL )
436 // -----------------------------------------------------------------------
438 CTFontList::~CTFontList()
440 CTFontContainer::const_iterator it = maFontContainer.begin();
441 for(; it != maFontContainer.end(); ++it )
442 delete (*it).second;
443 maFontContainer.clear();
445 if( mpCTFontArray )
446 CFRelease( mpCTFontArray );
447 if( mpCTFontCollection )
448 CFRelease( mpCTFontCollection );
451 // -----------------------------------------------------------------------
453 void CTFontList::AddFont( CTFontData* pFontData )
455 sal_IntPtr nFontId = pFontData->GetFontId();
456 maFontContainer[ nFontId ] = pFontData;
459 // -----------------------------------------------------------------------
461 void CTFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
463 CTFontContainer::const_iterator it = maFontContainer.begin();
464 for(; it != maFontContainer.end(); ++it )
465 rFontList.Add( (*it).second->Clone() );
468 // -----------------------------------------------------------------------
470 ImplMacFontData* CTFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
472 CTFontContainer::const_iterator it = maFontContainer.find( nFontId );
473 if( it == maFontContainer.end() )
474 return NULL;
475 return (*it).second;
478 // -----------------------------------------------------------------------
480 bool CTFontList::Init( void )
482 // enumerate available system fonts
483 static const int nMaxDictEntries = 8;
484 CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL,
485 nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
486 CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue );
487 mpCTFontCollection = CTFontCollectionCreateFromAvailableFonts( pCFDict );
488 CFRelease( pCFDict );
490 mpCTFontArray = CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection );
491 const int nFontCount = CFArrayGetCount( mpCTFontArray );
492 const CFRange aFullRange = CFRangeMake( 0, nFontCount );
493 CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this );
495 return true;
498 // =======================================================================
500 SystemFontList* GetCoretextFontList( void )
502 CTFontList* pList = new CTFontList();
503 if( !pList->Init() ) {
504 delete pList;
505 return NULL;
508 return pList;
511 // =======================================================================