bump product version to 4.1.6.2
[LibreOffice.git] / unotools / source / config / fontcfg.cxx
blob9da6e05c0d672bcb56c8be364856f2742febea0a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <i18nlangtag/mslangid.hxx>
21 #include <unotools/fontcfg.hxx>
22 #include <unotools/fontdefs.hxx>
23 #include <comphelper/processfactory.hxx>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/Sequence.hxx>
26 #include <com/sun/star/beans/PropertyValue.hpp>
27 #include <com/sun/star/configuration/theDefaultProvider.hpp>
28 #include <unotools/configpaths.hxx>
29 #include <unotools/syslocale.hxx>
30 #include <rtl/ustrbuf.hxx>
31 #include <rtl/instance.hxx>
32 #include <sal/macros.h>
34 #if OSL_DEBUG_LEVEL > 1
35 #include <stdio.h>
36 #endif
38 #include <string.h>
39 #include <list>
40 #include <algorithm>
42 using namespace utl;
43 using namespace com::sun::star::uno;
44 using namespace com::sun::star::lang;
45 using namespace com::sun::star::beans;
46 using namespace com::sun::star::container;
47 using namespace com::sun::star::configuration;
51 * DefaultFontConfiguration
54 static const char* getKeyType( int nKeyType )
56 switch( nKeyType )
58 case DEFAULTFONT_CJK_DISPLAY: return "CJK_DISPLAY";
59 case DEFAULTFONT_CJK_HEADING: return "CJK_HEADING";
60 case DEFAULTFONT_CJK_PRESENTATION: return "CJK_PRESENTATION";
61 case DEFAULTFONT_CJK_SPREADSHEET: return "CJK_SPREADSHEET";
62 case DEFAULTFONT_CJK_TEXT: return "CJK_TEXT";
63 case DEFAULTFONT_CTL_DISPLAY: return "CTL_DISPLAY";
64 case DEFAULTFONT_CTL_HEADING: return "CTL_HEADING";
65 case DEFAULTFONT_CTL_PRESENTATION: return "CTL_PRESENTATION";
66 case DEFAULTFONT_CTL_SPREADSHEET: return "CTL_SPREADSHEET";
67 case DEFAULTFONT_CTL_TEXT: return "CTL_TEXT";
68 case DEFAULTFONT_FIXED: return "FIXED";
69 case DEFAULTFONT_LATIN_DISPLAY: return "LATIN_DISPLAY";
70 case DEFAULTFONT_LATIN_FIXED: return "LATIN_FIXED";
71 case DEFAULTFONT_LATIN_HEADING: return "LATIN_HEADING";
72 case DEFAULTFONT_LATIN_PRESENTATION: return "LATIN_PRESENTATION";
73 case DEFAULTFONT_LATIN_SPREADSHEET: return "LATIN_SPREADSHEET";
74 case DEFAULTFONT_LATIN_TEXT: return "LATIN_TEXT";
75 case DEFAULTFONT_SANS: return "SANS";
76 case DEFAULTFONT_SANS_UNICODE: return "SANS_UNICODE";
77 case DEFAULTFONT_SERIF: return "SERIF";
78 case DEFAULTFONT_SYMBOL: return "SYMBOL";
79 case DEFAULTFONT_UI_FIXED: return "UI_FIXED";
80 case DEFAULTFONT_UI_SANS: return "UI_SANS";
81 default:
82 OSL_FAIL( "unmatched type" );
83 return "";
87 namespace
89 class theDefaultFontConfiguration
90 : public rtl::Static<DefaultFontConfiguration,
91 theDefaultFontConfiguration>
96 DefaultFontConfiguration& DefaultFontConfiguration::get()
98 return theDefaultFontConfiguration::get();
101 DefaultFontConfiguration::DefaultFontConfiguration()
105 // get service provider
106 Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
107 // create configuration hierachical access name
110 m_xConfigProvider = theDefaultProvider::get( xContext );
111 Sequence< Any > aArgs(1);
112 PropertyValue aVal;
113 aVal.Name = OUString( "nodepath" );
114 aVal.Value <<= OUString( "/org.openoffice.VCL/DefaultFonts" );
115 aArgs.getArray()[0] <<= aVal;
116 m_xConfigAccess =
117 Reference< XNameAccess >(
118 m_xConfigProvider->createInstanceWithArguments( OUString( "com.sun.star.configuration.ConfigurationAccess" ),
119 aArgs ),
120 UNO_QUERY );
121 if( m_xConfigAccess.is() )
123 Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
124 // fill config hash with empty interfaces
125 int nLocales = aLocales.getLength();
126 const OUString* pLocaleStrings = aLocales.getConstArray();
127 Locale aLoc;
128 for( int i = 0; i < nLocales; i++ )
130 sal_Int32 nIndex = 0;
131 aLoc.Language = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiLowerCase();
132 if( nIndex != -1 )
133 aLoc.Country = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
134 else
135 aLoc.Country = OUString();
136 if( nIndex != -1 )
137 aLoc.Variant = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
138 else
139 aLoc.Variant = OUString();
140 m_aConfig[ aLoc ] = LocaleAccess();
141 m_aConfig[ aLoc ].aConfigLocaleString = pLocaleStrings[i];
145 catch (const Exception&)
147 // configuration is awry
148 m_xConfigProvider.clear();
149 m_xConfigAccess.clear();
152 catch (const WrappedTargetException&)
155 #if OSL_DEBUG_LEVEL > 1
156 fprintf( stderr, "config provider: %s, config access: %s\n",
157 m_xConfigProvider.is() ? "true" : "false",
158 m_xConfigAccess.is() ? "true" : "false"
160 #endif
163 DefaultFontConfiguration::~DefaultFontConfiguration()
165 // release all nodes
166 m_aConfig.clear();
167 // release top node
168 m_xConfigAccess.clear();
169 // release config provider
170 m_xConfigProvider.clear();
173 OUString DefaultFontConfiguration::tryLocale( const Locale& rLocale, const OUString& rType ) const
175 OUString aRet;
177 boost::unordered_map< Locale, LocaleAccess, LocaleHash >::const_iterator it =
178 m_aConfig.find( rLocale );
179 if( it != m_aConfig.end() )
181 if( !it->second.xAccess.is() )
185 Reference< XNameAccess > xNode;
186 if ( m_xConfigAccess->hasByName( it->second.aConfigLocaleString ) )
188 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
189 if( aAny >>= xNode )
190 it->second.xAccess = xNode;
193 catch (const NoSuchElementException&)
196 catch (const WrappedTargetException&)
200 if( it->second.xAccess.is() )
204 if ( it->second.xAccess->hasByName( rType ) )
206 Any aAny = it->second.xAccess->getByName( rType );
207 aAny >>= aRet;
210 catch (const NoSuchElementException&)
213 catch (const WrappedTargetException&)
219 return aRet;
222 OUString DefaultFontConfiguration::getDefaultFont( const Locale& rLocale, int nType ) const
224 Locale aLocale;
225 aLocale.Language = rLocale.Language.toAsciiLowerCase();
226 aLocale.Country = rLocale.Country.toAsciiUpperCase();
227 aLocale.Variant = rLocale.Variant.toAsciiUpperCase();
229 OUString aType = OUString::createFromAscii( getKeyType( nType ) );
230 OUString aRet = tryLocale( aLocale, aType );
231 if( aRet.isEmpty() && !aLocale.Variant.isEmpty() )
233 aLocale.Variant = OUString();
234 aRet = tryLocale( aLocale, aType );
236 if( aRet.isEmpty() && !aLocale.Country.isEmpty() )
238 aLocale.Country = OUString();
239 aRet = tryLocale( aLocale, aType );
241 if( aRet.isEmpty() )
243 aLocale.Language = OUString( "en" );
244 aRet = tryLocale( aLocale, aType );
246 return aRet;
249 OUString DefaultFontConfiguration::getUserInterfaceFont( const Locale& rLocale ) const
251 Locale aLocale = rLocale;
252 if( aLocale.Language.isEmpty() )
253 aLocale = SvtSysLocale().GetUILanguageTag().getLocale();
255 OUString aUIFont = getDefaultFont( aLocale, DEFAULTFONT_UI_SANS );
257 if( !aUIFont.isEmpty() )
258 return aUIFont;
260 // fallback mechanism (either no configuration or no entry in configuration
262 #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"
263 #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"
264 #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"
265 #define FALLBACKFONT_UI_SANS_THAI "OONaksit;Tahoma;Lucidasans;Arial Unicode MS"
266 #define FALLBACKFONT_UI_SANS_KOREAN "SunGulim;BaekmukGulim;Gulim;Roundgothic;Arial Unicode MS;Lucida Sans Unicode;gnu-unifont;Andale Sans UI"
267 #define FALLBACKFONT_UI_SANS_JAPANESE1 "HG-GothicB-Sun;Andale Sans UI;HG MhinchoLightJ"
268 #define FALLBACKFONT_UI_SANS_JAPANESE2 "Kochi Gothic;Gothic"
269 #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;"
270 #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;"
272 // optimize font list for some locales, as long as Andale Sans UI does not support them
273 if( aLocale.Language == "ar" || aLocale.Language == "he" || aLocale.Language == "iw" )
275 return OUString(FALLBACKFONT_UI_SANS_ARABIC);
277 else if ( aLocale.Language == "th" )
279 return OUString(FALLBACKFONT_UI_SANS_THAI);
281 else if ( aLocale.Language == "ko" )
283 // we need localized names for korean fonts
284 const sal_Unicode aSunGulim[] = { 0xC36C, 0xAD74, 0xB9BC, 0 };
285 const sal_Unicode aBaekmukGulim[] = { 0xBC31, 0xBC35, 0xAD74, 0xB9BC, 0 };
287 OUStringBuffer aFallBackKoreanLocalized;
288 aFallBackKoreanLocalized.append(aSunGulim);
289 aFallBackKoreanLocalized.append(';');
290 aFallBackKoreanLocalized.append(aBaekmukGulim);
291 aFallBackKoreanLocalized.append(";");
292 aFallBackKoreanLocalized.append(FALLBACKFONT_UI_SANS_KOREAN);
294 return aFallBackKoreanLocalized.makeStringAndClear();
296 else if( aLocale.Language == "cs" ||
297 aLocale.Language == "hu" ||
298 aLocale.Language == "pl" ||
299 aLocale.Language == "ro" ||
300 aLocale.Language == "rm" ||
301 aLocale.Language == "hr" ||
302 aLocale.Language == "sk" ||
303 aLocale.Language == "sl" ||
304 aLocale.Language == "sb")
306 return OUString(FALLBACKFONT_UI_SANS_LATIN2);
308 else if (MsLangId::isTraditionalChinese(aLocale))
309 return OUString(FALLBACKFONT_UI_SANS_CHINTRD);
310 else if (MsLangId::isSimplifiedChinese(aLocale))
311 return OUString(FALLBACKFONT_UI_SANS_CHINSIM);
312 else if ( aLocale.Language == "ja" )
314 // we need localized names for japanese fonts
315 const sal_Unicode aMSGothic[] = { 0xFF2D, 0xFF33, ' ', 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
316 const sal_Unicode aMSPGothic[] = { 0xFF2D, 0xFF33, ' ', 0xFF30, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
317 const sal_Unicode aTLPGothic[] = { 0x0054, 0x004C, 0x0050, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
318 const sal_Unicode aLXGothic[] = { 0x004C, 0x0058, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
319 const sal_Unicode aKochiGothic[] = { 0x6771, 0x98A8, 0x30B4, 0x30B7, 0x30C3, 0x30AF, 0 };
321 OUStringBuffer aFallBackJapaneseLocalized;
322 aFallBackJapaneseLocalized.append("MS UI Gothic;");
323 aFallBackJapaneseLocalized.append(FALLBACKFONT_UI_SANS_JAPANESE1);
324 aFallBackJapaneseLocalized.append(aMSPGothic);
325 aFallBackJapaneseLocalized.append(';');
326 aFallBackJapaneseLocalized.append(aMSGothic);
327 aFallBackJapaneseLocalized.append(';');
328 aFallBackJapaneseLocalized.append(aTLPGothic);
329 aFallBackJapaneseLocalized.append(';');
330 aFallBackJapaneseLocalized.append(aLXGothic);
331 aFallBackJapaneseLocalized.append(';');
332 aFallBackJapaneseLocalized.append(aKochiGothic);
333 aFallBackJapaneseLocalized.append(';');
334 aFallBackJapaneseLocalized.append(FALLBACKFONT_UI_SANS_JAPANESE2);
336 return aFallBackJapaneseLocalized.makeStringAndClear();
339 return OUString(FALLBACKFONT_UI_SANS);
342 // ------------------------------------------------------------------------------------
345 * FontSubstConfigItem::get
348 namespace
350 class theFontSubstConfiguration
351 : public rtl::Static<FontSubstConfiguration, theFontSubstConfiguration>
356 FontSubstConfiguration& FontSubstConfiguration::get()
358 return theFontSubstConfiguration::get();
362 * FontSubstConfigItem::FontSubstConfigItem
365 FontSubstConfiguration::FontSubstConfiguration() :
366 maSubstHash( 300 )
370 // get service provider
371 Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
372 // create configuration hierachical access name
375 m_xConfigProvider = theDefaultProvider::get( xContext );
376 Sequence< Any > aArgs(1);
377 PropertyValue aVal;
378 aVal.Name = OUString( "nodepath" );
379 aVal.Value <<= OUString( "/org.openoffice.VCL/FontSubstitutions" );
380 aArgs.getArray()[0] <<= aVal;
381 m_xConfigAccess =
382 Reference< XNameAccess >(
383 m_xConfigProvider->createInstanceWithArguments( OUString( "com.sun.star.configuration.ConfigurationAccess" ),
384 aArgs ),
385 UNO_QUERY );
386 if( m_xConfigAccess.is() )
388 Sequence< OUString > aLocales = m_xConfigAccess->getElementNames();
389 // fill config hash with empty interfaces
390 int nLocales = aLocales.getLength();
391 const OUString* pLocaleStrings = aLocales.getConstArray();
392 Locale aLoc;
393 for( int i = 0; i < nLocales; i++ )
395 sal_Int32 nIndex = 0;
396 aLoc.Language = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiLowerCase();
397 if( nIndex != -1 )
398 aLoc.Country = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
399 else
400 aLoc.Country = OUString();
401 if( nIndex != -1 )
402 aLoc.Variant = pLocaleStrings[i].getToken( 0, sal_Unicode('-'), nIndex ).toAsciiUpperCase();
403 else
404 aLoc.Variant = OUString();
405 m_aSubst[ aLoc ] = LocaleSubst();
406 m_aSubst[ aLoc ].aConfigLocaleString = pLocaleStrings[i];
410 catch (const Exception&)
412 // configuration is awry
413 m_xConfigProvider.clear();
414 m_xConfigAccess.clear();
417 catch (const WrappedTargetException&)
420 #if OSL_DEBUG_LEVEL > 1
421 fprintf( stderr, "config provider: %s, config access: %s\n",
422 m_xConfigProvider.is() ? "true" : "false",
423 m_xConfigAccess.is() ? "true" : "false"
425 #endif
429 * FontSubstConfigItem::~FontSubstConfigItem
432 FontSubstConfiguration::~FontSubstConfiguration()
434 // release config access
435 m_xConfigAccess.clear();
436 // release config provider
437 m_xConfigProvider.clear();
441 * FontSubstConfigItem::getMapName
443 // =======================================================================
445 static const char* const aImplKillLeadingList[] =
447 "microsoft",
448 "monotype",
449 "linotype",
450 "baekmuk",
451 "adobe",
452 "nimbus",
453 "zycjk",
454 "itc",
455 "sun",
456 "amt",
457 "ms",
458 "mt",
459 "cg",
460 "hg",
461 "fz",
462 "ipa",
463 "sazanami",
464 "kochi",
465 NULL
468 // -----------------------------------------------------------------------
470 static const char* const aImplKillTrailingList[] =
472 "microsoft",
473 "monotype",
474 "linotype",
475 "adobe",
476 "nimbus",
477 "itc",
478 "sun",
479 "amt",
480 "ms",
481 "mt",
482 "clm",
483 // Scripts, for compatibility with older versions
484 "we",
485 "cyr",
486 "tur",
487 "wt",
488 "greek",
489 "wl",
490 // CJK extensions
491 "gb",
492 "big5",
493 "pro",
494 "z01",
495 "z02",
496 "z03",
497 "z13",
498 "b01",
499 "w3x12",
500 // Old Printer Fontnames
501 "5cpi",
502 "6cpi",
503 "7cpi",
504 "8cpi",
505 "9cpi",
506 "10cpi",
507 "11cpi",
508 "12cpi",
509 "13cpi",
510 "14cpi",
511 "15cpi",
512 "16cpi",
513 "18cpi",
514 "24cpi",
515 "scale",
516 "pc",
517 NULL
520 // -----------------------------------------------------------------------
522 static const char* const aImplKillTrailingWithExceptionsList[] =
524 "ce", "monospace", "oldface", NULL,
525 "ps", "caps", NULL,
526 NULL
529 // -----------------------------------------------------------------------
531 struct ImplFontAttrWeightSearchData
533 const char* mpStr;
534 FontWeight meWeight;
537 static ImplFontAttrWeightSearchData const aImplWeightAttrSearchList[] =
539 // the attribute names are ordered by "first match wins"
540 // e.g. "semilight" should wins over "semi"
541 { "extrablack", WEIGHT_BLACK },
542 { "ultrablack", WEIGHT_BLACK },
543 { "ultrabold", WEIGHT_ULTRABOLD },
544 { "semibold", WEIGHT_SEMIBOLD },
545 { "semilight", WEIGHT_SEMILIGHT },
546 { "semi", WEIGHT_SEMIBOLD },
547 { "demi", WEIGHT_SEMIBOLD },
548 { "black", WEIGHT_BLACK },
549 { "bold", WEIGHT_BOLD },
550 { "heavy", WEIGHT_BLACK },
551 { "ultralight", WEIGHT_ULTRALIGHT },
552 { "light", WEIGHT_LIGHT },
553 { "medium", WEIGHT_MEDIUM },
554 { NULL, WEIGHT_DONTKNOW },
557 // -----------------------------------------------------------------------
559 struct ImplFontAttrWidthSearchData
561 const char* mpStr;
562 FontWidth meWidth;
565 static ImplFontAttrWidthSearchData const aImplWidthAttrSearchList[] =
567 { "narrow", WIDTH_CONDENSED },
568 { "semicondensed", WIDTH_SEMI_CONDENSED },
569 { "ultracondensed", WIDTH_ULTRA_CONDENSED },
570 { "semiexpanded", WIDTH_SEMI_EXPANDED },
571 { "ultraexpanded", WIDTH_ULTRA_EXPANDED },
572 { "expanded", WIDTH_EXPANDED },
573 { "wide", WIDTH_ULTRA_EXPANDED },
574 { "condensed", WIDTH_CONDENSED },
575 { "cond", WIDTH_CONDENSED },
576 { "cn", WIDTH_CONDENSED },
577 { NULL, WIDTH_DONTKNOW },
580 struct ImplFontAttrTypeSearchData
582 const char* mpStr;
583 sal_uLong mnType;
586 static ImplFontAttrTypeSearchData const aImplTypeAttrSearchList[] =
588 { "monotype", 0 },
589 { "linotype", 0 },
590 { "titling", IMPL_FONT_ATTR_TITLING },
591 { "captitals", IMPL_FONT_ATTR_CAPITALS },
592 { "captital", IMPL_FONT_ATTR_CAPITALS },
593 { "caps", IMPL_FONT_ATTR_CAPITALS },
594 { "italic", IMPL_FONT_ATTR_ITALIC },
595 { "oblique", IMPL_FONT_ATTR_ITALIC },
596 { "rounded", IMPL_FONT_ATTR_ROUNDED },
597 { "outline", IMPL_FONT_ATTR_OUTLINE },
598 { "shadow", IMPL_FONT_ATTR_SHADOW },
599 { "handwriting", IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
600 { "hand", IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
601 { "signet", IMPL_FONT_ATTR_HANDWRITING | IMPL_FONT_ATTR_SCRIPT },
602 { "script", IMPL_FONT_ATTR_BRUSHSCRIPT | IMPL_FONT_ATTR_SCRIPT },
603 { "calligraphy", IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
604 { "chancery", IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
605 { "corsiva", IMPL_FONT_ATTR_CHANCERY | IMPL_FONT_ATTR_SCRIPT },
606 { "gothic", IMPL_FONT_ATTR_SANSSERIF | IMPL_FONT_ATTR_GOTHIC },
607 { "schoolbook", IMPL_FONT_ATTR_SERIF | IMPL_FONT_ATTR_SCHOOLBOOK },
608 { "schlbk", IMPL_FONT_ATTR_SERIF | IMPL_FONT_ATTR_SCHOOLBOOK },
609 { "typewriter", IMPL_FONT_ATTR_TYPEWRITER | IMPL_FONT_ATTR_FIXED },
610 { "lineprinter", IMPL_FONT_ATTR_TYPEWRITER | IMPL_FONT_ATTR_FIXED },
611 { "monospaced", IMPL_FONT_ATTR_FIXED },
612 { "monospace", IMPL_FONT_ATTR_FIXED },
613 { "mono", IMPL_FONT_ATTR_FIXED },
614 { "fixed", IMPL_FONT_ATTR_FIXED },
615 { "sansserif", IMPL_FONT_ATTR_SANSSERIF },
616 { "sans", IMPL_FONT_ATTR_SANSSERIF },
617 { "swiss", IMPL_FONT_ATTR_SANSSERIF },
618 { "serif", IMPL_FONT_ATTR_SERIF },
619 { "bright", IMPL_FONT_ATTR_SERIF },
620 { "symbols", IMPL_FONT_ATTR_SYMBOL },
621 { "symbol", IMPL_FONT_ATTR_SYMBOL },
622 { "dingbats", IMPL_FONT_ATTR_SYMBOL },
623 { "dings", IMPL_FONT_ATTR_SYMBOL },
624 { "ding", IMPL_FONT_ATTR_SYMBOL },
625 { "bats", IMPL_FONT_ATTR_SYMBOL },
626 { "math", IMPL_FONT_ATTR_SYMBOL },
627 { "oldstyle", IMPL_FONT_ATTR_OTHERSTYLE },
628 { "oldface", IMPL_FONT_ATTR_OTHERSTYLE },
629 { "old", IMPL_FONT_ATTR_OTHERSTYLE },
630 { "new", 0 },
631 { "modern", 0 },
632 { "lucida", 0 },
633 { "regular", 0 },
634 { "extended", 0 },
635 { "extra", IMPL_FONT_ATTR_OTHERSTYLE },
636 { "ext", 0 },
637 { "scalable", 0 },
638 { "scale", 0 },
639 { "nimbus", 0 },
640 { "adobe", 0 },
641 { "itc", 0 },
642 { "amt", 0 },
643 { "mt", 0 },
644 { "ms", 0 },
645 { "cpi", 0 },
646 { "no", 0 },
647 { NULL, 0 },
650 // -----------------------------------------------------------------------
652 static bool ImplKillLeading( String& rName, const char* const* ppStr )
654 for(; *ppStr; ++ppStr )
656 const char* pStr = *ppStr;
657 const sal_Unicode* pNameStr = rName.GetBuffer();
658 while ( (*pNameStr == (sal_Unicode)(unsigned char)*pStr) && *pStr )
660 pNameStr++;
661 pStr++;
663 if ( !*pStr )
665 xub_StrLen nLen = sal::static_int_cast<xub_StrLen>(pNameStr - rName.GetBuffer());
666 rName.Erase( 0, nLen );
667 return true;
671 // special case for Baekmuk
672 // TODO: allow non-ASCII KillLeading list
673 const sal_Unicode* pNameStr = rName.GetBuffer();
674 if( (pNameStr[0]==0xBC31) && (pNameStr[1]==0xBC35) )
676 xub_StrLen nLen = (pNameStr[2]==0x0020) ? 3 : 2;
677 rName.Erase( 0, nLen );
678 return true;
681 return false;
684 // -----------------------------------------------------------------------
686 static xub_StrLen ImplIsTrailing( const String& rName, const char* pStr )
688 xub_StrLen nStrLen = static_cast<xub_StrLen>( strlen( pStr ) );
689 if( nStrLen >= rName.Len() )
690 return 0;
692 const sal_Unicode* pEndName = rName.GetBuffer() + rName.Len();
693 const sal_Unicode* pNameStr = pEndName - nStrLen;
694 do if( *(pNameStr++) != *(pStr++) )
695 return 0;
696 while( *pStr );
698 return nStrLen;
701 // -----------------------------------------------------------------------
703 static bool ImplKillTrailing( String& rName, const char* const* ppStr )
705 for(; *ppStr; ++ppStr )
707 xub_StrLen nTrailLen = ImplIsTrailing( rName, *ppStr );
708 if( nTrailLen )
710 rName.Erase( rName.Len()-nTrailLen );
711 return true;
715 return false;
718 // -----------------------------------------------------------------------
720 static bool ImplKillTrailingWithExceptions( String& rName, const char* const* ppStr )
722 for(; *ppStr; ++ppStr )
724 xub_StrLen nTrailLen = ImplIsTrailing( rName, *ppStr );
725 if( nTrailLen )
727 // check string match against string exceptions
728 while( *++ppStr )
729 if( ImplIsTrailing( rName, *ppStr ) )
730 return false;
732 rName.Erase( rName.Len()-nTrailLen );
733 return true;
735 else
737 // skip exception strings
738 while( *++ppStr ) ;
742 return false;
745 // -----------------------------------------------------------------------
747 static sal_Bool ImplFindAndErase( String& rName, const char* pStr )
749 xub_StrLen nPos = rName.SearchAscii( pStr );
750 if ( nPos == STRING_NOTFOUND )
751 return sal_False;
753 const char* pTempStr = pStr;
754 while ( *pTempStr )
755 pTempStr++;
756 rName.Erase( nPos, (xub_StrLen)(pTempStr-pStr) );
757 return sal_True;
760 // =======================================================================
762 void FontSubstConfiguration::getMapName( const String& rOrgName, String& rShortName,
763 String& rFamilyName, FontWeight& rWeight, FontWidth& rWidth, sal_uLong& rType )
765 rShortName = rOrgName;
767 // TODO: get rid of the crazy O(N*strlen) searches below
768 // they should be possible in O(strlen)
770 // Kill leading vendor names and other unimportant data
771 ImplKillLeading( rShortName, aImplKillLeadingList );
773 // Kill trailing vendor names and other unimportant data
774 ImplKillTrailing( rShortName, aImplKillTrailingList );
775 ImplKillTrailingWithExceptions( rShortName, aImplKillTrailingWithExceptionsList );
777 rFamilyName = rShortName;
779 // Kill attributes from the name and update the data
780 // Weight
781 const ImplFontAttrWeightSearchData* pWeightList = aImplWeightAttrSearchList;
782 while ( pWeightList->mpStr )
784 if ( ImplFindAndErase( rFamilyName, pWeightList->mpStr ) )
786 if ( (rWeight == WEIGHT_DONTKNOW) || (rWeight == WEIGHT_NORMAL) )
787 rWeight = pWeightList->meWeight;
788 break;
790 pWeightList++;
793 // Width
794 const ImplFontAttrWidthSearchData* pWidthList = aImplWidthAttrSearchList;
795 while ( pWidthList->mpStr )
797 if ( ImplFindAndErase( rFamilyName, pWidthList->mpStr ) )
799 if ( (rWidth == WIDTH_DONTKNOW) || (rWidth == WIDTH_NORMAL) )
800 rWidth = pWidthList->meWidth;
801 break;
803 pWidthList++;
806 // Type
807 rType = 0;
808 const ImplFontAttrTypeSearchData* pTypeList = aImplTypeAttrSearchList;
809 while ( pTypeList->mpStr )
811 if ( ImplFindAndErase( rFamilyName, pTypeList->mpStr ) )
812 rType |= pTypeList->mnType;
813 pTypeList++;
816 // Remove numbers
817 // TODO: also remove localized and fullwidth digits
818 xub_StrLen i = 0;
819 while ( i < rFamilyName.Len() )
821 sal_Unicode c = rFamilyName.GetChar( i );
822 if ( (c >= 0x0030) && (c <= 0x0039) )
823 rFamilyName.Erase( i, 1 );
824 else
825 i++;
830 struct StrictStringSort : public ::std::binary_function< const FontNameAttr&, const FontNameAttr&, bool >
832 bool operator()( const FontNameAttr& rLeft, const FontNameAttr& rRight )
833 { return rLeft.Name.CompareTo( rRight.Name ) == COMPARE_LESS ; }
836 static const char* const pAttribNames[] =
838 "default",
839 "standard",
840 "normal",
841 "symbol",
842 "fixed",
843 "sansserif",
844 "serif",
845 "decorative",
846 "special",
847 "italic",
848 "title",
849 "capitals",
850 "cjk",
851 "cjk_jp",
852 "cjk_sc",
853 "cjk_tc",
854 "cjk_kr",
855 "ctl",
856 "nonelatin",
857 "full",
858 "outline",
859 "shadow",
860 "rounded",
861 "typewriter",
862 "script",
863 "handwriting",
864 "chancery",
865 "comic",
866 "brushscript",
867 "gothic",
868 "schoolbook",
869 "other"
872 struct enum_convert
874 const char* pName;
875 int nEnum;
879 static const enum_convert pWeightNames[] =
881 { "normal", WEIGHT_NORMAL },
882 { "medium", WEIGHT_MEDIUM },
883 { "bold", WEIGHT_BOLD },
884 { "black", WEIGHT_BLACK },
885 { "semibold", WEIGHT_SEMIBOLD },
886 { "light", WEIGHT_LIGHT },
887 { "semilight", WEIGHT_SEMILIGHT },
888 { "ultrabold", WEIGHT_ULTRABOLD },
889 { "semi", WEIGHT_SEMIBOLD },
890 { "demi", WEIGHT_SEMIBOLD },
891 { "heavy", WEIGHT_BLACK },
892 { "unknown", WEIGHT_DONTKNOW },
893 { "thin", WEIGHT_THIN },
894 { "ultralight", WEIGHT_ULTRALIGHT }
897 static const enum_convert pWidthNames[] =
899 { "normal", WIDTH_NORMAL },
900 { "condensed", WIDTH_CONDENSED },
901 { "expanded", WIDTH_EXPANDED },
902 { "unknown", WIDTH_DONTKNOW },
903 { "ultracondensed", WIDTH_ULTRA_CONDENSED },
904 { "extracondensed", WIDTH_EXTRA_CONDENSED },
905 { "semicondensed", WIDTH_SEMI_CONDENSED },
906 { "semiexpanded", WIDTH_SEMI_EXPANDED },
907 { "extraexpanded", WIDTH_EXTRA_EXPANDED },
908 { "ultraexpanded", WIDTH_ULTRA_EXPANDED }
911 void FontSubstConfiguration::fillSubstVector( const com::sun::star::uno::Reference< XNameAccess > xFont,
912 const OUString& rType,
913 std::vector< String >& rSubstVector ) const
917 Any aAny = xFont->getByName( rType );
918 if( aAny.getValueTypeClass() == TypeClass_STRING )
920 const OUString* pLine = (const OUString*)aAny.getValue();
921 sal_Int32 nLength = pLine->getLength();
922 if( nLength )
924 const sal_Unicode* pStr = pLine->getStr();
925 sal_Int32 nTokens = 0;
926 // count tokens
927 while( nLength-- )
929 if( *pStr++ == sal_Unicode(';') )
930 nTokens++;
932 rSubstVector.clear();
933 // optimize performance, heap fragmentation
934 rSubstVector.reserve( nTokens );
935 sal_Int32 nIndex = 0;
936 while( nIndex != -1 )
938 OUString aSubst( pLine->getToken( 0, ';', nIndex ) );
939 if( !aSubst.isEmpty() )
941 UniqueSubstHash::iterator aEntry = maSubstHash.find( aSubst );
942 if (aEntry != maSubstHash.end())
943 aSubst = *aEntry;
944 else
945 maSubstHash.insert( aSubst );
946 rSubstVector.push_back( aSubst );
952 catch (const NoSuchElementException&)
955 catch (const WrappedTargetException&)
960 FontWeight FontSubstConfiguration::getSubstWeight( const com::sun::star::uno::Reference< XNameAccess > xFont,
961 const OUString& rType ) const
963 int weight = -1;
966 Any aAny = xFont->getByName( rType );
967 if( aAny.getValueTypeClass() == TypeClass_STRING )
969 const OUString* pLine = (const OUString*)aAny.getValue();
970 if( !pLine->isEmpty() )
972 for( weight=SAL_N_ELEMENTS(pWeightNames)-1; weight >= 0; weight-- )
973 if( pLine->equalsIgnoreAsciiCaseAscii( pWeightNames[weight].pName ) )
974 break;
976 #if OSL_DEBUG_LEVEL > 1
977 if( weight < 0 )
978 fprintf( stderr, "Error: invalid weight %s\n",
979 OUStringToOString( *pLine, RTL_TEXTENCODING_ASCII_US ).getStr() );
980 #endif
983 catch (const NoSuchElementException&)
986 catch (const WrappedTargetException&)
989 return (FontWeight)( weight >= 0 ? pWeightNames[weight].nEnum : WEIGHT_DONTKNOW );
992 FontWidth FontSubstConfiguration::getSubstWidth( const com::sun::star::uno::Reference< XNameAccess > xFont,
993 const OUString& rType ) const
995 int width = -1;
998 Any aAny = xFont->getByName( rType );
999 if( aAny.getValueTypeClass() == TypeClass_STRING )
1001 const OUString* pLine = (const OUString*)aAny.getValue();
1002 if( !pLine->isEmpty() )
1004 for( width=SAL_N_ELEMENTS(pWidthNames)-1; width >= 0; width-- )
1005 if( pLine->equalsIgnoreAsciiCaseAscii( pWidthNames[width].pName ) )
1006 break;
1008 #if OSL_DEBUG_LEVEL > 1
1009 if( width < 0 )
1010 fprintf( stderr, "Error: invalid width %s\n",
1011 OUStringToOString( *pLine, RTL_TEXTENCODING_ASCII_US ).getStr() );
1012 #endif
1015 catch (const NoSuchElementException&)
1018 catch (const WrappedTargetException&)
1021 return (FontWidth)( width >= 0 ? pWidthNames[width].nEnum : WIDTH_DONTKNOW );
1024 unsigned long FontSubstConfiguration::getSubstType( const com::sun::star::uno::Reference< XNameAccess > xFont,
1025 const OUString& rType ) const
1027 unsigned long type = 0;
1030 Any aAny = xFont->getByName( rType );
1031 if( aAny.getValueTypeClass() == TypeClass_STRING )
1033 const OUString* pLine = (const OUString*)aAny.getValue();
1034 if( !pLine->isEmpty() )
1036 sal_Int32 nIndex = 0;
1037 while( nIndex != -1 )
1039 String aToken( pLine->getToken( 0, ',', nIndex ) );
1040 for( int k = 0; k < 32; k++ )
1041 if( aToken.EqualsIgnoreCaseAscii( pAttribNames[k] ) )
1043 type |= 1 << k;
1044 break;
1050 catch (const NoSuchElementException&)
1053 catch (const WrappedTargetException&)
1057 return type;
1060 void FontSubstConfiguration::readLocaleSubst( const com::sun::star::lang::Locale& rLocale ) const
1062 boost::unordered_map< Locale, LocaleSubst, LocaleHash >::const_iterator it =
1063 m_aSubst.find( rLocale );
1064 if( it != m_aSubst.end() )
1066 if( ! it->second.bConfigRead )
1068 it->second.bConfigRead = true;
1069 Reference< XNameAccess > xNode;
1072 Any aAny = m_xConfigAccess->getByName( it->second.aConfigLocaleString );
1073 aAny >>= xNode;
1075 catch (const NoSuchElementException&)
1078 catch (const WrappedTargetException&)
1081 if( xNode.is() )
1083 Sequence< OUString > aFonts = xNode->getElementNames();
1084 int nFonts = aFonts.getLength();
1085 const OUString* pFontNames = aFonts.getConstArray();
1086 // improve performance, heap fragmentation
1087 it->second.aSubstAttributes.reserve( nFonts );
1089 // strings for subst retrieval, construct only once
1090 OUString aSubstFontsStr ( "SubstFonts" );
1091 OUString aSubstFontsMSStr ( "SubstFontsMS" );
1092 OUString aSubstFontsPSStr ( "SubstFontsPS" );
1093 OUString aSubstFontsHTMLStr ( "SubstFontsHTML" );
1094 OUString aSubstWeightStr ( "FontWeight" );
1095 OUString aSubstWidthStr ( "FontWidth" );
1096 OUString aSubstTypeStr ( "FontType" );
1097 for( int i = 0; i < nFonts; i++ )
1099 Reference< XNameAccess > xFont;
1102 Any aAny = xNode->getByName( pFontNames[i] );
1103 aAny >>= xFont;
1105 catch (const NoSuchElementException&)
1108 catch (const WrappedTargetException&)
1111 if( ! xFont.is() )
1113 #if OSL_DEBUG_LEVEL > 1
1114 fprintf( stderr, "did not get font attributes for %s\n",
1115 OUStringToOString( pFontNames[i], RTL_TEXTENCODING_UTF8 ).getStr() );
1116 #endif
1117 continue;
1120 FontNameAttr aAttr;
1121 // read subst attributes from config
1122 aAttr.Name = pFontNames[i];
1123 fillSubstVector( xFont, aSubstFontsStr, aAttr.Substitutions );
1124 fillSubstVector( xFont, aSubstFontsMSStr, aAttr.MSSubstitutions );
1125 fillSubstVector( xFont, aSubstFontsPSStr, aAttr.PSSubstitutions );
1126 fillSubstVector( xFont, aSubstFontsHTMLStr, aAttr.HTMLSubstitutions );
1127 aAttr.Weight = getSubstWeight( xFont, aSubstWeightStr );
1128 aAttr.Width = getSubstWidth( xFont, aSubstWidthStr );
1129 aAttr.Type = getSubstType( xFont, aSubstTypeStr );
1131 // finally insert this entry
1132 it->second.aSubstAttributes.push_back( aAttr );
1134 std::sort( it->second.aSubstAttributes.begin(), it->second.aSubstAttributes.end(), StrictStringSort() );
1140 const FontNameAttr* FontSubstConfiguration::getSubstInfo( const String& rFontName, const Locale& rLocale ) const
1142 if( !rFontName.Len() )
1143 return NULL;
1145 // search if a (language dep.) replacement table for the given font exists
1146 // fallback is english
1147 String aSearchFont( rFontName );
1148 aSearchFont.ToLowerAscii();
1149 FontNameAttr aSearchAttr;
1150 aSearchAttr.Name = aSearchFont;
1152 Locale aLocale;
1153 aLocale.Language = rLocale.Language.toAsciiLowerCase();
1154 aLocale.Country = rLocale.Country.toAsciiUpperCase();
1155 aLocale.Variant = rLocale.Variant.toAsciiUpperCase();
1157 if( aLocale.Language.isEmpty() )
1158 aLocale = SvtSysLocale().GetUILanguageTag().getLocale();
1160 while( !aLocale.Language.isEmpty() )
1162 boost::unordered_map< Locale, LocaleSubst, LocaleHash >::const_iterator lang = m_aSubst.find( aLocale );
1163 if( lang != m_aSubst.end() )
1165 if( ! lang->second.bConfigRead )
1166 readLocaleSubst( aLocale );
1167 // try to find an exact match
1168 // because the list is sorted this will also find fontnames of the form searchfontname*
1169 std::vector< FontNameAttr >::const_iterator it = ::std::lower_bound( lang->second.aSubstAttributes.begin(), lang->second.aSubstAttributes.end(), aSearchAttr, StrictStringSort() );
1170 if( it != lang->second.aSubstAttributes.end())
1172 const FontNameAttr& rFoundAttr = *it;
1173 // a search for "abcblack" may match with an entry for "abc"
1174 // the reverse is not a good idea (e.g. #i112731# alba->albani)
1175 if( rFoundAttr.Name.Len() <= aSearchFont.Len() )
1176 if( aSearchFont.CompareTo( rFoundAttr.Name, rFoundAttr.Name.Len() ) == COMPARE_EQUAL )
1177 return &rFoundAttr;
1180 // gradually become more unspecific
1181 if( !aLocale.Variant.isEmpty() )
1182 aLocale.Variant = OUString();
1183 else if( !aLocale.Country.isEmpty() )
1184 aLocale.Country = OUString();
1185 else if( aLocale.Language != "en" )
1186 aLocale.Language = OUString( "en" );
1187 else
1188 aLocale.Language = OUString();
1190 return NULL;
1193 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */