xref: /AOO41X/main/vcl/aqua/source/gdi/ctfonts.cxx (revision 88e7420bd8e28c20c0032721cf5f459b86d7e953)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 // MARKER(update_precomp.py): autogen include statement, do not remove
23 #include "precompiled_vcl.hxx"
24 
25 #include "impfont.hxx"
26 #include "outfont.hxx"
27 #include "sallayout.hxx"
28 
29 #include "aqua/salinst.h"
30 #include "aqua/saldata.hxx"
31 #include "aqua/salgdi.h"
32 #include "ctfonts.hxx"
33 
34 #include "basegfx/polygon/b2dpolygon.hxx"
35 #include "basegfx/matrix/b2dhommatrix.hxx"
36 
37 #ifndef DISABLE_CORETEXT_DYNLOAD
38 #include <dlfcn.h>
39 #endif
40 
41 // =======================================================================
42 
43 // CoreText specific physically available font face
44 class CTFontData
45 :   public ImplMacFontData
46 {
47 public:
48     explicit                CTFontData( const ImplDevFontAttributes&, sal_IntPtr nFontId );
49     virtual                 ~CTFontData( void );
50     virtual ImplFontData*   Clone( void ) const;
51 
52     virtual ImplMacTextStyle*   CreateMacTextStyle( const ImplFontSelectData& ) const;
53     virtual ImplFontEntry*      CreateFontInstance( /*const*/ ImplFontSelectData& ) const;
54     virtual int                 GetFontTable( const char pTagName[5], unsigned char* ) const;
55 };
56 
57 // =======================================================================
58 
59 class CTFontList
60 :   public SystemFontList
61 {
62 public:
63     explicit    CTFontList( void );
64     virtual     ~CTFontList( void );
65 
66     bool        Init( void );
67     void        AddFont( CTFontData* );
68 
69     virtual void    AnnounceFonts( ImplDevFontList& ) const;
70     virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr ) const;
71 
72 private:
73     CTFontCollectionRef mpCTFontCollection;
74     CFArrayRef mpCTFontArray;
75 
76     typedef std::hash_map<sal_IntPtr,CTFontData*> CTFontContainer;
77     CTFontContainer maFontContainer;
78 };
79 
80 // =======================================================================
81 
82 CTTextStyle::CTTextStyle( const ImplFontSelectData& rFSD )
83 :   ImplMacTextStyle( rFSD )
84 ,   mpStyleDict( NULL )
85 {
86     mpFontData = (CTFontData*)rFSD.mpFontData;
87     const ImplFontSelectData* const pReqFont = &rFSD;
88 
89     double fScaledFontHeight = pReqFont->mfExactHeight;
90 #if 0 // TODO: does CoreText need font size limiting???
91     static const float fMaxFontHeight = 144.0; // TODO: is there a limit for CoreText?
92     if( fScaledFontHeight > fMaxFontHeight )
93     {
94         mfFontScale = fScaledFontHeight / fMaxFontHeight;
95         fScaledFontHeight = fMaxFontHeight;
96     }
97 #endif
98 
99     // convert font rotation to radian
100     mfFontRotation = pReqFont->mnOrientation * (M_PI / 1800.0);
101 
102     // handle font stretching if any
103     const CGAffineTransform* pMatrix = NULL;
104     CGAffineTransform aMatrix;
105     if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) )
106     {
107         mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
108         aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F );
109         pMatrix = &aMatrix;
110     }
111 
112     // create the style object for CoreText font attributes
113     static const CFIndex nMaxDictSize = 16; // TODO: does this really suffice?
114     mpStyleDict = CFDictionaryCreateMutable( NULL, nMaxDictSize,
115         &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
116 
117     // set some default styles: no kerning, regular ligatures
118     static const CGFloat fValZero = 0.0;
119     CFNumberRef pCFFloatNumZero = CFNumberCreate( NULL, kCFNumberFloatType, &fValZero );
120     CFDictionarySetValue( mpStyleDict, kCTKernAttributeName, pCFFloatNumZero );
121     CFRelease( pCFFloatNumZero);
122     static const int nValOne = 1;
123     CFNumberRef pCFIntNumOne = CFNumberCreate( NULL, kCFNumberIntType, &nValOne );
124     CFDictionarySetValue( mpStyleDict, kCTLigatureAttributeName, pCFIntNumOne );
125     CFRelease( pCFIntNumOne);
126     CFBooleanRef pCFVertBool = pReqFont->mbVertical ? kCFBooleanTrue : kCFBooleanFalse;
127     CFDictionarySetValue( mpStyleDict, kCTVerticalFormsAttributeName, pCFVertBool );
128 
129     CTFontDescriptorRef pFontDesc = (CTFontDescriptorRef)mpFontData->GetFontId();
130     CTFontRef pNewCTFont = CTFontCreateWithFontDescriptor( pFontDesc, fScaledFontHeight, pMatrix );
131     CFDictionarySetValue( mpStyleDict, kCTFontAttributeName, pNewCTFont );
132     CFRelease( pNewCTFont);
133 
134 #if 0 // LastResort is implicit in CoreText's font cascading
135     const void* aGFBDescriptors[] = { CTFontDescriptorCreateWithNameAndSize( CFSTR("LastResort"), 0) }; // TODO: use the full GFB list
136     const int nGfbCount = sizeof(aGFBDescriptors) / sizeof(*aGFBDescriptors);
137     CFArrayRef pGfbList = CFArrayCreate( NULL, aGFBDescriptors, nGfbCount, &kCFTypeArrayCallBacks);
138     CFDictionaryAddValue( mpStyleDict, kCTFontCascadeListAttribute, pGfbList);
139     CFRelease( pGfbList);
140 #endif
141 }
142 
143 // -----------------------------------------------------------------------
144 
145 CTTextStyle::~CTTextStyle( void )
146 {
147     if( mpStyleDict )
148         CFRelease( mpStyleDict );
149 }
150 
151 // -----------------------------------------------------------------------
152 
153 void CTTextStyle::GetFontMetric( float fDPIY, ImplFontMetricData& rMetric ) const
154 {
155     // get the matching CoreText font handle
156     // TODO: is it worth it to cache the CTFontRef in SetFont() and reuse it here?
157     CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
158 
159     const double fPixelSize = (mfFontScale * fDPIY);
160     rMetric.mnAscent       = lrint( CTFontGetAscent( aCTFontRef ) * fPixelSize);
161     rMetric.mnDescent      = lrint( CTFontGetDescent( aCTFontRef ) * fPixelSize);
162     rMetric.mnIntLeading   = lrint( CTFontGetLeading( aCTFontRef ) * fPixelSize);
163     rMetric.mnExtLeading   = 0;
164     // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
165     // setting this width to the pixel height of the fontsize is good enough
166     // it also makes the calculation of the stretch factor simple
167     rMetric.mnWidth        = lrint( CTFontGetSize( aCTFontRef ) * fPixelSize * mfFontStretch);
168 
169     // all CoreText fonts are scalable
170     rMetric.mbScalableFont = true;
171     // TODO: check if any kerning is supported
172     rMetric.mbKernableFont = true;
173 }
174 
175 // -----------------------------------------------------------------------
176 
177 bool CTTextStyle::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) const
178 {
179     const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
180     CGGlyph nCGGlyph = aGlyphId & GF_IDXMASK; // NOTE: CoreText handles glyph fallback itself
181     CTFontRef aCTFontRef = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
182 
183     const CTFontOrientation aFontOrientation = kCTFontDefaultOrientation; // TODO: horz/vert
184     const CGRect aCGRect = rCT.FontGetBoundingRectsForGlyphs( aCTFontRef, aFontOrientation, &nCGGlyph, NULL, 1 );
185 
186     rRect.Left()   = lrint( mfFontScale * aCGRect.origin.x );
187     rRect.Top()    = lrint( mfFontScale * aCGRect.origin.y );
188     rRect.Right()  = lrint( mfFontScale * (aCGRect.origin.x + aCGRect.size.width) );
189     rRect.Bottom() = lrint( mfFontScale * (aCGRect.origin.y + aCGRect.size.height) );
190     return true;
191 }
192 
193 // -----------------------------------------------------------------------
194 
195 // callbacks from CTFontCreatePathForGlyph+CGPathApply for GetGlyphOutline()
196 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
197 
198 static void MyCGPathApplierFunc( void* pData, const CGPathElement* pElement )
199 {
200     basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
201     const int nPointCount = rPolygon.count();
202 
203     switch( pElement->type )
204     {
205     case kCGPathElementCloseSubpath:
206     case kCGPathElementMoveToPoint:
207         if( nPointCount > 0 ) {
208             static_cast<GgoData*>(pData)->mpPolyPoly->append( rPolygon );
209             rPolygon.clear();
210         }
211         // fall through for kCGPathElementMoveToPoint:
212         if( pElement->type != kCGPathElementMoveToPoint )
213             break;
214     case kCGPathElementAddLineToPoint:
215         rPolygon.append( basegfx::B2DPoint( +pElement->points[0].x, -pElement->points[0].y ) );
216         break;
217     case kCGPathElementAddCurveToPoint:
218         rPolygon.append( basegfx::B2DPoint( +pElement->points[2].x, -pElement->points[2].y ) );
219         rPolygon.setNextControlPoint( nPointCount-1, basegfx::B2DPoint( pElement->points[0].x, -pElement->points[0].y ) );
220         rPolygon.setPrevControlPoint( nPointCount+0, basegfx::B2DPoint( pElement->points[1].x, -pElement->points[1].y ) );
221         break;
222     case kCGPathElementAddQuadCurveToPoint: {
223         const basegfx::B2DPoint aStartPt = rPolygon.getB2DPoint( nPointCount-1 );
224         const basegfx::B2DPoint aCtrPt1( (aStartPt.getX() + 2* pElement->points[0].x) / 3.0,
225                     (aStartPt.getY() - 2 * pElement->points[0].y) / 3.0 );
226         const basegfx::B2DPoint aCtrPt2( (+2 * +pElement->points[0].x + pElement->points[1].x) / 3.0,
227                 (-2 * pElement->points[0].y - pElement->points[1].y) / 3.0 );
228         rPolygon.append( basegfx::B2DPoint( +pElement->points[1].x, -pElement->points[1].y ) );
229         rPolygon.setNextControlPoint( nPointCount-1, aCtrPt1 );
230         rPolygon.setPrevControlPoint( nPointCount+0, aCtrPt2 );
231         } break;
232     }
233 }
234 
235 bool CTTextStyle::GetGlyphOutline( sal_GlyphId aGlyphId, basegfx::B2DPolyPolygon& rResult ) const
236 {
237     rResult.clear();
238 
239     const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
240     // TODO: GF_FONTMASK if using non-native glyph fallback
241     CGGlyph nCGGlyph = aGlyphId & GF_IDXMASK;
242     CTFontRef pCTFont = (CTFontRef)CFDictionaryGetValue( mpStyleDict, kCTFontAttributeName );
243     CGPathRef xPath = rCT.FontCreatePathForGlyph( pCTFont, nCGGlyph, NULL );
244 
245     GgoData aGgoData;
246     aGgoData.mpPolyPoly = &rResult;
247     CGPathApply( xPath, (void*)&aGgoData, MyCGPathApplierFunc );
248 #if 0 // TODO: does OSX ensure that the last polygon is always closed?
249     const CGPathElement aClosingElement = { kCGPathElementCloseSubpath, NULL };
250     MyCGPathApplierFunc( (void*)&aGgoData, &aClosingElement );
251 #endif
252 
253     // apply the font scale
254     if( mfFontScale != 1.0 ) {
255         basegfx::B2DHomMatrix aScale;
256         aScale.scale( +mfFontScale, +mfFontScale );
257         rResult.transform( aScale );
258     }
259 
260     return true;
261 }
262 
263 // -----------------------------------------------------------------------
264 
265 void CTTextStyle::SetTextColor( const RGBAColor& rColor )
266 {
267 #if (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_5)
268     CGColorRef pCGColor = CGColorCreateGenericRGB( rColor.GetRed(),
269         rColor.GetGreen(), rColor.GetBlue(), rColor.GetAlpha() );
270 #else // for builds on OSX 10.4 SDK
271     const CGColorSpaceRef pCGColorSpace = GetSalData()->mxRGBSpace;
272     CGColorRef pCGColor = CGColorCreate( pCGColorSpace, rColor.AsArray() );
273 #endif
274     CFDictionarySetValue( mpStyleDict, kCTForegroundColorAttributeName, pCGColor );
275     CFRelease( pCGColor);
276 }
277 
278 // =======================================================================
279 
280 CTFontData::CTFontData( const ImplDevFontAttributes& rDFA, sal_IntPtr nFontId )
281 :   ImplMacFontData( rDFA, nFontId )
282 {}
283 
284 // -----------------------------------------------------------------------
285 
286 CTFontData::~CTFontData( void )
287 {
288     // TODO: any resources to release?
289 }
290 
291 // -----------------------------------------------------------------------
292 
293 ImplFontData* CTFontData::Clone( void ) const
294 {
295     return new CTFontData( *this);
296 }
297 
298 // -----------------------------------------------------------------------
299 
300 ImplMacTextStyle* CTFontData::CreateMacTextStyle( const ImplFontSelectData& rFSD ) const
301 {
302     return new CTTextStyle( rFSD);
303 }
304 
305 // -----------------------------------------------------------------------
306 
307 ImplFontEntry* CTFontData::CreateFontInstance( /*const*/ ImplFontSelectData& rFSD ) const
308 {
309     return new ImplFontEntry( rFSD);
310 }
311 
312 // -----------------------------------------------------------------------
313 
314 int CTFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const
315 {
316     DBG_ASSERT( pTagName[4]=='\0', "CTFontData::GetFontTable with invalid tagname!\n" );
317 
318     const CTFontTableTag nTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0);
319 
320     // get the raw table length
321     CTFontDescriptorRef pFontDesc = reinterpret_cast<CTFontDescriptorRef>( GetFontId());
322     CTFontRef rCTFont = CTFontCreateWithFontDescriptor( pFontDesc, 0.0, NULL);
323     CFDataRef pDataRef = CTFontCopyTable( rCTFont, nTagCode, kCTFontTableOptionExcludeSynthetic);
324     CFRelease( rCTFont);
325     if( !pDataRef)
326         return 0;
327 
328     const CFIndex nByteLength = CFDataGetLength( pDataRef);
329 
330     // get the raw table data if requested
331     if( pResultBuf && (nByteLength > 0))
332     {
333         const CFRange aFullRange = CFRangeMake( 0, nByteLength);
334         CFDataGetBytes( pDataRef, aFullRange, (UInt8*)pResultBuf);
335     }
336 
337     CFRelease( pDataRef);
338 
339     return (int)nByteLength;
340 }
341 
342 // =======================================================================
343 
344 static void CTFontEnumCallBack( const void* pValue, void* pContext )
345 {
346     CTFontDescriptorRef pFD = static_cast<CTFontDescriptorRef>(pValue);
347 
348     // all CoreText fonts are device fonts that can rotate just fine
349     ImplDevFontAttributes rDFA;
350     rDFA.mbOrientation = true;
351     rDFA.mbDevice      = true;
352     rDFA.mnQuality     = 0;
353 
354     // reset the font attributes
355     rDFA.meFamily     = FAMILY_DONTKNOW;
356     rDFA.mePitch      = PITCH_VARIABLE;
357     rDFA.meWidthType  = WIDTH_NORMAL;
358     rDFA.meWeight     = WEIGHT_NORMAL;
359     rDFA.meItalic     = ITALIC_NONE;
360     rDFA.mbSymbolFlag = false;
361 
362     // all scalable fonts on this platform are subsettable
363     rDFA.mbEmbeddable = false;
364     rDFA.mbSubsettable = true;
365 
366     // get font name
367     // TODO: use kCTFontDisplayNameAttribute instead???
368     CFStringRef pFamilyName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontFamilyNameAttribute );
369     rDFA.maName = GetOUString( pFamilyName );
370     // get font style
371     CFStringRef pStyleName = (CFStringRef)CTFontDescriptorCopyAttribute( pFD, kCTFontStyleNameAttribute );
372     rDFA.maStyleName = GetOUString( pStyleName );
373 
374     // get font-enabled status
375     int bFontEnabled = FALSE;
376     CFNumberRef pFontEnabled = (CFNumberRef)CTFontDescriptorCopyAttribute( pFD, kCTFontEnabledAttribute );
377     CFNumberGetValue( pFontEnabled, kCFNumberIntType, &bFontEnabled );
378 
379     // get font attributes
380     CFDictionaryRef pAttrDict = (CFDictionaryRef)CTFontDescriptorCopyAttribute( pFD, kCTFontTraitsAttribute );
381 
382     // get symbolic trait
383     // TODO: use other traits such as MonoSpace/Condensed/Expanded or Vertical too
384     SInt64 nSymbolTrait = 0;
385     CFNumberRef pSymbolNum = NULL;
386     if( CFDictionaryGetValueIfPresent( pAttrDict, kCTFontSymbolicTrait, (const void**)&pSymbolNum ) ) {
387         CFNumberGetValue( pSymbolNum, kCFNumberSInt64Type, &nSymbolTrait );
388         rDFA.mbSymbolFlag = ((nSymbolTrait & kCTFontClassMaskTrait) == kCTFontSymbolicClass);
389     }
390 
391     // get the font weight
392     double fWeight = 0;
393     CFNumberRef pWeightNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWeightTrait );
394     CFNumberGetValue( pWeightNum, kCFNumberDoubleType, &fWeight );
395     int nInt = WEIGHT_NORMAL;
396     if( fWeight > 0 ) {
397         nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_BLACK - WEIGHT_NORMAL)/0.68));
398         if( nInt > WEIGHT_BLACK )
399             nInt = WEIGHT_BLACK;
400     } else if( fWeight < 0 ) {
401         nInt = rint(WEIGHT_NORMAL + fWeight * ((WEIGHT_NORMAL - WEIGHT_THIN)/0.9));
402         if( nInt < WEIGHT_THIN )
403             nInt = WEIGHT_THIN;
404     }
405     rDFA.meWeight = (FontWeight)nInt;
406 
407     // get the font slant
408     double fSlant = 0;
409     CFNumberRef pSlantNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontSlantTrait );
410     CFNumberGetValue( pSlantNum, kCFNumberDoubleType, &fSlant );
411     if( fSlant >= 0.035 )
412         rDFA.meItalic = ITALIC_NORMAL;
413 
414     // get width trait
415     double fWidth = 0;
416     CFNumberRef pWidthNum = (CFNumberRef)CFDictionaryGetValue( pAttrDict, kCTFontWidthTrait );
417     CFNumberGetValue( pWidthNum, kCFNumberDoubleType, &fWidth );
418     nInt = WIDTH_NORMAL;
419     if( fWidth > 0 ) {
420         nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_ULTRA_EXPANDED - WIDTH_NORMAL)/0.4));
421         if( nInt > WIDTH_ULTRA_EXPANDED )
422             nInt = WIDTH_ULTRA_EXPANDED;
423     } else if( fWidth < 0 ) {
424         nInt = rint( WIDTH_NORMAL + fWidth * ((WIDTH_NORMAL - WIDTH_ULTRA_CONDENSED)/0.5));
425         if( nInt < WIDTH_ULTRA_CONDENSED )
426             nInt = WIDTH_ULTRA_CONDENSED;
427     }
428     rDFA.meWidthType = (FontWidth)nInt;
429 
430     // release the attribute dict that we had copied
431     CFRelease( pAttrDict );
432 
433     // TODO? also use the HEAD table if available to get more attributes
434 //  CFDataRef CTFontCopyTable( CTFontRef, kCTFontTableHead, /*kCTFontTableOptionNoOptions*/kCTFontTableOptionExcludeSynthetic );
435 
436 #if (OSL_DEBUG_LEVEL >= 1)
437     // update font attributes using the font's postscript name
438     ImplDevFontAttributes rDFA2;
439     CTFontRef pFont = CTFontCreateWithFontDescriptor( pFD, 0.0, NULL );
440     CFStringRef pPSName = CTFontCopyPostScriptName( pFont );
441     const String aPSName = GetOUString( pPSName );
442 
443     rDFA2.mbSymbolFlag = false;
444     rDFA2.mePitch      = PITCH_VARIABLE;
445     rDFA2.meWidthType  = WIDTH_NORMAL;
446     rDFA2.meWeight     = WEIGHT_NORMAL;
447     rDFA2.meItalic     = ITALIC_NONE;
448 
449     UpdateAttributesFromPSName( aPSName, rDFA2 );
450     CFRelease( pPSName );
451     CFRelease( pFont );
452 
453     // show the font details and compare the CTFontDescriptor vs. PSName traits
454     char cMatch = (rDFA.mbSymbolFlag==rDFA2.mbSymbolFlag);
455     cMatch &= (rDFA.meWeight==rDFA2.meWeight);
456     cMatch &= ((rDFA.meItalic==ITALIC_NONE) == (rDFA2.meItalic==ITALIC_NONE));
457     cMatch &= (rDFA.meWidthType==rDFA2.meWidthType);
458     cMatch = cMatch ? '.' : '#';
459 
460     char aFN[256], aSN[256];
461     CFStringGetCString( pFamilyName, aFN, sizeof(aFN), kCFStringEncodingUTF8 );
462     CFStringGetCString( pStyleName, aSN, sizeof(aSN), kCFStringEncodingUTF8 );
463 
464     const ByteString aPSCName( aPSName, RTL_TEXTENCODING_UTF8 );
465     const char* aPN = aPSCName.GetBuffer();
466     printf("\tCTFont_%d%x%d%d_%c_%d%x%d%d ena=%d s=%02d b=%+.2f i=%+.2f w=%+.2f (\"%s\", \"%s\", \"%s\")\n",
467         (int)rDFA.mbSymbolFlag,(int)rDFA.meWeight,(int)rDFA.meItalic,(int)rDFA.meWidthType,
468         cMatch,
469         (int)rDFA2.mbSymbolFlag,(int)rDFA2.meWeight,(int)rDFA2.meItalic,(int)rDFA2.meWidthType,
470         bFontEnabled,
471         (int)(nSymbolTrait>>kCTFontClassMaskShift),fWeight,fSlant,fWidth,aFN,aSN,aPN);
472 #endif // (OSL_DEBUG_LEVEL >= 1)
473 
474     if( bFontEnabled)
475     {
476         const sal_IntPtr nFontId = (sal_IntPtr)pValue;
477         CTFontData* pFontData = new CTFontData( rDFA, nFontId );
478         CTFontList* pFontList = (CTFontList*)pContext;
479         pFontList->AddFont( pFontData );
480     }
481 }
482 
483 // =======================================================================
484 
485 CTFontList::CTFontList()
486 :   mpCTFontCollection( NULL )
487 ,   mpCTFontArray( NULL )
488 {}
489 
490 // -----------------------------------------------------------------------
491 
492 CTFontList::~CTFontList()
493 {
494     CTFontContainer::const_iterator it = maFontContainer.begin();
495     for(; it != maFontContainer.end(); ++it )
496         delete (*it).second;
497     maFontContainer.clear();
498 
499     if( mpCTFontArray )
500         CFRelease( mpCTFontArray );
501     if( mpCTFontCollection )
502         CFRelease( mpCTFontCollection );
503 }
504 
505 // -----------------------------------------------------------------------
506 
507 void CTFontList::AddFont( CTFontData* pFontData )
508 {
509     sal_IntPtr nFontId = pFontData->GetFontId();
510     maFontContainer[ nFontId ] = pFontData;
511 }
512 
513 // -----------------------------------------------------------------------
514 
515 void CTFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
516 {
517     CTFontContainer::const_iterator it = maFontContainer.begin();
518     for(; it != maFontContainer.end(); ++it )
519         rFontList.Add( (*it).second->Clone() );
520 }
521 
522 // -----------------------------------------------------------------------
523 
524 ImplMacFontData* CTFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
525 {
526     CTFontContainer::const_iterator it = maFontContainer.find( nFontId );
527     if( it == maFontContainer.end() )
528         return NULL;
529     return (*it).second;
530 }
531 
532 // -----------------------------------------------------------------------
533 
534 bool CTFontList::Init( void )
535 {
536 #ifndef DISABLE_CORETEXT_DYNLOAD
537     // check availability of the CoreText API
538     const DynCoreTextSyms& rCT = DynCoreTextSyms::get();
539     if( !rCT.IsActive() )
540         return false;
541 #endif // DISABLE_CORETEXT_DYNLOAD
542 
543     // enumerate available system fonts
544     static const int nMaxDictEntries = 8;
545     CFMutableDictionaryRef pCFDict = CFDictionaryCreateMutable( NULL,
546         nMaxDictEntries, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks );
547     CFDictionaryAddValue( pCFDict, kCTFontCollectionRemoveDuplicatesOption, kCFBooleanTrue );
548     mpCTFontCollection = rCT.FontCollectionCreateFromAvailableFonts( pCFDict );
549     CFRelease( pCFDict );
550 
551     mpCTFontArray = rCT.FontCollectionCreateMatchingFontDescriptors( mpCTFontCollection );
552     const int nFontCount = CFArrayGetCount( mpCTFontArray );
553     const CFRange aFullRange = CFRangeMake( 0, nFontCount );
554     CFArrayApplyFunction( mpCTFontArray, aFullRange, CTFontEnumCallBack, this );
555 
556     return true;
557 }
558 
559 // =======================================================================
560 
561 #ifndef DISABLE_CORETEXT_DYNLOAD
562 
563 DynCoreTextSyms::DynCoreTextSyms( void )
564 {
565     mbIsActive = false;
566 
567     // check if CoreText has been explicitely disabled
568     const char* pEnvStr = getenv( "SAL_DISABLE_CORETEXT");
569     if( pEnvStr && (pEnvStr[0] != '0') )
570         return;
571 
572     // check CoreText version
573     GetCoreTextVersion = (uint32_t(*)(void))dlsym( RTLD_DEFAULT, "CTGetCoreTextVersion");
574     if( !GetCoreTextVersion) return;
575 
576     const uint32_t nCTVersion = GetCoreTextVersion();
577     static const uint32_t mykCTVersionNumber10_5 = 0x00020000;
578     if( nCTVersion < mykCTVersionNumber10_5)
579         return;
580 
581     // load CoreText symbols dynamically
582     LineGetTrailingWhitespaceWidth = (double(*)(CTLineRef))dlsym( RTLD_DEFAULT, "CTLineGetTrailingWhitespaceWidth");
583     if( !LineGetTrailingWhitespaceWidth) return;
584 
585     LineCreateJustifiedLine = (CTLineRef(*)(CTLineRef,CGFloat,double))dlsym( RTLD_DEFAULT, "CTLineCreateJustifiedLine");
586     if( !LineCreateJustifiedLine) return;
587 
588     LineGetOffsetForStringIndex = (CGFloat(*)(CTLineRef,CFIndex,CGFloat*))dlsym( RTLD_DEFAULT, "CTLineGetOffsetForStringIndex");
589     if( !LineGetOffsetForStringIndex) return;
590 
591     LineGetGlyphRuns = (CFArrayRef(*)(CTLineRef))dlsym( RTLD_DEFAULT, "CTLineGetGlyphRuns");
592     if( !LineGetGlyphRuns) return;
593 
594     RunGetGlyphCount = (CFIndex(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetGlyphCount");
595     if( !RunGetGlyphCount) return;
596 
597     RunGetGlyphsPtr = (const CGGlyph*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetGlyphsPtr");
598     if( !RunGetGlyphsPtr) return;
599 
600     RunGetPositionsPtr = (const CGPoint*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetPositionsPtr");
601     if( !RunGetPositionsPtr) return;
602 
603     RunGetAdvancesPtr = (const CGSize*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetAdvancesPtr");
604     if( !RunGetAdvancesPtr) return;
605 
606     RunGetStringIndicesPtr = (const CFIndex*(*)(CTRunRef))dlsym( RTLD_DEFAULT, "CTRunGetStringIndicesPtr");
607     if( !RunGetStringIndicesPtr) return;
608 
609     FontCollectionCreateFromAvailableFonts = (CTFontCollectionRef(*)(CFDictionaryRef))dlsym( RTLD_DEFAULT, "CTFontCollectionCreateFromAvailableFonts");
610     if( !FontCollectionCreateFromAvailableFonts) return;
611 
612     FontCollectionCreateMatchingFontDescriptors = (CFArrayRef(*)(CTFontCollectionRef))dlsym( RTLD_DEFAULT, "CTFontCollectionCreateMatchingFontDescriptors");
613     if( !FontCollectionCreateMatchingFontDescriptors) return;
614 
615     FontCreatePathForGlyph = (CGPathRef(*)(CTFontRef,CGGlyph,const CGAffineTransform*))dlsym( RTLD_DEFAULT, "CTFontCreatePathForGlyph");
616     if( !FontCreatePathForGlyph) return;
617 
618     FontGetBoundingRectsForGlyphs = (CGRect(*)(CTFontRef,CTFontOrientation,CGGlyph*,CGRect*,CFIndex))dlsym( RTLD_DEFAULT, "CTFontGetBoundingRectsForGlyphs");
619     if( !FontGetBoundingRectsForGlyphs) return;
620 
621     mbIsActive = true;
622 }
623 
624 // -----------------------------------------------------------------------
625 
626 const DynCoreTextSyms& DynCoreTextSyms::get( void )
627 {
628     static DynCoreTextSyms aCT;
629     return aCT;
630 }
631 
632 #endif // DISABLE_CORETEXT_DYNLOAD
633 
634 // =======================================================================
635 
636 SystemFontList* GetCoretextFontList( void )
637 {
638     CTFontList* pList = new CTFontList();
639     if( !pList->Init() ) {
640         delete pList;
641         return NULL;
642     }
643 
644     return pList;
645 }
646 
647 // =======================================================================
648 
649