xref: /trunk/main/vcl/aqua/source/gdi/atsfonts.cxx (revision 51747b8e)
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 "atsfonts.hxx"
36 #include "impfont.hxx"
37 
38 // -----------------------------------------------------------------------
39 
40 static bool GetDevFontAttributes( ATSUFontID nFontID, ImplDevFontAttributes& rDFA )
41 {
42     // all ATSU fonts are device fonts that can be directly rotated
43     rDFA.mbOrientation = true;
44     rDFA.mbDevice      = true;
45     rDFA.mnQuality     = 0;
46 
47     // reset the attributes
48     rDFA.meFamily     = FAMILY_DONTKNOW;
49     rDFA.mePitch      = PITCH_VARIABLE;
50     rDFA.meWidthType  = WIDTH_NORMAL;
51     rDFA.meWeight     = WEIGHT_NORMAL;
52     rDFA.meItalic     = ITALIC_NONE;
53     rDFA.mbSymbolFlag = false;
54 
55     // ignore bitmap fonts
56     ATSFontRef rATSFontRef = FMGetATSFontRefFromFont( nFontID );
57     ByteCount nHeadLen = 0;
58     OSStatus rc = ATSFontGetTable( rATSFontRef, 0x68656164/*head*/, 0, 0, NULL, &nHeadLen );
59     if( (rc != noErr) || (nHeadLen <= 0) )
60         return false;
61 
62 	// all scalable fonts on this platform are subsettable
63 	rDFA.mbSubsettable	= true;
64 	rDFA.mbEmbeddable	= false;
65 
66 	// prepare iterating over all name strings of the font
67     ItemCount nFontNameCount = 0;
68     rc = ATSUCountFontNames( nFontID, &nFontNameCount );
69     if( rc != noErr )
70         return false;
71     int nBestNameValue = 0;
72     int nBestStyleValue = 0;
73 	FontLanguageCode eBestLangCode = 0;
74 	const FontLanguageCode eUILangCode = Application::GetSettings().GetUILanguage();
75 	typedef std::vector<char> NameBuffer;
76     NameBuffer aNameBuffer( 256 );
77 
78     // iterate over all available name strings of the font
79     for( ItemCount nNameIndex = 0; nNameIndex < nFontNameCount; ++nNameIndex )
80     {
81         ByteCount nNameLength = 0;
82 
83         FontNameCode     eFontNameCode;
84         FontPlatformCode eFontNamePlatform;
85         FontScriptCode   eFontNameScript;
86         FontLanguageCode eFontNameLanguage;
87         rc = ATSUGetIndFontName( nFontID, nNameIndex, 0, NULL,
88             &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
89         if( rc != noErr )
90             continue;
91 
92         // ignore non-interesting name entries
93         if( (eFontNameCode != kFontFamilyName)
94         &&  (eFontNameCode != kFontStyleName)
95         &&  (eFontNameCode != kFontPostscriptName) )
96             continue;
97 
98         // heuristic to find the most common font name
99         // prefering default language names or even better the names matching to the UI language
100         int nNameValue = (eFontNameLanguage==eUILangCode) ? 0 : ((eFontNameLanguage==0) ? -10 : -20);
101         rtl_TextEncoding eEncoding = RTL_TEXTENCODING_UNICODE;
102         const int nPlatformEncoding = ((int)eFontNamePlatform << 8) + (int)eFontNameScript;
103         switch( nPlatformEncoding )
104         {
105             case 0x000: nNameValue += 23; break;    // Unicode 1.0
106             case 0x001: nNameValue += 24; break;    // Unicode 1.1
107             case 0x002: nNameValue += 25; break;    // iso10646_1993
108             case 0x003: nNameValue += 26; break;    // UCS-2
109             case 0x301: nNameValue += 27; break;    // Win UCS-2
110             case 0x004:                             // UCS-4
111             case 0x30A: nNameValue += 0;            // Win-UCS-4
112                         eEncoding = RTL_TEXTENCODING_UCS4;
113                         break;
114             case 0x100: nNameValue += 21; 	        // Mac Roman
115                         eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
116                         break;
117             case 0x300: nNameValue =  0;            // Win Symbol encoded name!
118                         rDFA.mbSymbolFlag = true;   // (often seen for symbol fonts)
119                         break;
120             default:    nNameValue = 0;	            // ignore other encodings
121 			break;
122         }
123 
124         // ignore name entries with no useful encoding
125         if( nNameValue <= 0 )
126             continue;
127         if( nNameLength >= aNameBuffer.size() )
128             continue;
129 
130         // get the encoded name
131         aNameBuffer.reserve( nNameLength+1 ); // extra byte helps for debugging
132         rc = ATSUGetIndFontName( nFontID, nNameIndex, nNameLength, &aNameBuffer[0],
133            &nNameLength, &eFontNameCode, &eFontNamePlatform, &eFontNameScript, &eFontNameLanguage );
134         if( rc != noErr )
135             continue;
136 
137         // convert to unicode name
138         UniString aUtf16Name;
139         if( eEncoding == RTL_TEXTENCODING_UNICODE ) // we are just interested in UTF16 encoded names
140             aUtf16Name = UniString( (const sal_Unicode*)&aNameBuffer[0], nNameLength/2 );
141         else if( eEncoding == RTL_TEXTENCODING_UCS4 )
142             aUtf16Name = UniString(); // TODO
143         else // assume the non-unicode encoded names are byte encoded
144             aUtf16Name = UniString( &aNameBuffer[0], nNameLength, eEncoding );
145 
146         // ignore empty strings
147         if( aUtf16Name.Len() <= 0 )
148             continue;
149 
150         // handle the name depending on its namecode
151         switch( eFontNameCode )
152 		{
153             case kFontFamilyName:
154                 // ignore font names starting with '.'
155                 if( aUtf16Name.GetChar(0) == '.' )
156                     nNameValue = 0;
157                 else if( rDFA.maName.Len() )
158                 {
159                     // even if a family name is not the one we are looking for
160                     // it is still useful as a font name alternative
161                     if( rDFA.maMapNames.Len() )
162                         rDFA.maMapNames += ';';
163                     rDFA.maMapNames += (nBestNameValue < nNameValue) ? rDFA.maName : aUtf16Name;
164                 }
165                 if( nBestNameValue < nNameValue )
166                 {
167                     // get the best family name
168                     nBestNameValue = nNameValue;
169 					eBestLangCode = eFontNameLanguage;
170                     rDFA.maName = aUtf16Name;
171                 }
172                 break;
173             case kFontStyleName:
174                 // get a style name matching to the family name
175                 if( nBestStyleValue < nNameValue )
176                 {
177                     nBestStyleValue = nNameValue;
178                     rDFA.maStyleName = aUtf16Name;
179                 }
180                 break;
181             case kFontPostscriptName:
182                 // use the postscript name to get some useful info
183                 UpdateAttributesFromPSName( aUtf16Name, rDFA );
184                 break;
185             default:
186                 // TODO: use other name entries too?
187                 break;
188         }
189     }
190 
191 #if 0 // multiple-master fonts are mostly obsolete nowadays
192       // if we still want to support them this should probably be done one frame higher
193     ItemCount nMaxInstances = 0;
194     rc = ATSUCountFontInstances ( nFontID, &nMaxInstances );
195     for( ItemCount nInstanceIndex = 0; nInstanceIndex < nMaxInstances; ++nInstanceIndex )
196     {
197         ItemCount nMaxVariations = 0;
198         rc = ATSUGetFontInstance( nFontID, nInstanceIndex, 0, NULL, NULL, &nMaxVariations );
199         if( (rc == noErr) && (nMaxVariations > 0) )
200         {
201             fprintf(stderr,"\tnMaxVariations=%d\n",(int)nMaxVariations);
202             typedef ::std::vector<ATSUFontVariationAxis> VariationAxisVector;
203             typedef ::std::vector<ATSUFontVariationValue> VariationValueVector;
204             VariationAxisVector aVariationAxes( nMaxVariations );
205             VariationValueVector aVariationValues( nMaxVariations );
206             ItemCount nVariationCount = 0;
207             rc = ATSUGetFontInstance ( nFontID, nInstanceIndex, nMaxVariations,
208                 &aVariationAxes[0], &aVariationValues[0], &nVariationCount );
209             fprintf(stderr,"\tnVariationCount=%d\n",(int)nVariationCount);
210             for( ItemCount nVariationIndex = 0; nVariationIndex < nMaxVariations; ++nVariationIndex )
211             {
212                 const char* pTag = (const char*)&aVariationAxes[nVariationIndex];
213                 fprintf(stderr,"\tvariation[%d] \'%c%c%c%c\' is %d\n", (int)nVariationIndex,
214                     pTag[3],pTag[2],pTag[1],pTag[0], (int)aVariationValues[nVariationIndex]);
215             }
216        }
217     }
218 #endif
219 
220 #if 0 // selecting non-defaulted font features is not enabled yet
221     ByteString aFName( rDFA.maName, RTL_TEXTENCODING_UTF8 );
222     ByteString aSName( rDFA.maStyleName, RTL_TEXTENCODING_UTF8 );
223     ItemCount nMaxFeatures = 0;
224     rc = ATSUCountFontFeatureTypes( nFontID, &nMaxFeatures );
225     fprintf(stderr,"Font \"%s\" \"%s\" has %d features\n",aFName.GetBuffer(),aSName.GetBuffer(),rc);
226     if( (rc == noErr) && (nMaxFeatures > 0) )
227     {
228         typedef std::vector<ATSUFontFeatureType> FeatureVector;
229         FeatureVector aFeatureVector( nMaxFeatures );
230         ItemCount nFeatureCount = 0;
231         rc = ATSUGetFontFeatureTypes( nFontID, nMaxFeatures, &aFeatureVector[0], &nFeatureCount );
232         fprintf(stderr,"nFeatureCount=%d\n",(int)nFeatureCount);
233         for( ItemCount nFeatureIndex = 0; nFeatureIndex < nFeatureCount; ++nFeatureIndex )
234         {
235             ItemCount nMaxSelectors = 0;
236             rc = ATSUCountFontFeatureSelectors( nFontID, aFeatureVector[nFeatureIndex], &nMaxSelectors );
237             fprintf(stderr,"\tFeature[%d] = %d has %d selectors\n",
238                (int)nFeatureIndex, (int)aFeatureVector[nFeatureIndex], (int)nMaxSelectors );
239             typedef std::vector<ATSUFontFeatureSelector> SelectorVector;
240             SelectorVector aSelectorVector( nMaxSelectors );
241             typedef std::vector<MacOSBoolean> BooleanVector;
242             BooleanVector aEnabledVector( nMaxSelectors );
243             BooleanVector aExclusiveVector( nMaxSelectors );
244             ItemCount nSelectorCount = 0;
245             rc = ATSUGetFontFeatureSelectors ( nFontID, aFeatureVector[nFeatureIndex], nMaxSelectors,
246                 &aSelectorVector[0], &aEnabledVector[0], &nSelectorCount, &aExclusiveVector[0]);
247             for( ItemCount nSelectorIndex = 0; nSelectorIndex < nSelectorCount; ++nSelectorIndex )
248             {
249                 FontNameCode eFontNameCode;
250                 rc = ATSUGetFontFeatureNameCode( nFontID, aFeatureVector[nFeatureIndex],
251                     aSelectorVector[nSelectorIndex], &eFontNameCode );
252                 fprintf(stderr,"\t\tselector[%d] n=%d e=%d, x=%d\n",
253                     (int)nSelectorIndex, (int)eFontNameCode,
254                     aEnabledVector[nSelectorIndex], aExclusiveVector[nSelectorIndex] );
255             }
256         }
257     }
258 #endif
259 
260     bool bRet = (rDFA.maName.Len() > 0);
261     return bRet;
262 }
263 
264 // =======================================================================
265 
266 SystemFontList::SystemFontList()
267 {
268     // count available system fonts
269     ItemCount nATSUICompatibleFontsAvailable = 0;
270     if( ATSUFontCount(&nATSUICompatibleFontsAvailable) != noErr )
271         return;
272     if( nATSUICompatibleFontsAvailable <= 0 )
273        return;
274 
275     // enumerate available system fonts
276     typedef std::vector<ATSUFontID> AtsFontIDVector;
277     AtsFontIDVector aFontIDVector( nATSUICompatibleFontsAvailable );
278     ItemCount nFontItemsCount = 0;
279     if( ATSUGetFontIDs( &aFontIDVector[0], aFontIDVector.capacity(), &nFontItemsCount ) != noErr )
280        return;
281 
282     BOOST_ASSERT(nATSUICompatibleFontsAvailable == nFontItemsCount && "Strange I would expect them to be equal");
283 
284     // prepare use of the available fonts
285     AtsFontIDVector::const_iterator it = aFontIDVector.begin();
286     for(; it != aFontIDVector.end(); ++it )
287     {
288 	const ATSUFontID nFontID = *it;
289         ImplDevFontAttributes aDevFontAttr;
290         if( !GetDevFontAttributes( nFontID, aDevFontAttr ) )
291             continue;
292         ImplMacFontData* pFontData = new ImplMacFontData(  aDevFontAttr, nFontID );
293         maFontContainer[ nFontID ] = pFontData;
294     }
295 
296     InitGlyphFallbacks();
297 }
298 
299 // -----------------------------------------------------------------------
300 
301 SystemFontList::~SystemFontList()
302 {
303     MacFontContainer::const_iterator it = maFontContainer.begin();
304     for(; it != maFontContainer.end(); ++it )
305         delete (*it).second;
306     maFontContainer.clear();
307 
308     ATSUDisposeFontFallbacks( maFontFallbacks );
309 }
310 
311 // -----------------------------------------------------------------------
312 
313 void SystemFontList::AnnounceFonts( ImplDevFontList& rFontList ) const
314 {
315     MacFontContainer::const_iterator it = maFontContainer.begin();
316     for(; it != maFontContainer.end(); ++it )
317         rFontList.Add( (*it).second->Clone() );
318 }
319 
320 // -----------------------------------------------------------------------
321 
322 // not all fonts are suitable for glyph fallback => sort them
323 struct GfbCompare{ bool operator()(const ImplMacFontData*, const ImplMacFontData*); };
324 
325 inline bool GfbCompare::operator()( const ImplMacFontData* pA, const ImplMacFontData* pB )
326 {
327 	// use symbol fonts only as last resort
328 	bool bPreferA = !pA->IsSymbolFont();
329 	bool bPreferB = !pB->IsSymbolFont();
330 	if( bPreferA != bPreferB )
331 		return bPreferA;
332 	// prefer scalable fonts
333 	bPreferA = pA->IsScalable();
334 	bPreferB = pB->IsScalable();
335 	if( bPreferA != bPreferB )
336 		return bPreferA;
337 	// prefer non-slanted fonts
338 	bPreferA = (pA->GetSlant() == ITALIC_NONE);
339 	bPreferB = (pB->GetSlant() == ITALIC_NONE);
340 	if( bPreferA != bPreferB )
341 		return bPreferA;
342 	// prefer normal weight fonts
343 	bPreferA = (pA->GetWeight() == WEIGHT_NORMAL);
344 	bPreferB = (pB->GetWeight() == WEIGHT_NORMAL);
345 	if( bPreferA != bPreferB )
346 		return bPreferA;
347 	// prefer normal width fonts
348 	bPreferA = (pA->GetWidthType() == WIDTH_NORMAL);
349 	bPreferB = (pB->GetWidthType() == WIDTH_NORMAL);
350 	if( bPreferA != bPreferB )
351 		return bPreferA;
352 	return false;
353 }
354 
355 void SystemFontList::InitGlyphFallbacks()
356 {
357     // sort fonts for "glyph fallback"
358     typedef std::multiset<const ImplMacFontData*,GfbCompare> FallbackSet;
359     FallbackSet aFallbackSet;
360     MacFontContainer::const_iterator it = maFontContainer.begin();
361     for(; it != maFontContainer.end(); ++it )
362     {
363 	const ImplMacFontData* pIFD = (*it).second;
364 	// TODO: subsettable/embeddable glyph fallback only for PDF export?
365         if( pIFD->IsSubsettable() || pIFD->IsEmbeddable() )
366 	    aFallbackSet.insert( pIFD );
367     }
368 
369     // tell ATSU about font preferences for "glyph fallback"
370     typedef std::vector<ATSUFontID> AtsFontIDVector;
371     AtsFontIDVector aFallbackVector;
372     aFallbackVector.reserve( maFontContainer.size() );
373     FallbackSet::const_iterator itFData = aFallbackSet.begin();
374     for(; itFData != aFallbackSet.end(); ++itFData )
375     {
376         const ImplMacFontData* pFontData = (*itFData);
377         ATSUFontID nFontID = (ATSUFontID)pFontData->GetFontId();
378         aFallbackVector.push_back( nFontID );
379     }
380 
381     ATSUCreateFontFallbacks( &maFontFallbacks );
382     ATSUSetObjFontFallbacks( maFontFallbacks,
383 		aFallbackVector.size(), &aFallbackVector[0], kATSUSequentialFallbacksPreferred );
384 }
385 
386 // -----------------------------------------------------------------------
387 
388 ImplMacFontData* SystemFontList::GetFontDataFromId( ATSUFontID nFontId ) const
389 {
390     MacFontContainer::const_iterator it = maFontContainer.find( nFontId );
391     if( it == maFontContainer.end() )
392 	return NULL;
393     return (*it).second;
394 }
395 
396 // -----------------------------------------------------------------------
397 
398