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