xref: /AOO41X/main/vcl/aqua/source/gdi/atsfonts.cxx (revision e26449d3b24846fb7600c5866a6e7ae082e782f2)
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 <boost/assert.hpp>
26 #include <vector>
27 #include <hash_map>
28 #include <set>
29 
30 #include "salgdi.h"
31 #include "atsfonts.hxx"
32 
33 #include "vcl/svapp.hxx"
34 #include "vcl/impfont.hxx"
35 
36 #include "basegfx/polygon/b2dpolygon.hxx"
37 #include "basegfx/matrix/b2dhommatrix.hxx"
38 
39 typedef GlyphID ATSGlyphID;
40 
41 // =======================================================================
42 
43 // mac specific physically available font face
44 class AtsFontData
45 :   public ImplMacFontData
46 {
47 public:
48     explicit                AtsFontData( const ImplDevFontAttributes&, ATSUFontID );
49     virtual                 ~AtsFontData( 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 AtsFontList
60 :   public SystemFontList
61 {
62 public:
63     explicit    AtsFontList( void );
64     virtual     ~AtsFontList( void );
65 
66     virtual void            AnnounceFonts( ImplDevFontList& ) const;
67     virtual ImplMacFontData* GetFontDataFromId( sal_IntPtr nFontId ) const;
68 
69 private:
70     typedef std::hash_map<sal_IntPtr,AtsFontData*> AtsFontContainer;
71     AtsFontContainer maFontContainer;
72 
73     void InitGlyphFallbacks( void );
74     ATSUFontFallbacks   maFontFallbacks;
75 };
76 
77 // =======================================================================
78 
AtsFontData(const ImplDevFontAttributes & rDFA,ATSUFontID nFontId)79 AtsFontData::AtsFontData( const ImplDevFontAttributes& rDFA, ATSUFontID nFontId )
80 :   ImplMacFontData( rDFA, (sal_IntPtr)nFontId )
81 {}
82 
83 // -----------------------------------------------------------------------
84 
~AtsFontData(void)85 AtsFontData::~AtsFontData( void )
86 {}
87 
88 // -----------------------------------------------------------------------
89 
Clone(void) const90 ImplFontData* AtsFontData::Clone( void ) const
91 {
92     AtsFontData* pClone = new AtsFontData(*this);
93     return pClone;
94 }
95 
96 // -----------------------------------------------------------------------
97 
CreateMacTextStyle(const ImplFontSelectData & rFSD) const98 ImplMacTextStyle* AtsFontData::CreateMacTextStyle( const ImplFontSelectData& rFSD ) const
99 {
100     return new AtsTextStyle( rFSD );
101 }
102 
103 // -----------------------------------------------------------------------
104 
CreateFontInstance(ImplFontSelectData & rFSD) const105 ImplFontEntry* AtsFontData::CreateFontInstance( /*const*/ ImplFontSelectData& rFSD ) const
106 {
107     return new ImplFontEntry( rFSD );
108 }
109 
110 // -----------------------------------------------------------------------
111 
GetFontTable(const char pTagName[5],unsigned char * pResultBuf) const112 int AtsFontData::GetFontTable( const char pTagName[5], unsigned char* pResultBuf ) const
113 {
114     DBG_ASSERT( aTagName[4]=='\0', "AtsFontData::GetFontTable with invalid tagname!\n" );
115 
116     const FourCharCode pTagCode = (pTagName[0]<<24) + (pTagName[1]<<16) + (pTagName[2]<<8) + (pTagName[3]<<0);
117 
118     // get the byte size of the raw table
119     ATSFontRef rATSFont = FMGetATSFontRefFromFont( (ATSUFontID)mnFontId );
120     ByteCount nBufSize = 0;
121     OSStatus eStatus = ATSFontGetTable( rATSFont, pTagCode, 0, 0, NULL, &nBufSize );
122     if( eStatus != noErr )
123         return 0;
124 
125     // get the raw table data if requested
126     if( pResultBuf && (nBufSize > 0))
127     {
128         ByteCount nRawLength = 0;
129         eStatus = ATSFontGetTable( rATSFont, pTagCode, 0, nBufSize, (void*)pResultBuf, &nRawLength );
130         if( eStatus != noErr )
131             return 0;
132         DBG_ASSERT( (nBufSize==nRawLength), "AtsFontData::GetFontTable ByteCount mismatch!\n");
133     }
134 
135     return nBufSize;
136 }
137 
138 // =======================================================================
139 
AtsTextStyle(const ImplFontSelectData & rFSD)140 AtsTextStyle::AtsTextStyle( const ImplFontSelectData& rFSD )
141 :   ImplMacTextStyle( rFSD )
142 {
143     // create the style object for ATSUI font attributes
144     ATSUCreateStyle( &maATSUStyle );
145     const ImplFontSelectData* const pReqFont = &rFSD;
146 
147     mpFontData = (AtsFontData*)rFSD.mpFontData;
148 
149     // limit the ATS font size to avoid Fixed16.16 overflows
150     double fScaledFontHeight = pReqFont->mfExactHeight;
151     static const float fMaxFontHeight = 144.0;
152     if( fScaledFontHeight > fMaxFontHeight )
153     {
154         mfFontScale = fScaledFontHeight / fMaxFontHeight;
155         fScaledFontHeight = fMaxFontHeight;
156     }
157 
158     // convert font rotation to radian
159     mfFontRotation = pReqFont->mnOrientation * (M_PI / 1800.0);
160 
161     // determine if font stretching is needed
162     if( (pReqFont->mnWidth != 0) && (pReqFont->mnWidth != pReqFont->mnHeight) )
163     {
164         mfFontStretch = (float)pReqFont->mnWidth / pReqFont->mnHeight;
165         // set text style to stretching matrix
166         CGAffineTransform aMatrix = CGAffineTransformMakeScale( mfFontStretch, 1.0F );
167         const ATSUAttributeTag aMatrixTag = kATSUFontMatrixTag;
168         const ATSUAttributeValuePtr aAttr = &aMatrix;
169         const ByteCount aMatrixBytes = sizeof(aMatrix);
170         /*OSStatus eStatus =*/ ATSUSetAttributes( maATSUStyle, 1, &aMatrixTag, &aMatrixBytes, &aAttr );
171     }
172 }
173 
174 // -----------------------------------------------------------------------
175 
~AtsTextStyle(void)176 AtsTextStyle::~AtsTextStyle( void )
177 {
178     ATSUDisposeStyle( maATSUStyle );
179 }
180 
181 // -----------------------------------------------------------------------
182 
GetFontMetric(float fDPIY,ImplFontMetricData & rMetric) const183 void AtsTextStyle::GetFontMetric( float fDPIY, ImplFontMetricData& rMetric ) const
184 {
185     // get the font metrics (in point units)
186     // of the font that has eventually been size-limited
187 
188     // get the matching ATSU font handle
189     ATSUFontID fontId;
190     OSStatus err = ::ATSUGetAttribute( maATSUStyle, kATSUFontTag, sizeof(ATSUFontID), &fontId, 0 );
191     DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font id\n");
192 
193     ATSFontMetrics aMetrics;
194     ATSFontRef rFont = FMGetATSFontRefFromFont( fontId );
195     err = ATSFontGetHorizontalMetrics ( rFont, kATSOptionFlagsDefault, &aMetrics );
196     DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font metrics\n");
197     if( err != noErr )
198         return;
199 
200     // all ATS fonts are scalable fonts
201     rMetric.mbScalableFont = true;
202     // TODO: check if any kerning is possible
203     rMetric.mbKernableFont = true;
204 
205     // convert into VCL font metrics (in unscaled pixel units)
206 
207     Fixed ptSize;
208     err = ATSUGetAttribute( maATSUStyle, kATSUSizeTag, sizeof(Fixed), &ptSize, 0);
209     DBG_ASSERT( (err==noErr), "AquaSalGraphics::GetFontMetric() : could not get font size\n");
210     const double fPointSize = Fix2X( ptSize );
211 
212     // convert quartz units to pixel units
213     // please see the comment in AquaSalGraphics::SetFont() for details
214     const double fPixelSize = (mfFontScale * fDPIY * fPointSize);
215     rMetric.mnAscent       = static_cast<long>(+aMetrics.ascent  * fPixelSize + 0.5);
216     rMetric.mnDescent      = static_cast<long>(-aMetrics.descent * fPixelSize + 0.5);
217     const long nExtDescent = static_cast<long>((-aMetrics.descent + aMetrics.leading) * fPixelSize + 0.5);
218     rMetric.mnExtLeading   = nExtDescent - rMetric.mnDescent;
219     rMetric.mnIntLeading   = 0;
220     // since ImplFontMetricData::mnWidth is only used for stretching/squeezing fonts
221     // setting this width to the pixel height of the fontsize is good enough
222     // it also makes the calculation of the stretch factor simple
223     rMetric.mnWidth        = static_cast<long>(mfFontStretch * fPixelSize + 0.5);
224 }
225 
226 // -----------------------------------------------------------------------
227 
SetTextColor(const RGBAColor & rColor)228 void AtsTextStyle::SetTextColor( const RGBAColor& rColor )
229 {
230     RGBColor aAtsColor;
231     aAtsColor.red   = (unsigned short)( rColor.GetRed()   * 65535.0 );
232     aAtsColor.green = (unsigned short)( rColor.GetGreen() * 65535.0 );
233     aAtsColor.blue  = (unsigned short)( rColor.GetColor() * 65535.0 );
234 
235     ATSUAttributeTag aTag = kATSUColorTag;
236     ByteCount aValueSize = sizeof( aAtsColor );
237     ATSUAttributeValuePtr aValue = &aAtsColor;
238 
239     /*OSStatus err =*/ ATSUSetAttributes( maATSUStyle, 1, &aTag, &aValueSize, &aValue );
240 }
241 
242 // -----------------------------------------------------------------------
243 
GetGlyphBoundRect(sal_GlyphId aGlyphId,Rectangle & rRect) const244 bool AtsTextStyle::GetGlyphBoundRect( sal_GlyphId aGlyphId, Rectangle& rRect ) const
245 {
246     ATSUStyle rATSUStyle = maATSUStyle; // TODO: handle glyph fallback
247     ATSGlyphID aGlyphId = aGlyphId;
248     ATSGlyphScreenMetrics aGlyphMetrics;
249     const bool bNonAntialiasedText = false;
250     OSStatus eStatus = ATSUGlyphGetScreenMetrics( rATSUStyle,
251         1, &aGlyphId, 0, FALSE, !bNonAntialiasedText, &aGlyphMetrics );
252     if( eStatus != noErr )
253         return false;
254 
255     const long nMinX = (long)(+aGlyphMetrics.topLeft.x * mfFontScale - 0.5);
256     const long nMaxX = (long)(aGlyphMetrics.width * mfFontScale + 0.5) + nMinX;
257     const long nMinY = (long)(-aGlyphMetrics.topLeft.y * mfFontScale - 0.5);
258     const long nMaxY = (long)(aGlyphMetrics.height * mfFontScale + 0.5) + nMinY;
259     rRect = Rectangle( nMinX, nMinY, nMaxX, nMaxY );
260     return true;
261 }
262 
263 // -----------------------------------------------------------------------
264 
265 // callbacks from ATSUGlyphGetCubicPaths() fore GetGlyphOutline()
266 struct GgoData { basegfx::B2DPolygon maPolygon; basegfx::B2DPolyPolygon* mpPolyPoly; };
267 
GgoLineToProc(const Float32Point * pPoint,void * pData)268 static OSStatus GgoLineToProc( const Float32Point* pPoint, void* pData )
269 {
270     basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
271     const basegfx::B2DPoint aB2DPoint( pPoint->x, pPoint->y );
272     rPolygon.append( aB2DPoint );
273     return noErr;
274 }
275 
GgoCurveToProc(const Float32Point * pCP1,const Float32Point * pCP2,const Float32Point * pPoint,void * pData)276 static OSStatus GgoCurveToProc( const Float32Point* pCP1, const Float32Point* pCP2,
277     const Float32Point* pPoint, void* pData )
278 {
279     basegfx::B2DPolygon& rPolygon = static_cast<GgoData*>(pData)->maPolygon;
280     const sal_uInt32 nPointCount = rPolygon.count();
281     const basegfx::B2DPoint aB2DControlPoint1( pCP1->x, pCP1->y );
282     rPolygon.setNextControlPoint( nPointCount-1, aB2DControlPoint1 );
283     const basegfx::B2DPoint aB2DEndPoint( pPoint->x, pPoint->y );
284     rPolygon.append( aB2DEndPoint );
285     const basegfx::B2DPoint aB2DControlPoint2( pCP2->x, pCP2->y );
286     rPolygon.setPrevControlPoint( nPointCount, aB2DControlPoint2 );
287     return noErr;
288 }
289 
GgoClosePathProc(void * pData)290 static OSStatus GgoClosePathProc( void* pData )
291 {
292     GgoData* pGgoData = static_cast<GgoData*>(pData);
293     basegfx::B2DPolygon& rPolygon = pGgoData->maPolygon;
294     if( rPolygon.count() > 0 )
295         pGgoData->mpPolyPoly->append( rPolygon );
296     rPolygon.clear();
297     return noErr;
298 }
299 
GgoMoveToProc(const Float32Point * pPoint,void * pData)300 static OSStatus GgoMoveToProc( const Float32Point* pPoint, void* pData )
301 {
302     GgoClosePathProc( pData );
303     OSStatus eStatus = GgoLineToProc( pPoint, pData );
304     return eStatus;
305 }
306 
GetGlyphOutline(sal_GlyphId aGlyphId,basegfx::B2DPolyPolygon & rResult) const307 bool AtsTextStyle::GetGlyphOutline( sal_GlyphId aGlyphId, basegfx::B2DPolyPolygon& rResult ) const
308 {
309     GgoData aGgoData;
310     aGgoData.mpPolyPoly = &rResult;
311     rResult.clear();
312 
313     OSStatus eGgoStatus = noErr;
314     OSStatus eStatus = ATSUGlyphGetCubicPaths( maATSUStyle, aGlyphId,
315         GgoMoveToProc, GgoLineToProc, GgoCurveToProc, GgoClosePathProc,
316         &aGgoData, &eGgoStatus );
317     if( (eStatus != noErr) ) // TODO: why is (eGgoStatus!=noErr) when curves are involved?
318         return false;
319 
320     GgoClosePathProc( &aGgoData );
321 
322     // apply the font scale
323     if( mfFontScale != 1.0 ) {
324         basegfx::B2DHomMatrix aScale;
325         aScale.scale( +mfFontScale, +mfFontScale );
326         rResult.transform( aScale );
327     }
328 
329     return true;
330 }
331 
332 // =======================================================================
333 
GetDevFontAttributes(ATSUFontID nFontID,ImplDevFontAttributes & rDFA)334 static bool GetDevFontAttributes( ATSUFontID nFontID, ImplDevFontAttributes& rDFA )
335 {
336     // all ATSU fonts are device fonts that can be directly rotated
337     rDFA.mbOrientation = true;
338     rDFA.mbDevice      = true;
339     rDFA.mnQuality     = 0;
340 
341     // reset the attributes
342     rDFA.meFamily     = FAMILY_DONTKNOW;
343     rDFA.mePitch      = PITCH_VARIABLE;
344     rDFA.meWidthType  = WIDTH_NORMAL;
345     rDFA.meWeight     = WEIGHT_NORMAL;
346     rDFA.meItalic     = ITALIC_NONE;
347     rDFA.mbSymbolFlag = false;
348 
349     // ignore bitmap fonts
350     ATSFontRef rATSFontRef = FMGetATSFontRefFromFont( nFontID );
351     ByteCount nHeadLen = 0;
352     OSStatus rc = ATSFontGetTable( rATSFontRef, 0x68656164/*head*/, 0, 0, NULL, &nHeadLen );
353     if( (rc != noErr) || (nHeadLen <= 0) )
354         return false;
355 
356     // all scalable fonts on this platform are subsettable
357     rDFA.mbSubsettable  = true;
358     rDFA.mbEmbeddable   = false;
359     // TODO: these members are needed only for our X11 platform targets
360     rDFA.meAntiAlias    = ANTIALIAS_DONTKNOW;
361     rDFA.meEmbeddedBitmap = EMBEDDEDBITMAP_DONTKNOW;
362 
363     // prepare iterating over all name strings of the font
364     ItemCount nFontNameCount = 0;
365     rc = ATSUCountFontNames( nFontID, &nFontNameCount );
366     if( rc != noErr )
367         return false;
368     int nBestNameValue = 0;
369     int nBestStyleValue = 0;
370     FontLanguageCode eBestLangCode = 0;
371     const FontLanguageCode eUILangCode = Application::GetSettings().GetUILanguage();
372     typedef std::vector<char> NameBuffer;
373     NameBuffer aNameBuffer( 256 );
374 
375     // iterate over all available name strings of the font
376     for( ItemCount nNameIndex = 0; nNameIndex < nFontNameCount; ++nNameIndex )
377     {
378         ByteCount nNameLength = 0;
379 
380         FontNameCode     eFontNameCode;
381         FontPlatformCode eFontNamePlatform;
382         FontScriptCode   eFontNameScript;
383         FontLanguageCode eFontNameLanguage;
384         rc = ATSUGetIndFontName( nFontID, nNameIndex, 0, NULL,
385             &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
386         if( rc != noErr )
387             continue;
388 
389         // ignore non-interesting name entries
390         if( (eFontNameCode != kFontFamilyName)
391         &&  (eFontNameCode != kFontStyleName)
392         &&  (eFontNameCode != kFontPostscriptName) )
393             continue;
394 
395         // heuristic to find the most common font name
396         // prefering default language names or even better the names matching to the UI language
397         int nNameValue = (eFontNameLanguage==eUILangCode) ? 0 : ((eFontNameLanguage==0) ? -10 : -20);
398         rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UNICODE;
399         const int nPlatformEncoding = ((int)eFontNamePlatform << 8) + (int)eFontNameScript;
400         switch( nPlatformEncoding )
401         {
402             case 0x000: nNameValue += 23; break;    // Unicode 1.0
403             case 0x001: nNameValue += 24; break;    // Unicode 1.1
404             case 0x002: nNameValue += 25; break;    // iso10646_1993
405             case 0x003: nNameValue += 26; break;    // UCS-2
406             case 0x301: nNameValue += 27; break;    // Win UCS-2
407             case 0x004:                             // UCS-4
408             case 0x30A: nNameValue += 0;            // Win-UCS-4
409                 eEncoding = RTL_TEXTENCODING_UCS4;
410                 break;
411             case 0x100: nNameValue += 21;           // Mac Roman
412                 eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
413                 break;
414             case 0x300: nNameValue =  0;            // Win Symbol encoded name!
415                 rDFA.mbSymbolFlag = true;           // (often seen for symbol fonts)
416                 break;
417             default: nNameValue = 0;                // ignore other encodings
418                 break;
419         }
420 
421         // ignore name entries with no useful encoding
422         if( nNameValue <= 0 )
423             continue;
424         if( nNameLength >= aNameBuffer.size() )
425             continue;
426 
427         // get the encoded name
428         aNameBuffer.reserve( nNameLength+1 ); // extra byte helps for debugging
429         rc = ATSUGetIndFontName( nFontID, nNameIndex, nNameLength, &aNameBuffer[0],
430             &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
431         if( rc != noErr )
432             continue;
433 
434         // convert to unicode name
435         UniString aUtf16Name;
436         if( eEncoding == RTL_TEXTENCODING_UNICODE ) // we are just interested in UTF16 encoded names
437             aUtf16Name = UniString( (const sal_Unicode*)&aNameBuffer[0], nNameLength/2 );
438         else if( eEncoding == RTL_TEXTENCODING_UCS4 )
439             aUtf16Name = UniString(); // TODO
440         else // assume the non-unicode encoded names are byte encoded
441             aUtf16Name = UniString( &aNameBuffer[0], nNameLength, eEncoding );
442 
443         // ignore empty strings
444         if( aUtf16Name.Len() <= 0 )
445             continue;
446 
447         // handle the name depending on its namecode
448         switch( eFontNameCode )
449         {
450         case kFontFamilyName:
451             // ignore font names starting with '.'
452             if( aUtf16Name.GetChar(0) == '.' )
453                 nNameValue = 0;
454             else if( rDFA.maName.Len() )
455             {
456                 // even if a family name is not the one we are looking for
457                 // it is still useful as a font name alternative
458                 if( rDFA.maMapNames.Len() )
459                     rDFA.maMapNames += ';';
460                 rDFA.maMapNames += (nBestNameValue < nNameValue) ? rDFA.maName : aUtf16Name;
461             }
462             if( nBestNameValue < nNameValue )
463             {
464                 // get the best family name
465                 nBestNameValue = nNameValue;
466                 eBestLangCode = eFontNameLanguage;
467                 rDFA.maName = aUtf16Name;
468             }
469             break;
470         case kFontStyleName:
471             // get a style name matching to the family name
472             if( nBestStyleValue < nNameValue )
473             {
474                 nBestStyleValue = nNameValue;
475                 rDFA.maStyleName = aUtf16Name;
476             }
477             break;
478         case kFontPostscriptName:
479             // use the postscript name to get some useful info
480             UpdateAttributesFromPSName( aUtf16Name, rDFA );
481             break;
482         default:
483             // TODO: use other name entries too?
484             break;
485         }
486     }
487 
488     bool bRet = (rDFA.maName.Len() > 0);
489     return bRet;
490 }
491 
492 // =======================================================================
493 
GetAtsFontList(void)494 SystemFontList* GetAtsFontList( void )
495 {
496     return new AtsFontList();
497 }
498 
499 // =======================================================================
500 
AtsFontList()501 AtsFontList::AtsFontList()
502 {
503     // count available system fonts
504     ItemCount nATSUICompatibleFontsAvailable = 0;
505     if( ATSUFontCount(&nATSUICompatibleFontsAvailable) != noErr )
506         return;
507     if( nATSUICompatibleFontsAvailable <= 0 )
508         return;
509 
510     // enumerate available system fonts
511     typedef std::vector<ATSUFontID> AtsFontIDVector;
512     AtsFontIDVector aFontIDVector( nATSUICompatibleFontsAvailable );
513     ItemCount nFontItemsCount = 0;
514     if( ATSUGetFontIDs( &aFontIDVector[0], aFontIDVector.capacity(), &nFontItemsCount ) != noErr )
515         return;
516 
517     BOOST_ASSERT(nATSUICompatibleFontsAvailable == nFontItemsCount && "Strange I would expect them to be equal");
518 
519     // prepare use of the available fonts
520     AtsFontIDVector::const_iterator it = aFontIDVector.begin();
521     for(; it != aFontIDVector.end(); ++it )
522     {
523         const ATSUFontID nFontID = *it;
524         ImplDevFontAttributes aDevFontAttr;
525         if( !GetDevFontAttributes( nFontID, aDevFontAttr ) )
526             continue;
527         AtsFontData* pFontData = new AtsFontData( aDevFontAttr, nFontID );
528         maFontContainer[ nFontID ] = pFontData;
529     }
530 
531     InitGlyphFallbacks();
532 }
533 
534 // -----------------------------------------------------------------------
535 
~AtsFontList()536 AtsFontList::~AtsFontList()
537 {
538     AtsFontContainer::const_iterator it = maFontContainer.begin();
539     for(; it != maFontContainer.end(); ++it )
540         delete (*it).second;
541     maFontContainer.clear();
542 
543     ATSUDisposeFontFallbacks( maFontFallbacks );
544 }
545 
546 // -----------------------------------------------------------------------
547 
AnnounceFonts(ImplDevFontList & rFontList) const548 void AtsFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
549 {
550     AtsFontContainer::const_iterator it = maFontContainer.begin();
551     for(; it != maFontContainer.end(); ++it )
552         rFontList.Add( (*it).second->Clone() );
553 }
554 
555 // -----------------------------------------------------------------------
556 
GetFontDataFromId(sal_IntPtr nFontId) const557 ImplMacFontData* AtsFontList::GetFontDataFromId( sal_IntPtr nFontId ) const
558 {
559     AtsFontContainer::const_iterator it = maFontContainer.find( nFontId );
560     if( it == maFontContainer.end() )
561         return NULL;
562     return (*it).second;
563 }
564 
565 // -----------------------------------------------------------------------
566 
567 // not all fonts are suitable for glyph fallback => sort them
568 struct GfbCompare{ bool operator()(const ImplMacFontData*, const ImplMacFontData*); };
569 
operator ()(const ImplMacFontData * pA,const ImplMacFontData * pB)570 inline bool GfbCompare::operator()( const ImplMacFontData* pA, const ImplMacFontData* pB )
571 {
572     // use symbol fonts only as last resort
573     bool bPreferA = !pA->IsSymbolFont();
574     bool bPreferB = !pB->IsSymbolFont();
575     if( bPreferA != bPreferB )
576         return bPreferA;
577     // prefer scalable fonts
578     bPreferA = pA->IsScalable();
579     bPreferB = pB->IsScalable();
580     if( bPreferA != bPreferB )
581         return bPreferA;
582     // prefer non-slanted fonts
583     bPreferA = (pA->GetSlant() == ITALIC_NONE);
584     bPreferB = (pB->GetSlant() == ITALIC_NONE);
585     if( bPreferA != bPreferB )
586         return bPreferA;
587     // prefer normal weight fonts
588     bPreferA = (pA->GetWeight() == WEIGHT_NORMAL);
589     bPreferB = (pB->GetWeight() == WEIGHT_NORMAL);
590     if( bPreferA != bPreferB )
591         return bPreferA;
592     // prefer normal width fonts
593     bPreferA = (pA->GetWidthType() == WIDTH_NORMAL);
594     bPreferB = (pB->GetWidthType() == WIDTH_NORMAL);
595     if( bPreferA != bPreferB )
596         return bPreferA;
597     return false;
598 }
599 
600 // -----------------------------------------------------------------------
601 
InitGlyphFallbacks()602 void AtsFontList::InitGlyphFallbacks()
603 {
604     // sort fonts for "glyph fallback"
605     typedef std::multiset<const ImplMacFontData*,GfbCompare> FallbackSet;
606     FallbackSet aFallbackSet;
607     AtsFontContainer::const_iterator it = maFontContainer.begin();
608     for(; it != maFontContainer.end(); ++it )
609     {
610         const ImplMacFontData* pIFD = (*it).second;
611         // TODO: subsettable/embeddable glyph fallback only for PDF export?
612         if( pIFD->IsSubsettable() || pIFD->IsEmbeddable() )
613             aFallbackSet.insert( pIFD );
614     }
615 
616     // tell ATSU about font preferences for "glyph fallback"
617     typedef std::vector<ATSUFontID> AtsFontIDVector;
618     AtsFontIDVector aFallbackVector;
619     aFallbackVector.reserve( maFontContainer.size() );
620     FallbackSet::const_iterator itFData = aFallbackSet.begin();
621     for(; itFData != aFallbackSet.end(); ++itFData )
622     {
623         const ImplMacFontData* pFontData = (*itFData);
624         ATSUFontID nFontID = (ATSUFontID)pFontData->GetFontId();
625         aFallbackVector.push_back( nFontID );
626     }
627 
628     ATSUCreateFontFallbacks( &maFontFallbacks );
629     ATSUSetObjFontFallbacks( maFontFallbacks,
630         aFallbackVector.size(), &aFallbackVector[0], kATSUSequentialFallbacksPreferred );
631 }
632 
633 // =======================================================================
634 
635