Version 4.2.0.1, tag libreoffice-4.2.0.1
[LibreOffice.git] / vcl / coretext / ctfonts.cxx
blobd46d19d31028de4ee12518a23b73f578e2e36648
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 <boost/unordered_map.hpp>
22 #include "impfont.hxx"
23 #include "outfont.hxx"
24 #include "sallayout.hxx"
26 #ifdef MACOSX
27 #include "aqua/salinst.h"
28 #include "aqua/saldata.hxx"
29 #endif
30 #include "coretext/salgdi2.h"
31 #include "quartz/utils.h"
32 #include "ctfonts.hxx"
34 #include "basegfx/polygon/b2dpolygon.hxx"
35 #include "basegfx/matrix/b2dhommatrix.hxx"
37 // =======================================================================
39 class CTFontList
40 : public SystemFontList
42 public:
43 explicit CTFontList( void );
44 virtual ~CTFontList( void );
46 bool Init( void );
47 void AddFont( CTFontData* );
49 virtual void AnnounceFonts( ImplDevFontList& ) const;
50 virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr ) const;
52 private:
53 CTFontCollectionRef mpCTFontCollection;
54 CFArrayRef mpCTFontArray;
56 typedef boost::unordered_map<sal_IntPtr,CTFontData*> CTFontContainer;
57 CTFontContainer maFontContainer;
60 // =======================================================================
62 inline double toRadian(int nDegree)
64 return nDegree * (M_PI / 1800.0);
67 CTTextStyle::CTTextStyle( const FontSelectPattern& rFSD )
68 : ImplMacTextStyle( rFSD )
69 , mpStyleDict( NULL )
71 mpFontData = (CTFontData*)rFSD.mpFontData;
72 const FontSelectPattern* const pReqFont = &rFSD;
74 double fScaledFontHeight = pReqFont->mfExactHeight;
76 // convert font rotation to radian
77 mfFontRotation = toRadian(pReqFont->mnOrientation);
79 // dummy matrix so we can use CGAffineTransformConcat() below
80 CGAffineTransform aMatrix = CGAffineTransformMakeTranslation(0, 0);
82 // handle font stretching if any
83 if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) )
85 mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
86 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMakeScale(mfFontStretch, 1.0F));
89 // create the style object for CoreText font attributes
90 static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice?
91 mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize,
92 &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
94 CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse;
95 CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool );
97 // fake bold
98 if ((pReqFont->GetWeight() >= WEIGHT_BOLD) && (mpFontData->GetWeight() < WEIGHT_SEMIBOLD))
100 int nStroke = -10.0;
101 CFNumberRef rStroke = CFNumberCreate(NULL, kCFNumberSInt32Type, &nStroke);
102 CFDictionarySetValue(mpStyleDict, kCTStrokeWidthAttributeName, rStroke);
105 // fake italic
106 if (((pReqFont->GetSlant() == ITALIC_NORMAL) || (pReqFont->GetSlant() == ITALIC_OBLIQUE))
107 && !((mpFontData->GetSlant() == ITALIC_NORMAL) || (mpFontData->GetSlant() == ITALIC_OBLIQUE)))
109 aMatrix = CGAffineTransformConcat(aMatrix, CGAffineTransformMake(1, 0, toRadian(120), 1, 0, 0));
112 CTFontDescriptorRef pFontDesc = (CTFontDescriptorRef)mpFontData->GetFontId();
113 CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, &aMatrix );
114 CFDictionarySetValue( mpStyleDict, kCTFontAttributeName, pNewCTFont );
115 CFRelease( pNewCTFont);
117 #if 0 // LastResort is implicit in CoreText's font cascading
118 const void* aGFBDescriptors[] = { CTFontDescriptorCreateWithNameAndSize( CFSTR("LastResort"), 0) }; // TODO: use the full GFB list
119 const int nGfbCount = sizeof(aGFBDescriptors) / sizeof(*aGFBDescriptors);
120 CFArrayRef pGfbList = CFArrayCreate( NULL, aGFBDescriptors, nGfbCount, &kCFTypeArrayCallBacks);
121 CFDictionaryAddValue( mpStyleDict, kCTFontCascadeListAttribute, pGfbList);
122 CFRelease( pGfbList);
123 #endif
126 // -----------------------------------------------------------------------
128 CTTextStyle::~CTTextStyle( void )
130 if( mpStyleDict )
131 CFRelease( mpStyleDict );
134 // -----------------------------------------------------------------------
136 void CTTextStyle::GetFontMetric( float fPixelSize, ImplFontMetricData& rMetric ) const
138 // get the matching CoreText font handle
139 // TODO: is it worth it to cache the CTFontRef in SetFont() and reuse it here?
140 CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
142 rMetric.mnAscent = lrint( CTFontGetAscent( aCTFontRef ) * fPixelSize);
143 rMetric.mnDescent = lrint( CTFontGetDescent( aCTFontRef ) * fPixelSize);
144 rMetric.mnExtLeading = lrint( CTFontGetLeading( aCTFontRef ) * fPixelSize);
145 rMetric.mnIntLeading = 0;
146 // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
147 // setting this width to the pixel height of the fontsize is good enough
148 // it also makes the calculation of the stretch factor simple
149 rMetric.mnWidth = lrint( CTFontGetSize( aCTFontRef ) * fPixelSize * mfFontStretch);
151 // all CoreText fonts are scalable
152 rMetric.mbScalableFont = true;
153 rMetric.mbKernableFont = true;
156 // -----------------------------------------------------------------------
158 bool CTTextStyle::GetGlyphBoundRect( sal_GlyphId nGlyphId, Rectangle& rRect ) const
160 CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK;
161 // XXX: this is broken if the glyph came from fallback font
162 CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
164 const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert
165 const CGRect aCGRect = CTFontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 );
167 rRect.Left() = lrint( aCGRect.origin.x );
168 rRect.Top() = lrint( aCGRect.origin.y );
169 rRect.Right() = lrint( aCGRect.origin.x + aCGRect.size.width );
170 rRect.Bottom() = lrint( aCGRect.origin.y + aCGRect.size.height );
171 return true;
174 // -----------------------------------------------------------------------
176 // callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline()
177 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
179 static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement )
181 basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
182 const int nPointCount = rPolygon.count();
184 switch( pElement->type )
186 case kCGPathElementCloseSubpath:
187 case kCGPathElementMoveToPoint:
188 if( nPointCount > 0 ) {
189 static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon );
190 rPolygon.clear();
192 // fall through for kCGPathElementMoveToPoint:
193 if( pElement->type != kCGPathElementMoveToPoint )
194 break;
195 case kCGPathElementAddLineToPoint:
196 rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) );
197 break;
198 case kCGPathElementAddCurveToPoint:
199 rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) );
200 rPolygon.setNextControlPoint( nPointCount-1, basegfx::B2DPoint( pElement->points[0].x, -pElement->points[0].y ) );
201 rPolygon.setPrevControlPoint( nPointCount+0, basegfx::B2DPoint( pElement->points[1].x, -pElement->points[1].y ) );
202 break;
203 case kCGPathElementAddQuadCurveToPoint: {
204 const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 );
205 const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2* pElement->points[0].x) / 3.0,
206 (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 );
207 const basegfx::B2DPoint aCtrPt2( (+2 * +pElement->points[0].x + pElement->points[1].x) / 3.0,
208 (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 );
209 rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) );
210 rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 );
211 rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 );
212 } break;
216 bool CTTextStyle::GetGlyphOutline( sal_GlyphId nGlyphId, basegfx::B2DPolyPolygon& rResult ) const
218 rResult.clear();
220 CGGlyph nCGGlyph = nGlyphId & GF_IDXMASK;
221 // XXX: this is broken if the glyph came from fallback font
222 CTFontRef pCTFont = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
223 CGPathRef xPath = CTFontCreatePathForGlyph( pCTFont, nCGGlyph, NULL );
225 GgoData aGgoData;
226 aGgoData.mpPolyPoly = &rResult;
227 CGPathApply( xPath, (void*)&aGgoData, MyCGPathApplierFunc );
228 #if 0 // TODO: does OSX ensure that the last polygon is always closed?
229 const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL };
230 MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement );
231 #endif
233 return true;
236 // -----------------------------------------------------------------------
238 void CTTextStyle::SetTextColor( const RGBAColor& rColor )
240 CGFloat aColor[] = { rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue(), rColor.GetAlpha() };
241 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
242 CGColorRef pCGColor = CGColorCreate( cs, aColor );
243 CGColorSpaceRelease( cs );
244 CFDictionarySetValue( mpStyleDict, kCTForegroundColorAttributeName, pCGColor );
245 CFRelease( pCGColor);
248 // =======================================================================
250 CTFontData::CTFontData( const ImplDevFontAttributes& rDFA, sal_IntPtr nFontId )
251 : ImplMacFontData( rDFA, nFontId )
254 // -----------------------------------------------------------------------
256 CTFontData::~CTFontData( void )
258 // TODO: any resources to release?
261 // -----------------------------------------------------------------------
263 PhysicalFontFace* CTFontData::Clone( void ) const
265 return new CTFontData( *this);
268 // -----------------------------------------------------------------------
270 ImplMacTextStyle* CTFontData::CreateMacTextStyle( const FontSelectPattern& rFSD ) const
272 return new CTTextStyle( rFSD);
275 // -----------------------------------------------------------------------
277 ImplFontEntry* CTFontData::CreateFontInstance( /*const*/ FontSelectPattern& rFSD ) const
279 return new ImplFontEntry( rFSD);
282 // -----------------------------------------------------------------------
284 int CTFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const
286 DBG_ASSERT( pTagName[4]=='\0', "CTFontData::GetFontTable with invalid tagname!\n" );
288 const CTFontTableTag nTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0);
290 // get the raw table length
291 CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>( GetFontId());
292 CTFontRef rCTFont = CTFontCreateWithFontDescriptor( pFontDesc, 0.0, NULL);
293 #if defined(MACOSX) && MACOSX_SDK_VERSION < 1080
294 const uint32_t opts( kCTFontTableOptionExcludeSynthetic );
295 #else
296 const uint32_t opts( kCTFontTableOptionNoOptions );
297 #endif
298 CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, opts);
299 CFRelease( rCTFont);
300 if( !pDataRef)
301 return 0;
303 const CFIndex nByteLength = CFDataGetLength( pDataRef);
305 // get the raw table data if requested
306 if( pResultBuf && (nByteLength > 0))
308 const CFRange aFullRange = CFRangeMake( 0, nByteLength);
309 CFDataGetBytes( pDataRef, aFullRange, (UInt8*)pResultBuf);
312 CFRelease( pDataRef);
314 return (int)nByteLength;
317 // =======================================================================
319 ImplDevFontAttributes DevFontFromCTFontDescriptor( CTFontDescriptorRef pFD, bool* bFontEnabled )
321 // all CoreText fonts are device fonts that can rotate just fine
322 ImplDevFontAttributes rDFA;
323 rDFA.mbOrientation = true;
324 rDFA.mbDevice = true;
325 rDFA.mnQuality = 0;
327 // reset the font attributes
328 rDFA.SetFamilyType( FAMILY_DONTKNOW );
329 rDFA.SetPitch( PITCH_VARIABLE );
330 rDFA.SetWidthType( WIDTH_NORMAL );
331 rDFA.SetWeight( WEIGHT_NORMAL );
332 rDFA.SetItalic( ITALIC_NONE );
333 rDFA.SetSymbolFlag( false );
335 // all scalable fonts on this platform are subsettable
336 rDFA.mbEmbeddable = false;
337 rDFA.mbSubsettable = true;
339 // get font name
340 CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute );
341 rDFA.SetFamilyName( GetOUString( pFamilyName ) );
342 // get font style
343 CFStringRef pStyleName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute );
344 rDFA.SetStyleName( GetOUString( pStyleName ) );
346 // get font-enabled status
347 if( bFontEnabled ) {
348 int bEnabled = FALSE;
349 CFNumberRef pEnabled = (CFNumberRef)CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute );
350 CFNumberGetValue( pEnabled, kCFNumberIntType, &bEnabled );
351 *bFontEnabled = bEnabled;
354 // get font attributes
355 CFDictionaryRef pAttrDict = (CFDictionaryRef)CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute );
357 // get symbolic trait
358 // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
359 SInt64 nSymbolTrait = 0;
360 CFNumberRef pSymbolNum = NULL;
361 if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, (const void**)&pSymbolNum ) ) {
362 CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait );
363 rDFA.SetSymbolFlag( ((nSymbolTrait & kCTFontClassMaskTrait) == kCTFontSymbolicClass) );
366 // get the font weight
367 double fWeight = 0;
368 CFNumberRef pWeightNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait );
369 CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight );
370 int nInt = WEIGHT_NORMAL;
371 if( fWeight > 0 ) {
372 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
373 if( nInt > WEIGHT_BLACK )
374 nInt = WEIGHT_BLACK;
375 } else if( fWeight < 0 ) {
376 nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.9));
377 if( nInt < WEIGHT_THIN )
378 nInt = WEIGHT_THIN;
380 rDFA.SetWeight( (FontWeight)nInt );
382 // get the font slant
383 double fSlant = 0;
384 CFNumberRef pSlantNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait );
385 CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
386 if( fSlant >= 0.035 )
387 rDFA.SetItalic( ITALIC_NORMAL );
389 // get width trait
390 double fWidth = 0;
391 CFNumberRef pWidthNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait );
392 CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
393 nInt = WIDTH_NORMAL;
394 if( fWidth > 0 ) {
395 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4));
396 if( nInt > WIDTH_ULTRA_EXPANDED )
397 nInt = WIDTH_ULTRA_EXPANDED;
398 } else if( fWidth < 0 ) {
399 nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5));
400 if( nInt < WIDTH_ULTRA_CONDENSED )
401 nInt = WIDTH_ULTRA_CONDENSED;
403 rDFA.SetWidthType( (FontWidth)nInt );
405 // release the attribute dict that we had copied
406 CFRelease( pAttrDict );
408 // TODO? also use the HEAD table if available to get more attributes
409 // CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
411 return rDFA;
414 static void CTFontEnumCallBack( const void* pValue, void* pContext )
416 CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
418 bool bFontEnabled;
419 ImplDevFontAttributes rDFA = DevFontFromCTFontDescriptor( pFD, &bFontEnabled );
421 if( bFontEnabled)
423 const sal_IntPtr nFontId = (sal_IntPtr)pValue;
424 CTFontData* pFontData = new CTFontData( rDFA, nFontId );
425 CTFontList* pFontList = (CTFontList*)pContext;
426 pFontList->AddFont( pFontData );
430 // =======================================================================
432 CTFontList::CTFontList()
433 : mpCTFontCollection( NULL )
434 , mpCTFontArray( NULL )
437 // -----------------------------------------------------------------------
439 CTFontList::~CTFontList()
441 CTFontContainer::const_iterator it = maFontContainer.begin();
442 for(; it != maFontContainer.end(); ++it )
443 delete (*it).second;
444 maFontContainer.clear();
446 if( mpCTFontArray )
447 CFRelease( mpCTFontArray );
448 if( mpCTFontCollection )
449 CFRelease( mpCTFontCollection );
452 // -----------------------------------------------------------------------
454 void CTFontList::AddFont( CTFontData* pFontData )
456 sal_IntPtr nFontId = pFontData->GetFontId();
457 maFontContainer[ nFontId ] = pFontData;
460 // -----------------------------------------------------------------------
462 void CTFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
464 CTFontContainer::const_iterator it = maFontContainer.begin();
465 for(; it != maFontContainer.end(); ++it )
466 rFontList.Add( (*it).second->Clone() );
469 // -----------------------------------------------------------------------
471 ImplMacFontData* CTFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
473 CTFontContainer::const_iterator it = maFontContainer.find( nFontId );
474 if( it == maFontContainer.end() )
475 return NULL;
476 return (*it).second;
479 // -----------------------------------------------------------------------
481 bool CTFontList::Init( void )
483 // enumerate available system fonts
484 static const int nMaxDictEntries = 8;
485 CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL,
486 nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
487 CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue );
488 mpCTFontCollection = CTFontCollectionCreateFromAvailableFonts( pCFDict );
489 CFRelease( pCFDict );
491 mpCTFontArray = CTFontCollectionCreateMatchingFontDescriptors( mpCTFontCollection );
492 const int nFontCount = CFArrayGetCount( mpCTFontArray );
493 const CFRange aFullRange = CFRangeMake( 0, nFontCount );
494 CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this );
496 return true;
499 // =======================================================================
501 SystemFontList* GetCoretextFontList( void )
503 CTFontList* pList = new CTFontList();
504 if( !pList->Init() ) {
505 delete pList;
506 return NULL;
509 return pList;
512 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */