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