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