xref: /trunk/main/unotools/source/config/fontcfg.cxx (revision cdf0e10c)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_unotools.hxx"
30 #include <unotools/fontcfg.hxx>
31 #include <unotools/fontdefs.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <com/sun/star/uno/Any.hxx>
34 #include <com/sun/star/uno/Sequence.hxx>
35 #include <com/sun/star/beans/PropertyValue.hpp>
36 #include <unotools/configpathes.hxx>
37 #include <unotools/syslocale.hxx>
38 #include <rtl/ustrbuf.hxx>
39 #include <tools/debug.hxx>
40 
41 #if OSL_DEBUG_LEVEL > 1
42 #include <stdio.h>
43 #endif
44 
45 #include <string.h>
46 #include <list>
47 #include <algorithm>
48 
49 #define DEFAULTFONT_CONFIGNODE "VCL/DefaultFonts"
50 #define SUBSTFONT_CONFIGNODE "VCL/FontSubstitutions"
51 
52 using namespace rtl;
53 using namespace utl;
54 using namespace com::sun::star::uno;
55 using namespace com::sun::star::lang;
56 using namespace com::sun::star::beans;
57 using namespace com::sun::star::container;
58 
59 static DefaultFontConfiguration* mpDefaultFontConfiguration = 0;
60 
61 static FontSubstConfiguration* mpFontSubstConfiguration = 0;
62 
63 /*
64  * DefaultFontConfiguration
65  */
66 
67 static const char* getKeyType( int nKeyType )
68 {
69     switch( nKeyType )
70     {
71     case DEFAULTFONT_CJK_DISPLAY: return "CJK_DISPLAY";
72     case DEFAULTFONT_CJK_HEADING: return "CJK_HEADING";
73     case DEFAULTFONT_CJK_PRESENTATION: return "CJK_PRESENTATION";
74     case DEFAULTFONT_CJK_SPREADSHEET: return "CJK_SPREADSHEET";
75     case DEFAULTFONT_CJK_TEXT: return "CJK_TEXT";
76     case DEFAULTFONT_CTL_DISPLAY: return "CTL_DISPLAY";
77     case DEFAULTFONT_CTL_HEADING: return "CTL_HEADING";
78     case DEFAULTFONT_CTL_PRESENTATION: return "CTL_PRESENTATION";
79     case DEFAULTFONT_CTL_SPREADSHEET: return "CTL_SPREADSHEET";
80     case DEFAULTFONT_CTL_TEXT: return "CTL_TEXT";
81     case DEFAULTFONT_FIXED: return "FIXED";
82     case DEFAULTFONT_LATIN_DISPLAY: return "LATIN_DISPLAY";
83     case DEFAULTFONT_LATIN_FIXED: return "LATIN_FIXED";
84     case DEFAULTFONT_LATIN_HEADING: return "LATIN_HEADING";
85     case DEFAULTFONT_LATIN_PRESENTATION: return "LATIN_PRESENTATION";
86     case DEFAULTFONT_LATIN_SPREADSHEET: return "LATIN_SPREADSHEET";
87     case DEFAULTFONT_LATIN_TEXT: return "LATIN_TEXT";
88     case DEFAULTFONT_SANS: return "SANS";
89     case DEFAULTFONT_SANS_UNICODE: return "SANS_UNICODE";
90     case DEFAULTFONT_SERIF: return "SERIF";
91     case DEFAULTFONT_SYMBOL: return "SYMBOL";
92     case DEFAULTFONT_UI_FIXED: return "UI_FIXED";
93     case DEFAULTFONT_UI_SANS: return "UI_SANS";
94     default:
95         DBG_ERROR( "unmatched type" );
96         return "";
97     }
98 }
99 
100 DefaultFontConfiguration* DefaultFontConfiguration::get()
101 {
102     if( !mpDefaultFontConfiguration )
103         mpDefaultFontConfiguration = new DefaultFontConfiguration();
104     return mpDefaultFontConfiguration;
105 }
106 
107 DefaultFontConfiguration::DefaultFontConfiguration()
108 {
109     try
110     {
111         // get service provider
112         Reference< XMultiServiceFactory > xSMgr( comphelper::getProcessServiceFactory() );
113         // create configuration hierachical access name
114         if( xSMgr.is() )
115         {
116             try
117             {
118                 m_xConfigProvider =
119                     Reference< XMultiServiceFactory >(
120                         xSMgr->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
121                                         "com.sun.star.configuration.ConfigurationProvider" ))),
122                         UNO_QUERY );
123                 if( m_xConfigProvider.is() )
124                 {
125                     Sequence< Any > aArgs(1);
126                     PropertyValue aVal;
127                     aVal.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) );
128                     aVal.Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.VCL/DefaultFonts" ) );
129                     aArgs.getArray()[0] <<= aVal;
130                     m_xConfigAccess =
131                         Reference< XNameAccess >(
132                             m_xConfigProvider->createInstanceWithArguments( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
133                                                 "com.sun.star.configuration.ConfigurationAccess" )),
134                                                                             aArgs ),
135                             UNO_QUERY );
136                     if( m_xConfigAccess.is() )
137                     {
138                         Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
139                         // fill config hash with empty interfaces
140                         int nLocales = aLocales.getLength();
141                         const OUString* pLocaleStrings = aLocales.getConstArray();
142                         Locale aLoc;
143                         for( int i = 0; i < nLocales; i++ )
144                         {
145                             sal_Int32 nIndex = 0;
146                             aLoc.Language = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiLowerCase();
147                             if( nIndex != -1 )
148                                 aLoc.Country = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
149                             else
150                                 aLoc.Country = OUString();
151                             if( nIndex != -1 )
152                                 aLoc.Variant = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
153                             else
154                                 aLoc.Variant = OUString();
155                             m_aConfig[ aLoc ] = LocaleAccess();
156                             m_aConfig[ aLoc ].aConfigLocaleString = pLocaleStrings[i];
157                         }
158                     }
159                 }
160             }
161             catch( Exception& )
162             {
163                 // configuration is awry
164                 m_xConfigProvider.clear();
165                 m_xConfigAccess.clear();
166             }
167         }
168     }
169     catch( WrappedTargetException& )
170     {
171     }
172     #if OSL_DEBUG_LEVEL > 1
173     fprintf( stderr, "config provider: %s, config access: %s\n",
174              m_xConfigProvider.is() ? "true" : "false",
175              m_xConfigAccess.is() ? "true" : "false"
176              );
177     #endif
178 }
179 
180 DefaultFontConfiguration::~DefaultFontConfiguration()
181 {
182     // release all nodes
183     m_aConfig.clear();
184     // release top node
185     m_xConfigAccess.clear();
186     // release config provider
187     m_xConfigProvider.clear();
188 }
189 
190 OUString DefaultFontConfiguration::tryLocale( const Locale& rLocale, const OUString& rType ) const
191 {
192     OUString aRet;
193 
194     std::hash_map< Locale, LocaleAccess, LocaleHash >::const_iterator it =
195         m_aConfig.find( rLocale );
196     if( it != m_aConfig.end() )
197     {
198         if( !it->second.xAccess.is() )
199         {
200             try
201             {
202                 Reference< XNameAccess > xNode;
203                 if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
204                 {
205                     Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
206                 	if( aAny >>= xNode )
207                 	    it->second.xAccess = xNode;
208 				}
209             }
210             catch( NoSuchElementException )
211             {
212             }
213             catch( WrappedTargetException )
214             {
215             }
216         }
217         if( it->second.xAccess.is() )
218         {
219             try
220             {
221                 if ( it->second.xAccess->hasByName( rType ) )
222                 {
223                     Any aAny = it->second.xAccess->getByName( rType );
224                     aAny >>= aRet;
225                 }
226             }
227             catch( NoSuchElementException& )
228             {
229             }
230             catch( WrappedTargetException& )
231             {
232             }
233         }
234     }
235 
236     return aRet;
237 }
238 
239 OUString DefaultFontConfiguration::getDefaultFont( const Locale& rLocale, int nType ) const
240 {
241     Locale aLocale;
242     aLocale.Language = rLocale.Language.toAsciiLowerCase();
243     aLocale.Country = rLocale.Country.toAsciiUpperCase();
244     aLocale.Variant = rLocale.Variant.toAsciiUpperCase();
245 
246     OUString aType = OUString::createFromAscii( getKeyType( nType ) );
247     OUString aRet = tryLocale( aLocale, aType );
248     if( ! aRet.getLength() && aLocale.Variant.getLength() )
249     {
250         aLocale.Variant = OUString();
251         aRet = tryLocale( aLocale, aType );
252     }
253     if( ! aRet.getLength() && aLocale.Country.getLength() )
254     {
255         aLocale.Country = OUString();
256         aRet = tryLocale( aLocale, aType );
257     }
258     if( ! aRet.getLength() )
259     {
260         aLocale.Language = OUString( RTL_CONSTASCII_USTRINGPARAM( "en" ) );
261         aRet = tryLocale( aLocale, aType );
262     }
263     return aRet;
264 }
265 
266 OUString DefaultFontConfiguration::getUserInterfaceFont( const Locale& rLocale ) const
267 {
268     Locale aLocale = rLocale;
269     if( ! aLocale.Language.getLength() )
270         aLocale = SvtSysLocale().GetUILocale();
271 
272     OUString aUIFont = getDefaultFont( aLocale, DEFAULTFONT_UI_SANS );
273 
274     if( aUIFont.getLength() )
275         return aUIFont;
276 
277     // fallback mechanism (either no configuration or no entry in configuration
278 
279     #define FALLBACKFONT_UI_SANS "Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Bitstream Vera Sans;gnu-unifont;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System"
280     #define FALLBACKFONT_UI_SANS_LATIN2 "Andale Sans UI;Albany;Albany AMT;Tahoma;Arial Unicode MS;Arial;Nimbus Sans L;Luxi Sans;Bitstream Vera Sans;Interface User;Geneva;WarpSans;Dialog;Swiss;Lucida;Helvetica;Charcoal;Chicago;MS Sans Serif;Helv;Times;Times New Roman;Interface System"
281     #define FALLBACKFONT_UI_SANS_ARABIC "Tahoma;Traditional Arabic;Simplified Arabic;Lucidasans;Lucida Sans;Supplement;Andale Sans UI;clearlyU;Interface User;Arial Unicode MS;Lucida Sans Unicode;WarpSans;Geneva;MS Sans Serif;Helv;Dialog;Albany;Lucida;Helvetica;Charcoal;Chicago;Arial;Helmet;Interface System;Sans Serif"
282     #define FALLBACKFONT_UI_SANS_THAI "OONaksit;Tahoma;Lucidasans;Arial Unicode MS"
283     #define FALLBACKFONT_UI_SANS_KOREAN "SunGulim;BaekmukGulim;Gulim;Roundgothic;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI"
284     #define FALLBACKFONT_UI_SANS_JAPANESE1 "HG-GothicB-Sun;Andale Sans UI;HG MhinchoLightJ"
285     #define FALLBACKFONT_UI_SANS_JAPANESE2 "Kochi Gothic;Gothic"
286     #define FALLBACKFONT_UI_SANS_CHINSIM "Andale Sans UI;Arial Unicode MS;ZYSong18030;AR PL SungtiL GB;AR PL KaitiM GB;SimSun;Lucida Sans Unicode;Fangsong;Hei;Song;Kai;Ming;gnu-unifont;Interface User;"
287     #define FALLBACKFONT_UI_SANS_CHINTRD "Andale Sans UI;Arial Unicode MS;AR PL Mingti2L Big5;AR PL KaitiM Big5;Kai;PMingLiU;MingLiU;Ming;Lucida Sans Unicode;gnu-unifont;Interface User;"
288 
289     // we need localized names for japanese fonts
290     static sal_Unicode const aMSGothic[] = { 0xFF2D, 0xFF33, ' ', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
291     static sal_Unicode const aMSPGothic[] = { 0xFF2D, 0xFF33, ' ', 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
292     static sal_Unicode const aTLPGothic[] = { 0x0054, 0x004C, 0x0050, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
293     static sal_Unicode const aLXGothic[] = { 0x004C, 0x0058, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
294     static sal_Unicode const aKochiGothic[] = { 0x6771, 0x98A8, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0, 0 };
295 
296     String aFallBackJapaneseLocalized( RTL_CONSTASCII_USTRINGPARAM( "MS UI Gothic;" ) );
297     aFallBackJapaneseLocalized += String( RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_JAPANESE1 ) );
298     aFallBackJapaneseLocalized += String( aMSPGothic );
299     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
300     aFallBackJapaneseLocalized += String( aMSGothic );
301     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
302     aFallBackJapaneseLocalized += String( aTLPGothic );
303     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
304     aFallBackJapaneseLocalized += String( aLXGothic );
305     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
306     aFallBackJapaneseLocalized += String( aKochiGothic );
307     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
308     aFallBackJapaneseLocalized += String(RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_JAPANESE2 ) );
309     static const OUString aFallBackJapanese( aFallBackJapaneseLocalized );
310     static const OUString aFallback (RTL_CONSTASCII_USTRINGPARAM(FALLBACKFONT_UI_SANS));
311     static const OUString aFallbackLatin2 (RTL_CONSTASCII_USTRINGPARAM(FALLBACKFONT_UI_SANS_LATIN2));
312     static const OUString aFallBackArabic (RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_ARABIC ) );
313     static const OUString aFallBackThai (RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_THAI ) );
314     static const OUString aFallBackChineseSIM (RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_CHINSIM ) );
315     static const OUString aFallBackChineseTRD (RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_CHINTRD ) );
316 
317     // we need localized names for korean fonts
318     static sal_Unicode const aSunGulim[] = { 0xC36C, 0xAD74, 0xB9BC, 0 };
319     static sal_Unicode const aBaekmukGulim[] = { 0xBC31, 0xBC35, 0xAD74, 0xB9BC, 0 };
320     String aFallBackKoreanLocalized( aSunGulim );
321     aFallBackKoreanLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
322     aFallBackKoreanLocalized += String( aBaekmukGulim );
323     aFallBackKoreanLocalized += String(RTL_CONSTASCII_USTRINGPARAM( ";" ) );
324     aFallBackKoreanLocalized += String(RTL_CONSTASCII_USTRINGPARAM( FALLBACKFONT_UI_SANS_KOREAN ) );
325     static const OUString aFallBackKorean( aFallBackKoreanLocalized );
326 
327     // optimize font list for some locales, as long as Andale Sans UI does not support them
328     if( aLocale.Language.equalsAscii( "ar" ) ||
329         aLocale.Language.equalsAscii( "he" ) ||
330         aLocale.Language.equalsAscii( "iw" ) )
331     {
332         return aFallBackArabic;
333     }
334     else if( aLocale.Language.equalsAscii( "th" ) )
335     {
336         return aFallBackThai;
337     }
338     else if( aLocale.Language.equalsAscii( "ko" ) )
339     {
340         return aFallBackKorean;
341     }
342     else if( aLocale.Language.equalsAscii( "cs" ) ||
343              aLocale.Language.equalsAscii( "hu" ) ||
344              aLocale.Language.equalsAscii( "pl" ) ||
345              aLocale.Language.equalsAscii( "ro" ) ||
346              aLocale.Language.equalsAscii( "rm" ) ||
347              aLocale.Language.equalsAscii( "hr" ) ||
348              aLocale.Language.equalsAscii( "sk" ) ||
349              aLocale.Language.equalsAscii( "sl" ) ||
350              aLocale.Language.equalsAscii( "sb" ) )
351     {
352         return aFallbackLatin2;
353     }
354     else if( aLocale.Language.equalsAscii( "zh" ) )
355     {
356         if( ! aLocale.Country.equalsAscii( "cn" ) )
357             return aFallBackChineseTRD;
358         else
359             return aFallBackChineseSIM;
360     }
361     else if( aLocale.Language.equalsAscii( "ja" ) )
362     {
363         return aFallBackJapanese;
364     }
365 
366    return aFallback;
367 }
368 
369 // ------------------------------------------------------------------------------------
370 
371 /*
372  *  FontSubstConfigItem::get
373  */
374 
375 FontSubstConfiguration* FontSubstConfiguration::get()
376 {
377     if( !mpFontSubstConfiguration )
378         mpFontSubstConfiguration = new FontSubstConfiguration();
379     return mpFontSubstConfiguration;
380 }
381 
382 /*
383  *  FontSubstConfigItem::FontSubstConfigItem
384  */
385 
386 FontSubstConfiguration::FontSubstConfiguration() :
387 	maSubstHash( 300 )
388 {
389     try
390     {
391         // get service provider
392         Reference< XMultiServiceFactory > xSMgr( comphelper::getProcessServiceFactory() );
393         // create configuration hierachical access name
394         if( xSMgr.is() )
395         {
396             try
397             {
398                 m_xConfigProvider =
399                     Reference< XMultiServiceFactory >(
400                         xSMgr->createInstance( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
401                                         "com.sun.star.configuration.ConfigurationProvider" ))),
402                         UNO_QUERY );
403                 if( m_xConfigProvider.is() )
404                 {
405                     Sequence< Any > aArgs(1);
406                     PropertyValue aVal;
407                     aVal.Name = OUString( RTL_CONSTASCII_USTRINGPARAM( "nodepath" ) );
408                     aVal.Value <<= OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.VCL/FontSubstitutions" ) );
409                     aArgs.getArray()[0] <<= aVal;
410                     m_xConfigAccess =
411                         Reference< XNameAccess >(
412                             m_xConfigProvider->createInstanceWithArguments( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
413                                                 "com.sun.star.configuration.ConfigurationAccess" )),
414                                                                             aArgs ),
415                             UNO_QUERY );
416                     if( m_xConfigAccess.is() )
417                     {
418                         Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
419                         // fill config hash with empty interfaces
420                         int nLocales = aLocales.getLength();
421                         const OUString* pLocaleStrings = aLocales.getConstArray();
422                         Locale aLoc;
423                         for( int i = 0; i < nLocales; i++ )
424                         {
425                             sal_Int32 nIndex = 0;
426                             aLoc.Language = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiLowerCase();
427                             if( nIndex != -1 )
428                                 aLoc.Country = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
429                             else
430                                 aLoc.Country = OUString();
431                             if( nIndex != -1 )
432                                 aLoc.Variant = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
433                             else
434                                 aLoc.Variant = OUString();
435                             m_aSubst[ aLoc ] = LocaleSubst();
436                             m_aSubst[ aLoc ].aConfigLocaleString = pLocaleStrings[i];
437                         }
438                     }
439                 }
440             }
441             catch( Exception& )
442             {
443                 // configuration is awry
444                 m_xConfigProvider.clear();
445                 m_xConfigAccess.clear();
446             }
447         }
448     }
449     catch( WrappedTargetException& )
450     {
451     }
452     #if OSL_DEBUG_LEVEL > 1
453     fprintf( stderr, "config provider: %s, config access: %s\n",
454              m_xConfigProvider.is() ? "true" : "false",
455              m_xConfigAccess.is() ? "true" : "false"
456              );
457     #endif
458 }
459 
460 /*
461  *  FontSubstConfigItem::~FontSubstConfigItem
462  */
463 
464 FontSubstConfiguration::~FontSubstConfiguration()
465 {
466     // release config access
467     m_xConfigAccess.clear();
468     // release config provider
469     m_xConfigProvider.clear();
470 }
471 
472 /*
473  *  FontSubstConfigItem::getMapName
474  */
475 // =======================================================================
476 
477 static const char* const aImplKillLeadingList[] =
478 {
479     "microsoft",
480     "monotype",
481     "linotype",
482     "baekmuk",
483     "adobe",
484     "nimbus",
485     "zycjk",
486     "itc",
487     "sun",
488     "amt",
489     "ms",
490     "mt",
491     "cg",
492     "hg",
493     "fz",
494     "ipa",
495     "sazanami",
496     "kochi",
497     NULL
498 };
499 
500 // -----------------------------------------------------------------------
501 
502 static const char* const aImplKillTrailingList[] =
503 {
504     "microsoft",
505     "monotype",
506     "linotype",
507     "adobe",
508     "nimbus",
509     "itc",
510     "sun",
511     "amt",
512     "ms",
513     "mt",
514     "clm",
515     // Scripts, for compatibility with older versions
516     "we",
517     "cyr",
518     "tur",
519     "wt",
520     "greek",
521     "wl",
522     // CJK extensions
523     "gb",
524     "big5",
525     "pro",
526     "z01",
527     "z02",
528     "z03",
529     "z13",
530     "b01",
531     "w3x12",
532     // Old Printer Fontnames
533     "5cpi",
534     "6cpi",
535     "7cpi",
536     "8cpi",
537     "9cpi",
538     "10cpi",
539     "11cpi",
540     "12cpi",
541     "13cpi",
542     "14cpi",
543     "15cpi",
544     "16cpi",
545     "18cpi",
546     "24cpi",
547     "scale",
548     "pc",
549     NULL
550 };
551 
552 // -----------------------------------------------------------------------
553 
554 static const char* const aImplKillTrailingWithExceptionsList[] =
555 {
556     "ce", "monospace", "oldface", NULL,
557     "ps", "caps", NULL,
558     NULL
559 };
560 
561 // -----------------------------------------------------------------------
562 
563 struct ImplFontAttrWeightSearchData
564 {
565     const char*             mpStr;
566     FontWeight              meWeight;
567 };
568 
569 static ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
570 {
571 // the attribute names are ordered by "first match wins"
572 // e.g. "semilight" should wins over "semi"
573 {   "extrablack",           WEIGHT_BLACK },
574 {   "ultrablack",           WEIGHT_BLACK },
575 {   "ultrabold",            WEIGHT_ULTRABOLD },
576 {   "semibold",             WEIGHT_SEMIBOLD },
577 {   "semilight",            WEIGHT_SEMILIGHT },
578 {   "semi",                 WEIGHT_SEMIBOLD },
579 {   "demi",                 WEIGHT_SEMIBOLD },
580 {   "black",                WEIGHT_BLACK },
581 {   "bold",                 WEIGHT_BOLD },
582 {   "heavy",                WEIGHT_BLACK },
583 {   "ultralight",           WEIGHT_ULTRALIGHT },
584 {   "light",                WEIGHT_LIGHT },
585 {   "medium",               WEIGHT_MEDIUM },
586 {   NULL,                   WEIGHT_DONTKNOW },
587 };
588 
589 // -----------------------------------------------------------------------
590 
591 struct ImplFontAttrWidthSearchData
592 {
593     const char*             mpStr;
594     FontWidth               meWidth;
595 };
596 
597 static ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] =
598 {
599 {   "narrow",               WIDTH_CONDENSED },
600 {   "semicondensed",        WIDTH_SEMI_CONDENSED },
601 {   "ultracondensed",       WIDTH_ULTRA_CONDENSED },
602 {   "semiexpanded",         WIDTH_SEMI_EXPANDED },
603 {   "ultraexpanded",        WIDTH_ULTRA_EXPANDED },
604 {   "expanded",             WIDTH_EXPANDED },
605 {   "wide",                 WIDTH_ULTRA_EXPANDED },
606 {   "condensed",            WIDTH_CONDENSED },
607 {   "cond",                 WIDTH_CONDENSED },
608 {   "cn",                   WIDTH_CONDENSED },
609 {   NULL,                   WIDTH_DONTKNOW },
610 };
611 
612 struct ImplFontAttrTypeSearchData
613 {
614     const char*             mpStr;
615     sal_uLong                   mnType;
616 };
617 
618 static ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
619 {
620 {   "monotype",             0 },
621 {   "linotype",             0 },
622 {   "titling",              IMPL_FONT_ATTR_TITLING },
623 {   "captitals",            IMPL_FONT_ATTR_CAPITALS },
624 {   "captital",             IMPL_FONT_ATTR_CAPITALS },
625 {   "caps",                 IMPL_FONT_ATTR_CAPITALS },
626 {   "italic",               IMPL_FONT_ATTR_ITALIC },
627 {   "oblique",              IMPL_FONT_ATTR_ITALIC },
628 {   "rounded",              IMPL_FONT_ATTR_ROUNDED },
629 {   "outline",              IMPL_FONT_ATTR_OUTLINE },
630 {   "shadow",               IMPL_FONT_ATTR_SHADOW },
631 {   "handwriting",          IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
632 {   "hand",                 IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
633 {   "signet",               IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
634 {   "script",               IMPL_FONT_ATTR_BRUSHSCRIPT | IMPL_FONT_ATTR_SCRIPT },
635 {   "calligraphy",          IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
636 {   "chancery",             IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
637 {   "corsiva",              IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
638 {   "gothic",               IMPL_FONT_ATTR_SANSSERIF | IMPL_FONT_ATTR_GOTHIC },
639 {   "schoolbook",           IMPL_FONT_ATTR_SERIF | IMPL_FONT_ATTR_SCHOOLBOOK },
640 {   "schlbk",               IMPL_FONT_ATTR_SERIF | IMPL_FONT_ATTR_SCHOOLBOOK },
641 {   "typewriter",           IMPL_FONT_ATTR_TYPEWRITER | IMPL_FONT_ATTR_FIXED },
642 {   "lineprinter",          IMPL_FONT_ATTR_TYPEWRITER | IMPL_FONT_ATTR_FIXED },
643 {   "monospaced",           IMPL_FONT_ATTR_FIXED },
644 {   "monospace",            IMPL_FONT_ATTR_FIXED },
645 {   "mono",                 IMPL_FONT_ATTR_FIXED },
646 {   "fixed",                IMPL_FONT_ATTR_FIXED },
647 {   "sansserif",            IMPL_FONT_ATTR_SANSSERIF },
648 {   "sans",                 IMPL_FONT_ATTR_SANSSERIF },
649 {   "swiss",                IMPL_FONT_ATTR_SANSSERIF },
650 {   "serif",                IMPL_FONT_ATTR_SERIF },
651 {   "bright",               IMPL_FONT_ATTR_SERIF },
652 {   "symbols",              IMPL_FONT_ATTR_SYMBOL },
653 {   "symbol",               IMPL_FONT_ATTR_SYMBOL },
654 {   "dingbats",             IMPL_FONT_ATTR_SYMBOL },
655 {   "dings",                IMPL_FONT_ATTR_SYMBOL },
656 {   "ding",                 IMPL_FONT_ATTR_SYMBOL },
657 {   "bats",                 IMPL_FONT_ATTR_SYMBOL },
658 {   "math",                 IMPL_FONT_ATTR_SYMBOL },
659 {   "oldstyle",             IMPL_FONT_ATTR_OTHERSTYLE },
660 {   "oldface",              IMPL_FONT_ATTR_OTHERSTYLE },
661 {   "old",                  IMPL_FONT_ATTR_OTHERSTYLE },
662 {   "new",                  0 },
663 {   "modern",               0 },
664 {   "lucida",               0 },
665 {   "regular",              0 },
666 {   "extended",             0 },
667 {   "extra",                IMPL_FONT_ATTR_OTHERSTYLE },
668 {   "ext",                  0 },
669 {   "scalable",             0 },
670 {   "scale",                0 },
671 {   "nimbus",               0 },
672 {   "adobe",                0 },
673 {   "itc",                  0 },
674 {   "amt",                  0 },
675 {   "mt",                   0 },
676 {   "ms",                   0 },
677 {   "cpi",                  0 },
678 {   "no",                   0 },
679 {   NULL,                   0 },
680 };
681 
682 // -----------------------------------------------------------------------
683 
684 static bool ImplKillLeading( String& rName, const char* const* ppStr )
685 {
686     for(; *ppStr; ++ppStr )
687     {
688         const char*         pStr = *ppStr;
689         const xub_Unicode*  pNameStr = rName.GetBuffer();
690         while ( (*pNameStr == (xub_Unicode)(unsigned char)*pStr) && *pStr )
691         {
692             pNameStr++;
693             pStr++;
694         }
695         if ( !*pStr )
696         {
697             xub_StrLen nLen = sal::static_int_cast<xub_StrLen>(pNameStr - rName.GetBuffer());
698             rName.Erase( 0, nLen );
699             return true;
700         }
701     }
702 
703     // special case for Baekmuk
704     // TODO: allow non-ASCII KillLeading list
705     const xub_Unicode* pNameStr = rName.GetBuffer();
706     if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
707     {
708         xub_StrLen nLen = (pNameStr[2]==0x0020) ? 3 : 2;
709         rName.Erase( 0, nLen );
710         return true;
711     }
712 
713     return false;
714 }
715 
716 // -----------------------------------------------------------------------
717 
718 static xub_StrLen ImplIsTrailing( const String& rName, const char* pStr )
719 {
720     xub_StrLen nStrLen = static_cast<xub_StrLen>( strlen( pStr ) );
721     if( nStrLen >= rName.Len() )
722         return 0;
723 
724     const xub_Unicode* pEndName = rName.GetBuffer() + rName.Len();
725     const sal_Unicode* pNameStr = pEndName - nStrLen;
726     do if( *(pNameStr++) != *(pStr++) )
727         return 0;
728     while( *pStr );
729 
730     return nStrLen;
731 }
732 
733 // -----------------------------------------------------------------------
734 
735 static bool ImplKillTrailing( String& rName, const char* const* ppStr )
736 {
737     for(; *ppStr; ++ppStr )
738     {
739         xub_StrLen nTrailLen = ImplIsTrailing( rName, *ppStr );
740         if( nTrailLen )
741         {
742             rName.Erase( rName.Len()-nTrailLen );
743             return true;
744         }
745     }
746 
747     return false;
748 }
749 
750 // -----------------------------------------------------------------------
751 
752 static bool ImplKillTrailingWithExceptions( String& rName, const char* const* ppStr )
753 {
754     for(; *ppStr; ++ppStr )
755     {
756         xub_StrLen nTrailLen = ImplIsTrailing( rName, *ppStr );
757         if( nTrailLen )
758         {
759             // check string match against string exceptions
760             while( *++ppStr )
761                 if( ImplIsTrailing( rName, *ppStr ) )
762                     return false;
763 
764             rName.Erase( rName.Len()-nTrailLen );
765             return true;
766         }
767         else
768         {
769             // skip exception strings
770             while( *++ppStr ) ;
771         }
772     }
773 
774     return false;
775 }
776 
777 // -----------------------------------------------------------------------
778 
779 static sal_Bool ImplFindAndErase( String& rName, const char* pStr )
780 {
781     xub_StrLen nPos = rName.SearchAscii( pStr );
782     if ( nPos == STRING_NOTFOUND )
783         return sal_False;
784 
785     const char* pTempStr = pStr;
786     while ( *pTempStr )
787         pTempStr++;
788     rName.Erase( nPos, (xub_StrLen)(pTempStr-pStr) );
789     return sal_True;
790 }
791 
792 // =======================================================================
793 
794 void FontSubstConfiguration::getMapName( const String& rOrgName, String& rShortName,
795     String& rFamilyName, FontWeight& rWeight, FontWidth& rWidth, sal_uLong& rType )
796 {
797     rShortName = rOrgName;
798 
799     // TODO: get rid of the crazy O(N*strlen) searches below
800     // they should be possible in O(strlen)
801 
802     // Kill leading vendor names and other unimportant data
803     ImplKillLeading( rShortName, aImplKillLeadingList );
804 
805     // Kill trailing vendor names and other unimportant data
806     ImplKillTrailing( rShortName, aImplKillTrailingList );
807     ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
808 
809     rFamilyName = rShortName;
810 
811     // Kill attributes from the name and update the data
812     // Weight
813     const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
814     while ( pWeightList->mpStr )
815     {
816         if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
817         {
818             if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
819                 rWeight = pWeightList->meWeight;
820             break;
821         }
822         pWeightList++;
823     }
824 
825     // Width
826     const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
827     while ( pWidthList->mpStr )
828     {
829         if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
830         {
831             if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
832                 rWidth = pWidthList->meWidth;
833             break;
834         }
835         pWidthList++;
836     }
837 
838     // Type
839     rType = 0;
840     const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
841     while ( pTypeList->mpStr )
842     {
843         if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
844             rType |= pTypeList->mnType;
845         pTypeList++;
846     }
847 
848     // Remove numbers
849     // TODO: also remove localized and fullwidth digits
850     xub_StrLen i = 0;
851     while ( i < rFamilyName.Len() )
852     {
853         sal_Unicode c = rFamilyName.GetChar( i );
854         if ( (c >= 0x0030) && (c <= 0x0039) )
855             rFamilyName.Erase( i, 1 );
856         else
857             i++;
858     }
859 }
860 
861 
862 struct StrictStringSort : public ::std::binary_function< const FontNameAttr&, const FontNameAttr&, bool >
863 {
864     bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
865     { return rLeft.Name.CompareTo( rRight.Name ) == COMPARE_LESS ; }
866 };
867 
868 static const char* const pAttribNames[] =
869 {
870     "default",
871     "standard",
872     "normal",
873     "symbol",
874     "fixed",
875     "sansserif",
876     "serif",
877     "decorative",
878     "special",
879     "italic",
880     "title",
881     "capitals",
882     "cjk",
883     "cjk_jp",
884     "cjk_sc",
885     "cjk_tc",
886     "cjk_kr",
887     "ctl",
888     "nonelatin",
889     "full",
890     "outline",
891     "shadow",
892     "rounded",
893     "typewriter",
894     "script",
895     "handwriting",
896     "chancery",
897     "comic",
898     "brushscript",
899     "gothic",
900     "schoolbook",
901     "other"
902 };
903 
904 struct enum_convert
905 {
906     const char* pName;
907     int          nEnum;
908 };
909 
910 
911 static const enum_convert pWeightNames[] =
912 {
913     { "normal", WEIGHT_NORMAL },
914     { "medium", WEIGHT_MEDIUM },
915     { "bold", WEIGHT_BOLD },
916     { "black", WEIGHT_BLACK },
917     { "semibold", WEIGHT_SEMIBOLD },
918     { "light", WEIGHT_LIGHT },
919     { "semilight", WEIGHT_SEMILIGHT },
920     { "ultrabold", WEIGHT_ULTRABOLD },
921     { "semi", WEIGHT_SEMIBOLD },
922     { "demi", WEIGHT_SEMIBOLD },
923     { "heavy", WEIGHT_BLACK },
924     { "unknown", WEIGHT_DONTKNOW },
925     { "thin", WEIGHT_THIN },
926     { "ultralight", WEIGHT_ULTRALIGHT }
927 };
928 
929 static const enum_convert pWidthNames[] =
930 {
931     { "normal", WIDTH_NORMAL },
932     { "condensed", WIDTH_CONDENSED },
933     { "expanded", WIDTH_EXPANDED },
934     { "unknown", WIDTH_DONTKNOW },
935     { "ultracondensed", WIDTH_ULTRA_CONDENSED },
936     { "extracondensed", WIDTH_EXTRA_CONDENSED },
937     { "semicondensed", WIDTH_SEMI_CONDENSED },
938     { "semiexpanded", WIDTH_SEMI_EXPANDED },
939     { "extraexpanded", WIDTH_EXTRA_EXPANDED },
940     { "ultraexpanded", WIDTH_ULTRA_EXPANDED }
941 };
942 
943 void FontSubstConfiguration::fillSubstVector( const com::sun::star::uno::Reference< XNameAccess > xFont,
944                                               const rtl::OUString& rType,
945                                               std::vector< String >& rSubstVector ) const
946 {
947     try
948     {
949         Any aAny = xFont->getByName( rType );
950         if( aAny.getValueTypeClass() == TypeClass_STRING )
951         {
952             const OUString* pLine = (const OUString*)aAny.getValue();
953             sal_Int32 nIndex = 0;
954             sal_Int32 nLength = pLine->getLength();
955             if( nLength )
956             {
957                 const sal_Unicode* pStr = pLine->getStr();
958                 sal_Int32 nTokens = 0;
959                 // count tokens
960                 while( nLength-- )
961                 {
962                     if( *pStr++ == sal_Unicode(';') )
963                         nTokens++;
964                 }
965                 rSubstVector.clear();
966                 // optimize performance, heap fragmentation
967                 rSubstVector.reserve( nTokens );
968                 while( nIndex != -1 )
969                 {
970                     OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
971                     if( aSubst.getLength() )
972 					{
973 						UniqueSubstHash::iterator aEntry = maSubstHash.find( aSubst );
974 						if (aEntry != maSubstHash.end())
975 							aSubst = *aEntry;
976 						else
977 							maSubstHash.insert( aSubst );
978                         rSubstVector.push_back( aSubst );
979 					}
980                 }
981             }
982         }
983     }
984     catch( NoSuchElementException )
985     {
986     }
987     catch( WrappedTargetException )
988     {
989     }
990 }
991 
992 FontWeight FontSubstConfiguration::getSubstWeight( const com::sun::star::uno::Reference< XNameAccess > xFont,
993                                                    const rtl::OUString& rType ) const
994 {
995     int weight = -1;
996     try
997     {
998         Any aAny = xFont->getByName( rType );
999         if( aAny.getValueTypeClass() == TypeClass_STRING )
1000         {
1001             const OUString* pLine = (const OUString*)aAny.getValue();
1002             if( pLine->getLength() )
1003             {
1004                 for( weight=sizeof(pWeightNames)/sizeof(pWeightNames[0])-1; weight >= 0; weight-- )
1005                     if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
1006                         break;
1007             }
1008 #if OSL_DEBUG_LEVEL > 1
1009             if( weight < 0 )
1010                 fprintf( stderr, "Error: invalid weight %s\n",
1011                          OUStringToOString( *pLine, RTL_TEXTENCODING_ASCII_US ).getStr() );
1012 #endif
1013         }
1014     }
1015     catch( NoSuchElementException )
1016     {
1017     }
1018     catch( WrappedTargetException )
1019     {
1020     }
1021     return (FontWeight)( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
1022 }
1023 
1024 FontWidth FontSubstConfiguration::getSubstWidth( const com::sun::star::uno::Reference< XNameAccess > xFont,
1025                                                  const rtl::OUString& rType ) const
1026 {
1027     int width = -1;
1028     try
1029     {
1030         Any aAny = xFont->getByName( rType );
1031         if( aAny.getValueTypeClass() == TypeClass_STRING )
1032         {
1033             const OUString* pLine = (const OUString*)aAny.getValue();
1034             if( pLine->getLength() )
1035             {
1036                 for( width=sizeof(pWidthNames)/sizeof(pWidthNames[0])-1; width >= 0; width-- )
1037                     if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
1038                         break;
1039             }
1040 #if OSL_DEBUG_LEVEL > 1
1041             if( width < 0 )
1042                 fprintf( stderr, "Error: invalid width %s\n",
1043                          OUStringToOString( *pLine, RTL_TEXTENCODING_ASCII_US ).getStr() );
1044 #endif
1045         }
1046     }
1047     catch( NoSuchElementException )
1048     {
1049     }
1050     catch( WrappedTargetException )
1051     {
1052     }
1053     return (FontWidth)( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
1054 }
1055 
1056 unsigned long FontSubstConfiguration::getSubstType( const com::sun::star::uno::Reference< XNameAccess > xFont,
1057                                                     const rtl::OUString& rType ) const
1058 {
1059     unsigned long type = 0;
1060     try
1061     {
1062         Any aAny = xFont->getByName( rType );
1063         if( aAny.getValueTypeClass() == TypeClass_STRING )
1064         {
1065             const OUString* pLine = (const OUString*)aAny.getValue();
1066             if( pLine->getLength() )
1067             {
1068                 sal_Int32 nIndex = 0;
1069                 while( nIndex != -1 )
1070                 {
1071                     String aToken( pLine->getToken( 0, ',', nIndex ) );
1072                     for( int k = 0; k < 32; k++ )
1073                         if( aToken.EqualsIgnoreCaseAscii( pAttribNames[k] ) )
1074                         {
1075                             type |= 1 << k;
1076                             break;
1077                         }
1078                 }
1079             }
1080         }
1081     }
1082     catch( NoSuchElementException )
1083     {
1084     }
1085     catch( WrappedTargetException )
1086     {
1087     }
1088 
1089     return type;
1090 }
1091 
1092 void FontSubstConfiguration::readLocaleSubst( const com::sun::star::lang::Locale& rLocale ) const
1093 {
1094     std::hash_map< Locale, LocaleSubst, LocaleHash >::const_iterator it =
1095         m_aSubst.find( rLocale );
1096     if( it != m_aSubst.end() )
1097     {
1098         if( ! it->second.bConfigRead )
1099         {
1100             it->second.bConfigRead = true;
1101             Reference< XNameAccess > xNode;
1102             try
1103             {
1104                 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
1105                 aAny >>= xNode;
1106             }
1107             catch( NoSuchElementException )
1108             {
1109             }
1110             catch( WrappedTargetException )
1111             {
1112             }
1113             if( xNode.is() )
1114             {
1115                 Sequence< OUString > aFonts = xNode->getElementNames();
1116                 int nFonts = aFonts.getLength();
1117                 const OUString* pFontNames = aFonts.getConstArray();
1118                 // improve performance, heap fragmentation
1119                 it->second.aSubstAttributes.reserve( nFonts );
1120 
1121                 // strings for subst retrieval, construct only once
1122                 OUString aSubstFontsStr     ( RTL_CONSTASCII_USTRINGPARAM( "SubstFonts" ) );
1123                 OUString aSubstFontsMSStr   ( RTL_CONSTASCII_USTRINGPARAM( "SubstFontsMS" ) );
1124                 OUString aSubstFontsPSStr   ( RTL_CONSTASCII_USTRINGPARAM( "SubstFontsPS" ) );
1125                 OUString aSubstFontsHTMLStr ( RTL_CONSTASCII_USTRINGPARAM( "SubstFontsHTML" ) );
1126                 OUString aSubstWeightStr    ( RTL_CONSTASCII_USTRINGPARAM( "FontWeight" ) );
1127                 OUString aSubstWidthStr     ( RTL_CONSTASCII_USTRINGPARAM( "FontWidth" ) );
1128                 OUString aSubstTypeStr      ( RTL_CONSTASCII_USTRINGPARAM( "FontType" ) );
1129                 for( int i = 0; i < nFonts; i++ )
1130                 {
1131                     Reference< XNameAccess > xFont;
1132                     try
1133                     {
1134                         Any aAny = xNode->getByName( pFontNames[i] );
1135                         aAny >>= xFont;
1136                     }
1137                     catch( NoSuchElementException )
1138                     {
1139                     }
1140                     catch( WrappedTargetException )
1141                     {
1142                     }
1143                     if( ! xFont.is() )
1144                     {
1145                         #if OSL_DEBUG_LEVEL > 1
1146                         fprintf( stderr, "did not get font attributes for %s\n",
1147                                  OUStringToOString( pFontNames[i], RTL_TEXTENCODING_UTF8 ).getStr() );
1148                         #endif
1149                         continue;
1150                     }
1151 
1152                     FontNameAttr aAttr;
1153                     // read subst attributes from config
1154                     aAttr.Name = pFontNames[i];
1155                     fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
1156                     fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
1157                     fillSubstVector( xFont, aSubstFontsPSStr, aAttr.PSSubstitutions );
1158                     fillSubstVector( xFont, aSubstFontsHTMLStr, aAttr.HTMLSubstitutions );
1159                     aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
1160                     aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
1161                     aAttr.Type = getSubstType( xFont, aSubstTypeStr );
1162 
1163                     // finally insert this entry
1164                     it->second.aSubstAttributes.push_back( aAttr );
1165                 }
1166                 std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1167             }
1168         }
1169     }
1170 }
1171 
1172 const FontNameAttr* FontSubstConfiguration::getSubstInfo( const String& rFontName, const Locale& rLocale ) const
1173 {
1174     if( !rFontName.Len() )
1175         return NULL;
1176 
1177     // search if a  (language dep.) replacement table for the given font exists
1178     // fallback is english
1179     String aSearchFont( rFontName );
1180     aSearchFont.ToLowerAscii();
1181     FontNameAttr aSearchAttr;
1182     aSearchAttr.Name = aSearchFont;
1183 
1184     Locale aLocale;
1185     aLocale.Language = rLocale.Language.toAsciiLowerCase();
1186     aLocale.Country = rLocale.Country.toAsciiUpperCase();
1187     aLocale.Variant = rLocale.Variant.toAsciiUpperCase();
1188 
1189     if( ! aLocale.Language.getLength() )
1190         aLocale = SvtSysLocale().GetUILocale();
1191 
1192     while( aLocale.Language.getLength() )
1193     {
1194         std::hash_map< Locale, LocaleSubst, LocaleHash >::const_iterator lang = m_aSubst.find( aLocale );
1195         if( lang != m_aSubst.end() )
1196         {
1197             if( ! lang->second.bConfigRead )
1198                 readLocaleSubst( aLocale );
1199             // try to find an exact match
1200             // because the list is sorted this will also find fontnames of the form searchfontname*
1201             std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
1202             if( it != lang->second.aSubstAttributes.end())
1203             {
1204                 const FontNameAttr& rFoundAttr = *it;
1205                 // a search for "abcblack" may match with an entry for "abc"
1206                 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1207                 if( rFoundAttr.Name.Len() <= aSearchFont.Len() )
1208                     if( aSearchFont.CompareTo( rFoundAttr.Name, rFoundAttr.Name.Len() ) == COMPARE_EQUAL )
1209                         return &rFoundAttr;
1210             }
1211         }
1212         // gradually become more unspecific
1213         if( aLocale.Variant.getLength() )
1214             aLocale.Variant = OUString();
1215         else if( aLocale.Country.getLength() )
1216             aLocale.Country = OUString();
1217         else if( ! aLocale.Language.equalsAscii( "en" ) )
1218             aLocale.Language = OUString( RTL_CONSTASCII_USTRINGPARAM( "en" ) );
1219         else
1220             aLocale.Language = OUString();
1221     }
1222     return NULL;
1223 }
1224 
1225