xref: /aoo42x/main/vcl/aqua/source/gdi/atsfonts.cxx (revision 9f62ea84)
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 
23 
24 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_vcl.hxx"
26 
27 #include <boost/assert.hpp>
28 #include <vector>
29 #include <set>
30 
31 #include "vcl/svapp.hxx"
32 
33 #include "aqua/salgdi.h"
34 #include "aqua/saldata.hxx"
35 #include "aqua/salatsuifontutils.hxx"
36 
37 // we have to get the font attributes from the name table
38 // since neither head's macStyle nor OS/2's panose are easily available
39 // during font enumeration. macStyle bits would be not sufficient anyway
40 // and SFNT fonts on Mac usually do not contain an OS/2 table.
41 static void UpdateAttributesFromPSName( const String& rPSName, ImplDevFontAttributes& rDFA )
42 {
43 	ByteString aPSName( rPSName, RTL_TEXTENCODING_UTF8 );
44 	aPSName.ToLowerAscii();
45 
46 	// TODO: use a multi-string ignore-case matcher once it becomes available
47     if( (aPSName.Search("regular") != STRING_NOTFOUND)
48     ||  (aPSName.Search("normal") != STRING_NOTFOUND)
49     ||  (aPSName.Search("roman") != STRING_NOTFOUND)
50     ||  (aPSName.Search("medium") != STRING_NOTFOUND)
51     ||  (aPSName.Search("plain") != STRING_NOTFOUND)
52     ||  (aPSName.Search("standard") != STRING_NOTFOUND)
53     ||  (aPSName.Search("std") != STRING_NOTFOUND) )
54     {
55        rDFA.meWidthType = WIDTH_NORMAL;
56        rDFA.meWeight    = WEIGHT_NORMAL;
57        rDFA.meItalic    = ITALIC_NONE;
58     }
59 
60     // heuristics for font weight
61 	if (aPSName.Search("extrablack") != STRING_NOTFOUND)
62 	    rDFA.meWeight = WEIGHT_BLACK;
63     else if (aPSName.Search("black") != STRING_NOTFOUND)
64 	    rDFA.meWeight = WEIGHT_BLACK;
65     //else if (aPSName.Search("book") != STRING_NOTFOUND)
66 	//    rDFA.meWeight = WEIGHT_SEMIBOLD;
67     else if( (aPSName.Search("semibold") != STRING_NOTFOUND)
68     ||  (aPSName.Search("smbd") != STRING_NOTFOUND))
69 	    rDFA.meWeight = WEIGHT_SEMIBOLD;
70     else if (aPSName.Search("ultrabold") != STRING_NOTFOUND)
71 	    rDFA.meWeight = WEIGHT_ULTRABOLD;
72     else if (aPSName.Search("extrabold") != STRING_NOTFOUND)
73 	    rDFA.meWeight = WEIGHT_BLACK;
74     else if( (aPSName.Search("bold") != STRING_NOTFOUND)
75     ||  (aPSName.Search("-bd") != STRING_NOTFOUND))
76 	    rDFA.meWeight = WEIGHT_BOLD;
77     else if (aPSName.Search("extralight") != STRING_NOTFOUND)
78 	    rDFA.meWeight = WEIGHT_ULTRALIGHT;
79     else if (aPSName.Search("ultralight") != STRING_NOTFOUND)
80 	    rDFA.meWeight = WEIGHT_ULTRALIGHT;
81     else if (aPSName.Search("light") != STRING_NOTFOUND)
82 	    rDFA.meWeight = WEIGHT_LIGHT;
83     else if (aPSName.Search("thin") != STRING_NOTFOUND)
84 	    rDFA.meWeight = WEIGHT_THIN;
85     else if (aPSName.Search("-w3") != STRING_NOTFOUND)
86 	    rDFA.meWeight = WEIGHT_LIGHT;
87     else if (aPSName.Search("-w4") != STRING_NOTFOUND)
88 	    rDFA.meWeight = WEIGHT_SEMILIGHT;
89     else if (aPSName.Search("-w5") != STRING_NOTFOUND)
90 	    rDFA.meWeight = WEIGHT_NORMAL;
91     else if (aPSName.Search("-w6") != STRING_NOTFOUND)
92 	    rDFA.meWeight = WEIGHT_SEMIBOLD;
93     else if (aPSName.Search("-w7") != STRING_NOTFOUND)
94 	    rDFA.meWeight = WEIGHT_BOLD;
95     else if (aPSName.Search("-w8") != STRING_NOTFOUND)
96 	    rDFA.meWeight = WEIGHT_ULTRABOLD;
97     else if (aPSName.Search("-w9") != STRING_NOTFOUND)
98 	    rDFA.meWeight = WEIGHT_BLACK;
99 
100     // heuristics for font slant
101     if( (aPSName.Search("italic") != STRING_NOTFOUND)
102     ||  (aPSName.Search(" ital") != STRING_NOTFOUND)
103     ||  (aPSName.Search("cursive") != STRING_NOTFOUND)
104     ||  (aPSName.Search("-it") != STRING_NOTFOUND)
105     ||  (aPSName.Search("lightit") != STRING_NOTFOUND)
106     ||  (aPSName.Search("mediumit") != STRING_NOTFOUND)
107     ||  (aPSName.Search("boldit") != STRING_NOTFOUND)
108     ||  (aPSName.Search("cnit") != STRING_NOTFOUND)
109     ||  (aPSName.Search("bdcn") != STRING_NOTFOUND)
110     ||  (aPSName.Search("bdit") != STRING_NOTFOUND)
111     ||  (aPSName.Search("condit") != STRING_NOTFOUND)
112     ||  (aPSName.Search("bookit") != STRING_NOTFOUND)
113     ||  (aPSName.Search("blackit") != STRING_NOTFOUND) )
114 	    rDFA.meItalic = ITALIC_NORMAL;
115     if( (aPSName.Search("oblique") != STRING_NOTFOUND)
116     ||  (aPSName.Search("inclined") != STRING_NOTFOUND)
117     ||  (aPSName.Search("slanted") != STRING_NOTFOUND) )
118 	    rDFA.meItalic = ITALIC_OBLIQUE;
119 
120     // heuristics for font width
121     if( (aPSName.Search("condensed") != STRING_NOTFOUND)
122     ||  (aPSName.Search("-cond") != STRING_NOTFOUND)
123     ||  (aPSName.Search("boldcond") != STRING_NOTFOUND)
124     ||  (aPSName.Search("boldcn") != STRING_NOTFOUND)
125     ||  (aPSName.Search("cnit") != STRING_NOTFOUND) )
126 	    rDFA.meWidthType = WIDTH_CONDENSED;
127     else if (aPSName.Search("narrow") != STRING_NOTFOUND)
128 	    rDFA.meWidthType = WIDTH_SEMI_CONDENSED;
129     else if (aPSName.Search("expanded") != STRING_NOTFOUND)
130 	    rDFA.meWidthType = WIDTH_EXPANDED;
131     else if (aPSName.Search("wide") != STRING_NOTFOUND)
132 	    rDFA.meWidthType = WIDTH_EXPANDED;
133 
134     // heuristics for font pitch
135     if( (aPSName.Search("mono") != STRING_NOTFOUND)
136     ||  (aPSName.Search("courier") != STRING_NOTFOUND)
137     ||  (aPSName.Search("monaco") != STRING_NOTFOUND)
138     ||  (aPSName.Search("typewriter") != STRING_NOTFOUND) )
139 	    rDFA.mePitch = PITCH_FIXED;
140 
141     // heuristics for font family type
142     if( (aPSName.Search("script") != STRING_NOTFOUND)
143     ||  (aPSName.Search("chancery") != STRING_NOTFOUND)
144     ||  (aPSName.Search("zapfino") != STRING_NOTFOUND))
145 	    rDFA.meFamily = FAMILY_SCRIPT;
146     else if( (aPSName.Search("comic") != STRING_NOTFOUND)
147     ||  (aPSName.Search("outline") != STRING_NOTFOUND)
148     ||  (aPSName.Search("pinpoint") != STRING_NOTFOUND) )
149 	    rDFA.meFamily = FAMILY_DECORATIVE;
150     else if( (aPSName.Search("sans") != STRING_NOTFOUND)
151     ||  (aPSName.Search("arial") != STRING_NOTFOUND) )
152 	    rDFA.meFamily = FAMILY_SWISS;
153     else if( (aPSName.Search("roman") != STRING_NOTFOUND)
154     ||  (aPSName.Search("times") != STRING_NOTFOUND) )
155 	    rDFA.meFamily = FAMILY_ROMAN;
156 
157     // heuristics for codepoint semantic
158     if( (aPSName.Search("symbol") != STRING_NOTFOUND)
159     ||  (aPSName.Search("dings") != STRING_NOTFOUND)
160     ||  (aPSName.Search("dingbats") != STRING_NOTFOUND)
161     ||  (aPSName.Search("ornaments") != STRING_NOTFOUND)
162     ||  (aPSName.Search("embellishments") != STRING_NOTFOUND) )
163         rDFA.mbSymbolFlag  = true;
164 
165    // #i100020# special heuristic for names with single-char styles
166    // NOTE: we are checking name that hasn't been lower-cased
167    if( rPSName.Len() > 3 )
168    {
169         int i = rPSName.Len();
170         sal_Unicode c = rPSName.GetChar( --i );
171         if( c == 'C' ) { // "capitals"
172             rDFA.meFamily = FAMILY_DECORATIVE;
173             c = rPSName.GetChar( --i );
174         }
175         if( c == 'O' ) { // CFF-based OpenType
176             c = rPSName.GetChar( --i );
177         }
178         if( c == 'I' ) { // "italic"
179             rDFA.meItalic = ITALIC_NORMAL;
180             c = rPSName.GetChar( --i );
181         }
182         if( c == 'B' )   // "bold"
183             rDFA.meWeight = WEIGHT_BOLD;
184         if( c == 'C' )   // "capitals"
185             rDFA.meFamily = FAMILY_DECORATIVE;
186         // TODO: check that all single-char styles have been resolved?
187     }
188 }
189 
190 // -----------------------------------------------------------------------
191 
192 static bool GetDevFontAttributes( ATSUFontID nFontID, ImplDevFontAttributes& rDFA )
193 {
194     // all ATSU fonts are device fonts that can be directly rotated
195     rDFA.mbOrientation = true;
196     rDFA.mbDevice      = true;
197     rDFA.mnQuality     = 0;
198 
199     // reset the attributes
200     rDFA.meFamily     = FAMILY_DONTKNOW;
201     rDFA.mePitch      = PITCH_VARIABLE;
202     rDFA.meWidthType  = WIDTH_NORMAL;
203     rDFA.meWeight     = WEIGHT_NORMAL;
204     rDFA.meItalic     = ITALIC_NONE;
205     rDFA.mbSymbolFlag = false;
206 
207     // ignore bitmap fonts
208     ATSFontRef rATSFontRef = FMGetATSFontRefFromFont( nFontID );
209     ByteCount nHeadLen = 0;
210     OSStatus rc = ATSFontGetTable( rATSFontRef, 0x68656164/*head*/, 0, 0, NULL, &nHeadLen );
211     if( (rc != noErr) || (nHeadLen <= 0) )
212         return false;
213 
214 	// all scalable fonts on this platform are subsettable
215 	rDFA.mbSubsettable	= true;
216 	rDFA.mbEmbeddable	= false;
217 
218 	// prepare iterating over all name strings of the font
219     ItemCount nFontNameCount = 0;
220     rc = ATSUCountFontNames( nFontID, &nFontNameCount );
221     if( rc != noErr )
222         return false;
223     int nBestNameValue = 0;
224     int nBestStyleValue = 0;
225 	FontLanguageCode eBestLangCode = 0;
226 	const FontLanguageCode eUILangCode = Application::GetSettings().GetUILanguage();
227 	typedef std::vector<char> NameBuffer;
228     NameBuffer aNameBuffer( 256 );
229 
230     // iterate over all available name strings of the font
231     for( ItemCount nNameIndex = 0; nNameIndex < nFontNameCount; ++nNameIndex )
232     {
233         ByteCount nNameLength = 0;
234 
235         FontNameCode     eFontNameCode;
236         FontPlatformCode eFontNamePlatform;
237         FontScriptCode   eFontNameScript;
238         FontLanguageCode eFontNameLanguage;
239         rc = ATSUGetIndFontName( nFontID, nNameIndex, 0, NULL,
240             &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
241         if( rc != noErr )
242             continue;
243 
244         // ignore non-interesting name entries
245         if( (eFontNameCode != kFontFamilyName)
246         &&  (eFontNameCode != kFontStyleName)
247         &&  (eFontNameCode != kFontPostscriptName) )
248             continue;
249 
250         // heuristic to find the most common font name
251         // prefering default language names or even better the names matching to the UI language
252         int nNameValue = (eFontNameLanguage==eUILangCode) ? 0 : ((eFontNameLanguage==0) ? -10 : -20);
253         rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UNICODE;
254         const int nPlatformEncoding = ((int)eFontNamePlatform << 8) + (int)eFontNameScript;
255         switch( nPlatformEncoding )
256         {
257             case 0x000: nNameValue += 23; break;    // Unicode 1.0
258             case 0x001: nNameValue += 24; break;    // Unicode 1.1
259             case 0x002: nNameValue += 25; break;    // iso10646_1993
260             case 0x003: nNameValue += 26; break;    // UCS-2
261             case 0x301: nNameValue += 27; break;    // Win UCS-2
262             case 0x004:                             // UCS-4
263             case 0x30A: nNameValue += 0;            // Win-UCS-4
264                         eEncoding = RTL_TEXTENCODING_UCS4;
265                         break;
266             case 0x100: nNameValue += 21; 	        // Mac Roman
267                         eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
268                         break;
269             case 0x300: nNameValue =  0;            // Win Symbol encoded name!
270                         rDFA.mbSymbolFlag = true;   // (often seen for symbol fonts)
271                         break;
272             default:    nNameValue = 0;	            // ignore other encodings
273 			break;
274         }
275 
276         // ignore name entries with no useful encoding
277         if( nNameValue <= 0 )
278             continue;
279         if( nNameLength >= aNameBuffer.size() )
280             continue;
281 
282         // get the encoded name
283         aNameBuffer.reserve( nNameLength+1 ); // extra byte helps for debugging
284         rc = ATSUGetIndFontName( nFontID, nNameIndex, nNameLength, &aNameBuffer[0],
285            &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
286         if( rc != noErr )
287             continue;
288 
289         // convert to unicode name
290         UniString aUtf16Name;
291         if( eEncoding == RTL_TEXTENCODING_UNICODE ) // we are just interested in UTF16 encoded names
292             aUtf16Name = UniString( (const sal_Unicode*)&aNameBuffer[0], nNameLength/2 );
293         else if( eEncoding == RTL_TEXTENCODING_UCS4 )
294             aUtf16Name = UniString(); // TODO
295         else // assume the non-unicode encoded names are byte encoded
296             aUtf16Name = UniString( &aNameBuffer[0], nNameLength, eEncoding );
297 
298         // ignore empty strings
299         if( aUtf16Name.Len() <= 0 )
300             continue;
301 
302         // handle the name depending on its namecode
303         switch( eFontNameCode )
304 		{
305             case kFontFamilyName:
306                 // ignore font names starting with '.'
307                 if( aUtf16Name.GetChar(0) == '.' )
308                     nNameValue = 0;
309                 else if( rDFA.maName.Len() )
310                 {
311                     // even if a family name is not the one we are looking for
312                     // it is still useful as a font name alternative
313                     if( rDFA.maMapNames.Len() )
314                         rDFA.maMapNames += ';';
315                     rDFA.maMapNames += (nBestNameValue < nNameValue) ? rDFA.maName : aUtf16Name;
316                 }
317                 if( nBestNameValue < nNameValue )
318                 {
319                     // get the best family name
320                     nBestNameValue = nNameValue;
321 					eBestLangCode = eFontNameLanguage;
322                     rDFA.maName = aUtf16Name;
323                 }
324                 break;
325             case kFontStyleName:
326                 // get a style name matching to the family name
327                 if( nBestStyleValue < nNameValue )
328                 {
329                     nBestStyleValue = nNameValue;
330                     rDFA.maStyleName = aUtf16Name;
331                 }
332                 break;
333             case kFontPostscriptName:
334                 // use the postscript name to get some useful info
335                 UpdateAttributesFromPSName( aUtf16Name, rDFA );
336                 break;
337             default:
338                 // TODO: use other name entries too?
339                 break;
340         }
341     }
342 
343 #if 0 // multiple-master fonts are mostly obsolete nowadays
344       // if we still want to support them this should probably be done one frame higher
345     ItemCount nMaxInstances = 0;
346     rc = ATSUCountFontInstances ( nFontID, &nMaxInstances );
347     for( ItemCount nInstanceIndex = 0; nInstanceIndex < nMaxInstances; ++nInstanceIndex )
348     {
349         ItemCount nMaxVariations = 0;
350         rc = ATSUGetFontInstance( nFontID, nInstanceIndex, 0, NULL, NULL, &nMaxVariations );
351         if( (rc == noErr) && (nMaxVariations > 0) )
352         {
353             fprintf(stderr,"\tnMaxVariations=%d\n",(int)nMaxVariations);
354             typedef ::std::vector<ATSUFontVariationAxis> VariationAxisVector;
355             typedef ::std::vector<ATSUFontVariationValue> VariationValueVector;
356             VariationAxisVector aVariationAxes( nMaxVariations );
357             VariationValueVector aVariationValues( nMaxVariations );
358             ItemCount nVariationCount = 0;
359             rc = ATSUGetFontInstance ( nFontID, nInstanceIndex, nMaxVariations,
360                 &aVariationAxes[0], &aVariationValues[0], &nVariationCount );
361             fprintf(stderr,"\tnVariationCount=%d\n",(int)nVariationCount);
362             for( ItemCount nVariationIndex = 0; nVariationIndex < nMaxVariations; ++nVariationIndex )
363             {
364                 const char* pTag = (const char*)&aVariationAxes[nVariationIndex];
365                 fprintf(stderr,"\tvariation[%d] \'%c%c%c%c\' is %d\n", (int)nVariationIndex,
366                     pTag[3],pTag[2],pTag[1],pTag[0], (int)aVariationValues[nVariationIndex]);
367             }
368        }
369     }
370 #endif
371 
372 #if 0 // selecting non-defaulted font features is not enabled yet
373     ByteString aFName( rDFA.maName, RTL_TEXTENCODING_UTF8 );
374     ByteString aSName( rDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
375     ItemCount nMaxFeatures = 0;
376     rc = ATSUCountFontFeatureTypes( nFontID, &nMaxFeatures );
377     fprintf(stderr,"Font \"%s\" \"%s\" has %d features\n",aFName.GetBuffer(),aSName.GetBuffer(),rc);
378     if( (rc == noErr) && (nMaxFeatures > 0) )
379     {
380         typedef std::vector<ATSUFontFeatureType> FeatureVector;
381         FeatureVector aFeatureVector( nMaxFeatures );
382         ItemCount nFeatureCount = 0;
383         rc = ATSUGetFontFeatureTypes( nFontID, nMaxFeatures, &aFeatureVector[0], &nFeatureCount );
384         fprintf(stderr,"nFeatureCount=%d\n",(int)nFeatureCount);
385         for( ItemCount nFeatureIndex = 0; nFeatureIndex < nFeatureCount; ++nFeatureIndex )
386         {
387             ItemCount nMaxSelectors = 0;
388             rc = ATSUCountFontFeatureSelectors( nFontID, aFeatureVector[nFeatureIndex], &nMaxSelectors );
389             fprintf(stderr,"\tFeature[%d] = %d has %d selectors\n",
390                (int)nFeatureIndex, (int)aFeatureVector[nFeatureIndex], (int)nMaxSelectors );
391             typedef std::vector<ATSUFontFeatureSelector> SelectorVector;
392             SelectorVector aSelectorVector( nMaxSelectors );
393             typedef std::vector<MacOSBoolean> BooleanVector;
394             BooleanVector aEnabledVector( nMaxSelectors );
395             BooleanVector aExclusiveVector( nMaxSelectors );
396             ItemCount nSelectorCount = 0;
397             rc = ATSUGetFontFeatureSelectors ( nFontID, aFeatureVector[nFeatureIndex], nMaxSelectors,
398                 &aSelectorVector[0], &aEnabledVector[0], &nSelectorCount, &aExclusiveVector[0]);
399             for( ItemCount nSelectorIndex = 0; nSelectorIndex < nSelectorCount; ++nSelectorIndex )
400             {
401                 FontNameCode eFontNameCode;
402                 rc = ATSUGetFontFeatureNameCode( nFontID, aFeatureVector[nFeatureIndex],
403                     aSelectorVector[nSelectorIndex], &eFontNameCode );
404                 fprintf(stderr,"\t\tselector[%d] n=%d e=%d, x=%d\n",
405                     (int)nSelectorIndex, (int)eFontNameCode,
406                     aEnabledVector[nSelectorIndex], aExclusiveVector[nSelectorIndex] );
407             }
408         }
409     }
410 #endif
411 
412     bool bRet = (rDFA.maName.Len() > 0);
413     return bRet;
414 }
415 
416 // =======================================================================
417 
418 SystemFontList::SystemFontList()
419 {
420     // count available system fonts
421     ItemCount nATSUICompatibleFontsAvailable = 0;
422     if( ATSUFontCount(&nATSUICompatibleFontsAvailable) != noErr )
423         return;
424     if( nATSUICompatibleFontsAvailable <= 0 )
425        return;
426 
427     // enumerate available system fonts
428     typedef std::vector<ATSUFontID> AtsFontIDVector;
429     AtsFontIDVector aFontIDVector( nATSUICompatibleFontsAvailable );
430     ItemCount nFontItemsCount = 0;
431     if( ATSUGetFontIDs( &aFontIDVector[0], aFontIDVector.capacity(), &nFontItemsCount ) != noErr )
432        return;
433 
434     BOOST_ASSERT(nATSUICompatibleFontsAvailable == nFontItemsCount && "Strange I would expect them to be equal");
435 
436     // prepare use of the available fonts
437     AtsFontIDVector::const_iterator it = aFontIDVector.begin();
438     for(; it != aFontIDVector.end(); ++it )
439     {
440 	const ATSUFontID nFontID = *it;
441         ImplDevFontAttributes aDevFontAttr;
442         if( !GetDevFontAttributes( nFontID, aDevFontAttr ) )
443             continue;
444         ImplMacFontData* pFontData = new ImplMacFontData(  aDevFontAttr, nFontID );
445         maFontContainer[ nFontID ] = pFontData;
446     }
447 
448     InitGlyphFallbacks();
449 }
450 
451 // -----------------------------------------------------------------------
452 
453 SystemFontList::~SystemFontList()
454 {
455     MacFontContainer::const_iterator it = maFontContainer.begin();
456     for(; it != maFontContainer.end(); ++it )
457         delete (*it).second;
458     maFontContainer.clear();
459 
460     ATSUDisposeFontFallbacks( maFontFallbacks );
461 }
462 
463 // -----------------------------------------------------------------------
464 
465 void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
466 {
467     MacFontContainer::const_iterator it = maFontContainer.begin();
468     for(; it != maFontContainer.end(); ++it )
469         rFontList.Add( (*it).second->Clone() );
470 }
471 
472 // -----------------------------------------------------------------------
473 
474 // not all fonts are suitable for glyph fallback => sort them
475 struct GfbCompare{ bool operator()(const ImplMacFontData*, const ImplMacFontData*); };
476 
477 inline bool GfbCompare::operator()( const ImplMacFontData* pA, const ImplMacFontData* pB )
478 {
479 	// use symbol fonts only as last resort
480 	bool bPreferA = !pA->IsSymbolFont();
481 	bool bPreferB = !pB->IsSymbolFont();
482 	if( bPreferA != bPreferB )
483 		return bPreferA;
484 	// prefer scalable fonts
485 	bPreferA = pA->IsScalable();
486 	bPreferB = pB->IsScalable();
487 	if( bPreferA != bPreferB )
488 		return bPreferA;
489 	// prefer non-slanted fonts
490 	bPreferA = (pA->GetSlant() == ITALIC_NONE);
491 	bPreferB = (pB->GetSlant() == ITALIC_NONE);
492 	if( bPreferA != bPreferB )
493 		return bPreferA;
494 	// prefer normal weight fonts
495 	bPreferA = (pA->GetWeight() == WEIGHT_NORMAL);
496 	bPreferB = (pB->GetWeight() == WEIGHT_NORMAL);
497 	if( bPreferA != bPreferB )
498 		return bPreferA;
499 	// prefer normal width fonts
500 	bPreferA = (pA->GetWidthType() == WIDTH_NORMAL);
501 	bPreferB = (pB->GetWidthType() == WIDTH_NORMAL);
502 	if( bPreferA != bPreferB )
503 		return bPreferA;
504 	return false;
505 }
506 
507 void SystemFontList::InitGlyphFallbacks()
508 {
509     // sort fonts for "glyph fallback"
510     typedef std::multiset<const ImplMacFontData*,GfbCompare> FallbackSet;
511     FallbackSet aFallbackSet;
512     MacFontContainer::const_iterator it = maFontContainer.begin();
513     for(; it != maFontContainer.end(); ++it )
514     {
515 	const ImplMacFontData* pIFD = (*it).second;
516 	// TODO: subsettable/embeddable glyph fallback only for PDF export?
517         if( pIFD->IsSubsettable() || pIFD->IsEmbeddable() )
518 	    aFallbackSet.insert( pIFD );
519     }
520 
521     // tell ATSU about font preferences for "glyph fallback"
522     typedef std::vector<ATSUFontID> AtsFontIDVector;
523     AtsFontIDVector aFallbackVector;
524     aFallbackVector.reserve( maFontContainer.size() );
525     FallbackSet::const_iterator itFData = aFallbackSet.begin();
526     for(; itFData != aFallbackSet.end(); ++itFData )
527     {
528         const ImplMacFontData* pFontData = (*itFData);
529         ATSUFontID nFontID = (ATSUFontID)pFontData->GetFontId();
530         aFallbackVector.push_back( nFontID );
531     }
532 
533     ATSUCreateFontFallbacks( &maFontFallbacks );
534     ATSUSetObjFontFallbacks( maFontFallbacks,
535 		aFallbackVector.size(), &aFallbackVector[0], kATSUSequentialFallbacksPreferred );
536 }
537 
538 // -----------------------------------------------------------------------
539 
540 ImplMacFontData* SystemFontList::GetFontDataFromId( ATSUFontID nFontId ) const
541 {
542     MacFontContainer::const_iterator it = maFontContainer.find( nFontId );
543     if( it == maFontContainer.end() )
544 	return NULL;
545     return (*it).second;
546 }
547 
548 // -----------------------------------------------------------------------
549 
550