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