Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / generic / fontmanager / fontconfig.cxx
blobfe978cdc24ad8c0449d37311020db0c036a67c11
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 "fontcache.hxx"
21 #include "impfont.hxx"
22 #include "fontmanager.hxx"
23 #include <vcl/svapp.hxx>
24 #include <vcl/sysdata.hxx>
25 #include <vcl/vclenum.hxx>
26 #include <vcl/wrkwin.hxx>
27 #include "outfont.hxx"
28 #include <i18nlangtag/languagetag.hxx>
29 #include <i18nutil/unicode.hxx>
30 #include <rtl/strbuf.hxx>
31 #include <unicode/uchar.h>
32 #include <unicode/uscript.h>
34 using namespace psp;
36 #include <fontconfig/fontconfig.h>
37 #include <ft2build.h>
38 #include <fontconfig/fcfreetype.h>
40 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
41 #include <dbus/dbus-glib.h>
42 #endif
44 #include <cstdio>
45 #include <cstdarg>
47 #include "unotools/atom.hxx"
49 #include "osl/module.h"
50 #include "osl/thread.h"
51 #include "osl/process.h"
53 #include "rtl/ustrbuf.hxx"
55 #include "sal/alloca.h"
57 #include <utility>
58 #include <algorithm>
60 using namespace osl;
62 namespace
64 typedef std::pair<FcChar8*, FcChar8*> lang_and_element;
67 class FontCfgWrapper
69 FcFontSet* m_pOutlineSet;
71 void addFontSet( FcSetName );
73 FontCfgWrapper();
74 ~FontCfgWrapper();
76 public:
77 static FontCfgWrapper& get();
78 static void release();
80 FcFontSet* getFontSet();
82 void clear();
84 public:
85 FcResult LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **family,
86 const char *elementtype, const char *elementlangtype);
87 //to-do, make private and add some cleanish accessor methods
88 std::unordered_map< OString, OString, OStringHash > m_aFontNameToLocalized;
89 std::unordered_map< OString, OString, OStringHash > m_aLocalizedToCanonical;
90 private:
91 void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);
93 LanguageTag* m_pLanguageTag;
96 FontCfgWrapper::FontCfgWrapper()
98 m_pOutlineSet( NULL ),
99 m_pLanguageTag( NULL )
101 FcInit();
104 void FontCfgWrapper::addFontSet( FcSetName eSetName )
107 add only acceptable outlined fonts to our config,
108 for future fontconfig use
110 FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
111 if( !pOrig )
112 return;
114 // filter the font sets to remove obsolete faces
115 for( int i = 0; i < pOrig->nfont; ++i )
117 FcPattern* pPattern = pOrig->fonts[i];
118 // #i115131# ignore non-outline fonts
119 FcBool bOutline = FcFalse;
120 FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
121 if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
122 continue;
123 FcPatternReference( pPattern );
124 FcFontSetAdd( m_pOutlineSet, pPattern );
127 // TODO?: FcFontSetDestroy( pOrig );
130 namespace
132 int compareFontNames(const FcPattern *a, const FcPattern *b)
134 FcChar8 *pNameA=NULL, *pNameB=NULL;
136 bool nHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
137 bool nHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
139 if (nHaveA && nHaveB)
140 return strcmp(reinterpret_cast<const char*>(pNameA), reinterpret_cast<const char*>(pNameB));
142 return int(nHaveA) - int(nHaveB);
145 //Sort fonts so that fonts with the same family name are side-by-side, with
146 //those with higher version numbers first
147 class SortFont : public ::std::binary_function< const FcPattern*, const FcPattern*, bool >
149 public:
150 bool operator()(const FcPattern *a, const FcPattern *b)
152 int comp = compareFontNames(a, b);
153 if (comp != 0)
154 return comp < 0;
156 int nVersionA=0, nVersionB=0;
158 bool nHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
159 bool nHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;
161 if (nHaveA && nHaveB)
162 return nVersionA > nVersionB;
164 return nHaveA > nHaveB;
168 //See fdo#30729 for where an old opensymbol installed system-wide can
169 //clobber the new opensymbol installed locally
171 //See if this font is a duplicate with equal attributes which has already been
172 //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet
173 //on being sorted with SortFont
174 bool isPreviouslyDuplicateOrObsoleted(FcFontSet *pFSet, int i)
176 const FcPattern *a = pFSet->fonts[i];
178 FcPattern* pTestPatternA = FcPatternDuplicate(a);
179 FcPatternDel(pTestPatternA, FC_FILE);
180 FcPatternDel(pTestPatternA, FC_CHARSET);
181 FcPatternDel(pTestPatternA, FC_CAPABILITY);
182 FcPatternDel(pTestPatternA, FC_FONTVERSION);
183 FcPatternDel(pTestPatternA, FC_LANG);
185 bool bIsDup(false);
187 // fdo#66715: loop for case of several font files for same font
188 for (int j = i - 1; 0 <= j && !bIsDup; --j)
190 const FcPattern *b = pFSet->fonts[j];
192 if (compareFontNames(a, b) != 0)
193 break;
195 FcPattern* pTestPatternB = FcPatternDuplicate(b);
196 FcPatternDel(pTestPatternB, FC_FILE);
197 FcPatternDel(pTestPatternB, FC_CHARSET);
198 FcPatternDel(pTestPatternB, FC_CAPABILITY);
199 FcPatternDel(pTestPatternB, FC_FONTVERSION);
200 FcPatternDel(pTestPatternB, FC_LANG);
202 bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);
204 FcPatternDestroy(pTestPatternB);
207 FcPatternDestroy(pTestPatternA);
209 return bIsDup;
213 FcFontSet* FontCfgWrapper::getFontSet()
215 if( !m_pOutlineSet )
217 m_pOutlineSet = FcFontSetCreate();
218 addFontSet( FcSetSystem );
219 if( FcGetVersion() > 20400 ) // #i85462# prevent crashes
220 addFontSet( FcSetApplication );
222 ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont());
225 return m_pOutlineSet;
228 FontCfgWrapper::~FontCfgWrapper()
230 clear();
231 //To-Do: get gtk vclplug smoketest to pass
232 //FcFini();
235 static FontCfgWrapper* pOneInstance = NULL;
237 FontCfgWrapper& FontCfgWrapper::get()
239 if( ! pOneInstance )
240 pOneInstance = new FontCfgWrapper();
241 return *pOneInstance;
244 void FontCfgWrapper::release()
246 if( pOneInstance )
248 delete pOneInstance;
249 pOneInstance = NULL;
253 namespace
255 static FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);
257 FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
259 FcChar8* candidate = elements.begin()->second;
260 /* FIXME-BCP47: once fontconfig supports language tags this
261 * language-territory stuff needs to be changed! */
262 SAL_INFO_IF( !rLangTag.isIsoLocale(), "i18n", "localizedsorter::bestname - not an ISO locale");
263 OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
264 OString sFullMatch = sLangMatch;
265 sFullMatch += OString('-');
266 sFullMatch += OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
268 std::vector<lang_and_element>::const_iterator aEnd = elements.end();
269 bool alreadyclosematch = false;
270 bool found_fallback_englishname = false;
271 for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter )
273 const char *pLang = reinterpret_cast<const char*>(aIter->first);
274 if( rtl_str_compare( pLang, sFullMatch.getStr() ) == 0)
276 // both language and country match
277 candidate = aIter->second;
278 break;
280 else if( alreadyclosematch )
282 // current candidate matches lang of lang-TERRITORY
283 // override candidate only if there is a full match
284 continue;
286 else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0)
288 // just the language matches
289 candidate = aIter->second;
290 alreadyclosematch = true;
292 else if( found_fallback_englishname )
294 // already found an english fallback, don't override candidate
295 // unless there is a better language match
296 continue;
298 else if( rtl_str_compare( pLang, "en") == 0)
300 // select a fallback candidate of the first english element
301 // name
302 candidate = aIter->second;
303 found_fallback_englishname = true;
306 return candidate;
310 //Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa
311 void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
312 const std::vector< lang_and_element > &lang_and_elements)
314 std::vector<lang_and_element>::const_iterator aEnd = lang_and_elements.end();
315 for (std::vector<lang_and_element>::const_iterator aIter = lang_and_elements.begin(); aIter != aEnd; ++aIter)
317 const char *candidate = reinterpret_cast<const char*>(aIter->second);
318 if (rtl_str_compare(candidate, reinterpret_cast<const char*>(bestfontname)) != 0)
319 m_aFontNameToLocalized[OString(candidate)] = OString(reinterpret_cast<const char*>(bestfontname));
321 if (rtl_str_compare(reinterpret_cast<const char*>(origfontname), reinterpret_cast<const char*>(bestfontname)) != 0)
322 m_aLocalizedToCanonical[OString(reinterpret_cast<const char*>(bestfontname))] = OString(reinterpret_cast<const char*>(origfontname));
325 FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **element,
326 const char *elementtype, const char *elementlangtype)
327 { /* e. g.: ^ FC_FAMILY ^ FC_FAMILYLANG */
328 FcChar8 *origelement;
329 FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
330 *element = origelement;
332 if( eElementRes == FcResultMatch)
334 FcChar8* elementlang = NULL;
335 if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
337 std::vector< lang_and_element > lang_and_elements;
338 lang_and_elements.push_back(lang_and_element(elementlang, *element));
339 int k = 1;
340 while (true)
342 if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
343 break;
344 if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
345 break;
346 lang_and_elements.push_back(lang_and_element(elementlang, *element));
347 ++k;
350 //possible to-do, sort by UILocale instead of process locale
351 if (!m_pLanguageTag)
353 rtl_Locale* pLoc = NULL;
354 osl_getProcessLocale(&pLoc);
355 m_pLanguageTag = new LanguageTag(*pLoc);
357 *element = bestname(lang_and_elements, *m_pLanguageTag);
359 //if this element is a fontname, map the other names to this best-name
360 if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
361 cacheLocalizedFontNames(origelement, *element, lang_and_elements);
365 return eElementRes;
368 void FontCfgWrapper::clear()
370 m_aFontNameToLocalized.clear();
371 m_aLocalizedToCanonical.clear();
372 if( m_pOutlineSet )
374 FcFontSetDestroy( m_pOutlineSet );
375 m_pOutlineSet = NULL;
377 delete m_pLanguageTag;
378 m_pLanguageTag = NULL;
382 * PrintFontManager::initFontconfig
384 void PrintFontManager::initFontconfig()
386 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
387 rWrapper.clear();
390 namespace
392 FontWeight convertWeight(int weight)
394 // set weight
395 if( weight <= FC_WEIGHT_THIN )
396 return WEIGHT_THIN;
397 else if( weight <= FC_WEIGHT_ULTRALIGHT )
398 return WEIGHT_ULTRALIGHT;
399 else if( weight <= FC_WEIGHT_LIGHT )
400 return WEIGHT_LIGHT;
401 else if( weight <= FC_WEIGHT_BOOK )
402 return WEIGHT_SEMILIGHT;
403 else if( weight <= FC_WEIGHT_NORMAL )
404 return WEIGHT_NORMAL;
405 else if( weight <= FC_WEIGHT_MEDIUM )
406 return WEIGHT_MEDIUM;
407 else if( weight <= FC_WEIGHT_SEMIBOLD )
408 return WEIGHT_SEMIBOLD;
409 else if( weight <= FC_WEIGHT_BOLD )
410 return WEIGHT_BOLD;
411 else if( weight <= FC_WEIGHT_ULTRABOLD )
412 return WEIGHT_ULTRABOLD;
413 return WEIGHT_BLACK;
416 FontItalic convertSlant(int slant)
418 // set italic
419 if( slant == FC_SLANT_ITALIC )
420 return ITALIC_NORMAL;
421 else if( slant == FC_SLANT_OBLIQUE )
422 return ITALIC_OBLIQUE;
423 return ITALIC_NONE;
426 FontPitch convertSpacing(int spacing)
428 // set pitch
429 if( spacing == FC_MONO || spacing == FC_CHARCELL )
430 return PITCH_FIXED;
431 return PITCH_VARIABLE;
434 // translation: fontconfig enum -> vcl enum
435 FontWidth convertWidth(int width)
437 if (width == FC_WIDTH_ULTRACONDENSED)
438 return WIDTH_ULTRA_CONDENSED;
439 else if (width == FC_WIDTH_EXTRACONDENSED)
440 return WIDTH_EXTRA_CONDENSED;
441 else if (width == FC_WIDTH_CONDENSED)
442 return WIDTH_CONDENSED;
443 else if (width == FC_WIDTH_SEMICONDENSED)
444 return WIDTH_SEMI_CONDENSED;
445 else if (width == FC_WIDTH_SEMIEXPANDED)
446 return WIDTH_SEMI_EXPANDED;
447 else if (width == FC_WIDTH_EXPANDED)
448 return WIDTH_EXPANDED;
449 else if (width == FC_WIDTH_EXTRAEXPANDED)
450 return WIDTH_EXTRA_EXPANDED;
451 else if (width == FC_WIDTH_ULTRAEXPANDED)
452 return WIDTH_ULTRA_EXPANDED;
453 return WIDTH_NORMAL;
457 //FontConfig doesn't come with a way to remove an element from a FontSet as far
458 //as I can see
459 static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
461 FcPatternDestroy(pFSet->fonts[i]);
463 int nTail = pFSet->nfont - (i + 1);
464 --pFSet->nfont;
465 if (!nTail)
466 return;
467 memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
470 void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int, OStringHash>& o_rVisitedPaths )
472 #if OSL_DEBUG_LEVEL > 1
473 int nFonts = 0;
474 #endif
475 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
477 FcFontSet* pFSet = rWrapper.getFontSet();
478 if( pFSet )
480 #if OSL_DEBUG_LEVEL > 1
481 fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont );
482 #endif
483 for( int i = 0; i < pFSet->nfont; i++ )
485 FcChar8* file = NULL;
486 FcChar8* family = NULL;
487 FcChar8* style = NULL;
488 FcChar8* format = NULL;
489 int slant = 0;
490 int weight = 0;
491 int width = 0;
492 int spacing = 0;
493 int nCollectionEntry = -1;
494 FcBool outline = false;
496 FcResult eFileRes = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
497 FcResult eFamilyRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
498 FcResult eStyleRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
499 FcResult eSlantRes = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
500 FcResult eWeightRes = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
501 FcResult eWidthRes = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
502 FcResult eSpacRes = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
503 FcResult eOutRes = FcPatternGetBool(pFSet->fonts[i], FC_OUTLINE, 0, &outline);
504 FcResult eIndexRes = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry);
505 FcResult eFormatRes = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
507 if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch )
508 continue;
510 #if (OSL_DEBUG_LEVEL > 2)
511 fprintf( stderr, "found font \"%s\" in file %s\n"
512 " weight = %d, slant = %d, style = \"%s\"\n"
513 " width = %d, spacing = %d, outline = %d, format %s\n"
514 , family, file
515 , eWeightRes == FcResultMatch ? weight : -1
516 , eSpacRes == FcResultMatch ? slant : -1
517 , eStyleRes == FcResultMatch ? (const char*) style : "<nil>"
518 , eWeightRes == FcResultMatch ? width : -1
519 , eSpacRes == FcResultMatch ? spacing : -1
520 , eOutRes == FcResultMatch ? outline : -1
521 , eFormatRes == FcResultMatch ? (const char*)format : "<unknown>"
523 #endif
525 // OSL_ASSERT(eOutRes != FcResultMatch || outline);
527 // only outline fonts are usable to psprint anyway
528 if( eOutRes == FcResultMatch && ! outline )
529 continue;
531 if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
533 #if OSL_DEBUG_LEVEL > 2
534 fprintf(stderr, "Ditching %s as duplicate/obsolete\n", file);
535 #endif
536 continue;
539 // see if this font is already cached
540 // update attributes
541 std::list< PrintFont* > aFonts;
542 OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
543 splitPath( aOrgPath, aDir, aBase );
545 o_rVisitedPaths[aDir] = 1;
547 int nDirID = getDirectoryAtom( aDir, true );
548 if( ! m_pFontCache->getFontCacheFile( nDirID, aBase, aFonts ) )
550 #if OSL_DEBUG_LEVEL > 2
551 fprintf( stderr, "file %s not cached\n", aBase.getStr() );
552 #endif
553 // not known, analyze font file to get attributes
554 // not described by fontconfig (e.g. alias names, PSName)
555 if (eFormatRes != FcResultMatch)
556 format = NULL;
557 analyzeFontFile( nDirID, aBase, aFonts, reinterpret_cast<char*>(format) );
558 #if OSL_DEBUG_LEVEL > 1
559 if( aFonts.empty() )
560 fprintf( stderr, "Warning: file \"%s\" is unusable to psprint\n", aOrgPath.getStr() );
561 #endif
563 if( aFonts.empty() )
565 //remove font, reuse index
566 //we want to remove unusable fonts here, in case there is a usable font
567 //which duplicates the properties of the unusable one
569 //not removing the unusable font will risk the usable font being rejected
570 //as a duplicate by isPreviouslyDuplicateOrObsoleted
571 lcl_FcFontSetRemove(pFSet, i--);
572 continue;
575 int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( reinterpret_cast<char*>(family) ), RTL_TEXTENCODING_UTF8 ), true );
576 PrintFont* pUpdate = aFonts.front();
577 std::list<PrintFont*>::const_iterator second_font = aFonts.begin();
578 ++second_font;
579 if( second_font != aFonts.end() ) // more than one font
581 // a collection entry, get the correct index
582 if( eIndexRes == FcResultMatch && nCollectionEntry != -1 )
584 for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
586 if( (*it)->m_eType == fonttype::TrueType &&
587 static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry )
589 pUpdate = *it;
590 break;
593 // update collection entry
594 // additional entries will be created in the cache
595 // if this is a new index (that is if the loop above
596 // ran to the end of the list)
597 if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here
598 static_cast<TrueTypeFontFile*>(pUpdate)->m_nCollectionEntry = nCollectionEntry;
600 else
602 #if OSL_DEBUG_LEVEL > 1
603 fprintf( stderr, "multiple fonts for file, but no index in fontconfig pattern ! (index res = %d collection entry = %d\nfile will not be used\n", eIndexRes, nCollectionEntry );
604 #endif
605 // we have found more than one font in this file
606 // but fontconfig will not tell us which index is meant
607 // -> something is in disorder, do not use this font
608 pUpdate = NULL;
612 if( pUpdate )
614 // set family name
615 if( pUpdate->m_nFamilyName != nFamilyName )
618 if( eWeightRes == FcResultMatch )
619 pUpdate->m_eWeight = convertWeight(weight);
620 if( eWidthRes == FcResultMatch )
621 pUpdate->m_eWidth = convertWidth(width);
622 if( eSpacRes == FcResultMatch )
623 pUpdate->m_ePitch = convertSpacing(spacing);
624 if( eSlantRes == FcResultMatch )
625 pUpdate->m_eItalic = convertSlant(slant);
626 if( eStyleRes == FcResultMatch )
628 pUpdate->m_aStyleName = OStringToOUString( OString( reinterpret_cast<char*>(style) ), RTL_TEXTENCODING_UTF8 );
631 // update font cache
632 m_pFontCache->updateFontCacheEntry( pUpdate, false );
633 // sort into known fonts
634 fontID aFont = m_nNextFontID++;
635 m_aFonts[ aFont ] = pUpdate;
636 m_aFontFileToFontID[ aBase ].insert( aFont );
637 #if OSL_DEBUG_LEVEL > 1
638 nFonts++;
639 #endif
640 #if OSL_DEBUG_LEVEL > 2
641 fprintf( stderr, "inserted font %s as fontID %d\n", family, aFont );
642 #endif
644 // clean up the fonts we did not put into the list
645 for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
647 if( *it != pUpdate )
649 m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item
650 delete *it;
656 // how does one get rid of the config ?
657 #if OSL_DEBUG_LEVEL > 1
658 fprintf( stderr, "inserted %d fonts from fontconfig\n", nFonts );
659 #endif
662 void PrintFontManager::deinitFontconfig()
664 FontCfgWrapper::release();
667 bool PrintFontManager::addFontconfigDir( const OString& rDirName )
669 // workaround for a stability problems in older FC versions
670 // when handling application specific fonts
671 const int nVersion = FcGetVersion();
672 if( nVersion <= 20400 )
673 return false;
674 const char* pDirName = (const char*)rDirName.getStr();
675 bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), reinterpret_cast<FcChar8 const *>(pDirName) ) == FcTrue);
677 #if OSL_DEBUG_LEVEL > 1
678 fprintf( stderr, "FcConfigAppFontAddDir( \"%s\") => %d\n", pDirName, bDirOk );
679 #endif
681 if( !bDirOk )
682 return false;
684 // load dir-specific fc-config file too if available
685 const OString aConfFileName = rDirName + "/fc_local.conf";
686 FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
687 if( pCfgFile )
689 fclose( pCfgFile);
690 bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
691 reinterpret_cast<FcChar8 const *>(aConfFileName.getStr()), FcTrue);
692 if( !bCfgOk )
693 fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk );
696 return true;
699 static void addtopattern(FcPattern *pPattern,
700 FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
702 if( eItalic != ITALIC_DONTKNOW )
704 int nSlant = FC_SLANT_ROMAN;
705 switch( eItalic )
707 case ITALIC_NORMAL:
708 nSlant = FC_SLANT_ITALIC;
709 break;
710 case ITALIC_OBLIQUE:
711 nSlant = FC_SLANT_OBLIQUE;
712 break;
713 default:
714 break;
716 FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
718 if( eWeight != WEIGHT_DONTKNOW )
720 int nWeight = FC_WEIGHT_NORMAL;
721 switch( eWeight )
723 case WEIGHT_THIN: nWeight = FC_WEIGHT_THIN;break;
724 case WEIGHT_ULTRALIGHT: nWeight = FC_WEIGHT_ULTRALIGHT;break;
725 case WEIGHT_LIGHT: nWeight = FC_WEIGHT_LIGHT;break;
726 case WEIGHT_SEMILIGHT: nWeight = FC_WEIGHT_BOOK;break;
727 case WEIGHT_NORMAL: nWeight = FC_WEIGHT_NORMAL;break;
728 case WEIGHT_MEDIUM: nWeight = FC_WEIGHT_MEDIUM;break;
729 case WEIGHT_SEMIBOLD: nWeight = FC_WEIGHT_SEMIBOLD;break;
730 case WEIGHT_BOLD: nWeight = FC_WEIGHT_BOLD;break;
731 case WEIGHT_ULTRABOLD: nWeight = FC_WEIGHT_ULTRABOLD;break;
732 case WEIGHT_BLACK: nWeight = FC_WEIGHT_BLACK;break;
733 default:
734 break;
736 FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
738 if( eWidth != WIDTH_DONTKNOW )
740 int nWidth = FC_WIDTH_NORMAL;
741 switch( eWidth )
743 case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
744 case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break;
745 case WIDTH_CONDENSED: nWidth = FC_WIDTH_CONDENSED;break;
746 case WIDTH_SEMI_CONDENSED: nWidth = FC_WIDTH_SEMICONDENSED;break;
747 case WIDTH_NORMAL: nWidth = FC_WIDTH_NORMAL;break;
748 case WIDTH_SEMI_EXPANDED: nWidth = FC_WIDTH_SEMIEXPANDED;break;
749 case WIDTH_EXPANDED: nWidth = FC_WIDTH_EXPANDED;break;
750 case WIDTH_EXTRA_EXPANDED: nWidth = FC_WIDTH_EXTRAEXPANDED;break;
751 case WIDTH_ULTRA_EXPANDED: nWidth = FC_WIDTH_ULTRAEXPANDED;break;
752 default:
753 break;
755 FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
757 if( ePitch != PITCH_DONTKNOW )
759 int nSpacing = FC_PROPORTIONAL;
760 switch( ePitch )
762 case PITCH_FIXED: nSpacing = FC_MONO;break;
763 case PITCH_VARIABLE: nSpacing = FC_PROPORTIONAL;break;
764 default:
765 break;
767 FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
768 if (nSpacing == FC_MONO)
769 FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>("monospace"));
773 namespace
775 //Someday fontconfig will hopefully use bcp47, see fdo#19869
776 //In the meantime try something that will fit to workaround fdo#35118
777 OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
779 #if defined(FC_VERSION) && (FC_VERSION >= 20492)
780 std::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
781 OString sLangAttrib;
783 sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
784 if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
786 return sLangAttrib;
789 sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
790 if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
792 return sLangAttrib;
795 OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
796 OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
798 if (!sRegion.isEmpty())
800 sLangAttrib = sLang + OString('-') + sRegion;
801 if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
803 return sLangAttrib;
807 if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLang.getStr())))
809 return sLang;
812 return OString();
813 #else
814 OString sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
815 if (sLangAttrib.equalsIgnoreAsciiCase("pa-in"))
816 sLangAttrib = "pa";
817 return sLangAttrib;
818 #endif
821 //returns true if the given code-point couldn't possibly be in rLangTag.
822 bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
824 //a non-default script is set, lets believe it
825 if (rLangTag.hasScript())
826 return false;
828 int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
829 UScriptCode eScript = static_cast<UScriptCode>(script);
830 bool bIsImpossible = false;
831 OUString sLang = rLangTag.getLanguage();
832 switch (eScript)
834 //http://en.wiktionary.org/wiki/Category:Oriya_script_languages
835 case USCRIPT_ORIYA:
836 bIsImpossible =
837 sLang != "or" &&
838 sLang != "kxv";
839 break;
840 //http://en.wiktionary.org/wiki/Category:Telugu_script_languages
841 case USCRIPT_TELUGU:
842 bIsImpossible =
843 sLang != "te" &&
844 sLang != "gon" &&
845 sLang != "kfc";
846 break;
847 //http://en.wiktionary.org/wiki/Category:Bengali_script_languages
848 case USCRIPT_BENGALI:
849 bIsImpossible =
850 sLang != "bn" &&
851 sLang != "as" &&
852 sLang != "bpy" &&
853 sLang != "ctg" &&
854 sLang != "sa";
855 break;
856 default:
857 break;
859 SAL_WARN_IF(bIsImpossible, "vcl", "In glyph fallback throwing away the language property of "
860 << sLang << " because the detected script for '0x"
861 << OUString::number(currentChar, 16)
862 << "' is " << uscript_getName(eScript)
863 << " and that language doesn't make sense. Autodetecting instead.");
864 return bIsImpossible;
867 LanguageTag getExemplarLangTagForCodePoint(sal_uInt32 currentChar)
869 int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
870 UScriptCode eScript = static_cast<UScriptCode>(script);
871 OStringBuffer aBuf(unicode::getExemplarLanguageForUScriptCode(eScript));
872 const char* pScriptCode = uscript_getShortName(eScript);
873 if (pScriptCode)
874 aBuf.append('-').append(pScriptCode);
875 return LanguageTag(OStringToOUString(aBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8));
878 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
879 guint get_xid_for_dbus()
881 const vcl::Window *pTopWindow = Application::IsHeadlessModeEnabled() ? NULL : Application::GetActiveTopWindow();
882 const SystemEnvData* pEnvData = pTopWindow ? pTopWindow->GetSystemData() : NULL;
883 return pEnvData ? pEnvData->aWindow : 0;
885 #endif
888 IMPL_LINK_NOARG_TYPED(PrintFontManager, autoInstallFontLangSupport, Timer *, void)
890 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
891 guint xid = get_xid_for_dbus();
893 if (!xid)
894 return;
896 GError *error = NULL;
897 /* get the DBUS session connection */
898 DBusGConnection *session_connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
899 if (error != NULL)
901 g_debug ("DBUS cannot connect : %s", error->message);
902 g_error_free (error);
903 return;
906 /* get the proxy with gnome-session-manager */
907 DBusGProxy *proxy = dbus_g_proxy_new_for_name(session_connection,
908 "org.freedesktop.PackageKit",
909 "/org/freedesktop/PackageKit",
910 "org.freedesktop.PackageKit.Modify");
911 if (proxy == NULL)
913 g_debug("Could not get DBUS proxy: org.freedesktop.PackageKit");
914 return;
917 gchar **fonts = static_cast<gchar**>(g_malloc((m_aCurrentRequests.size() + 1) * sizeof(gchar*)));
918 gchar **font = fonts;
919 for (std::vector<OString>::const_iterator aI = m_aCurrentRequests.begin(); aI != m_aCurrentRequests.end(); ++aI)
920 *font++ = const_cast<gchar*>(aI->getStr());
921 *font = NULL;
922 gboolean res = dbus_g_proxy_call(proxy, "InstallFontconfigResources", &error,
923 G_TYPE_UINT, xid, /* xid */
924 G_TYPE_STRV, fonts, /* data */
925 G_TYPE_STRING, "hide-finished", /* interaction */
926 G_TYPE_INVALID,
927 G_TYPE_INVALID);
928 /* check the return value */
929 if (!res)
930 g_debug("InstallFontconfigResources method failed");
932 /* check the error value */
933 if (error != NULL)
935 g_debug("InstallFontconfigResources problem : %s", error->message);
936 g_error_free(error);
939 g_free(fonts);
940 g_object_unref(G_OBJECT (proxy));
941 m_aCurrentRequests.clear();
942 #endif
945 bool PrintFontManager::Substitute( FontSelectPattern &rPattern, OUString& rMissingCodes )
947 bool bRet = false;
949 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
951 // build pattern argument for fontconfig query
952 FcPattern* pPattern = FcPatternCreate();
954 // Prefer scalable fonts
955 FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
957 const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
958 const FcChar8* pTargetNameUtf8 = reinterpret_cast<FcChar8 const *>(aTargetName.getStr());
959 FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
961 LanguageTag aLangTag(rPattern.meLanguage);
962 OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
964 // Add required Unicode characters, if any
965 if ( !rMissingCodes.isEmpty() )
967 FcCharSet *unicodes = FcCharSetCreate();
968 for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
970 // also handle unicode surrogates
971 const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
972 FcCharSetAddChar( unicodes, nCode );
973 //if the codepoint is impossible for this lang tag, then clear it
974 //and autodetect something useful
975 if (!aLangAttrib.isEmpty() && isImpossibleCodePointForLang(aLangTag, nCode))
976 aLangAttrib.clear();
977 //#i105784#/rhbz#527719 improve selection of fallback font
978 if (aLangAttrib.isEmpty())
980 aLangTag = getExemplarLangTagForCodePoint(nCode);
981 aLangAttrib = mapToFontConfigLangTag(aLangTag);
984 FcPatternAddCharSet(pPattern, FC_CHARSET, unicodes);
985 FcCharSetDestroy(unicodes);
988 if (!aLangAttrib.isEmpty())
989 FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
991 addtopattern(pPattern, rPattern.GetSlant(), rPattern.GetWeight(),
992 rPattern.GetWidthType(), rPattern.GetPitch());
994 // query fontconfig for a substitute
995 FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
996 FcDefaultSubstitute(pPattern);
998 // process the result of the fontconfig query
999 FcResult eResult = FcResultNoMatch;
1000 FcFontSet* pFontSet = rWrapper.getFontSet();
1001 FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
1002 FcPatternDestroy( pPattern );
1004 FcFontSet* pSet = NULL;
1005 if( pResult )
1007 pSet = FcFontSetCreate();
1008 // info: destroying the pSet destroys pResult implicitly
1009 // since pResult was "added" to pSet
1010 FcFontSetAdd( pSet, pResult );
1013 if( pSet )
1015 if( pSet->nfont > 0 )
1017 //extract the closest match
1018 FcChar8* file = NULL;
1019 FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1020 int nCollectionEntry = 0;
1021 FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
1022 if (eIndexRes != FcResultMatch)
1023 nCollectionEntry = 0;
1024 if( eFileRes == FcResultMatch )
1026 OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
1027 splitPath( aOrgPath, aDir, aBase );
1028 int nDirID = getDirectoryAtom( aDir, true );
1029 fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
1030 if( aFont > 0 )
1032 FastPrintFontInfo aInfo;
1033 bRet = getFontFastInfo( aFont, aInfo );
1034 rPattern.maSearchName = aInfo.m_aFamilyName;
1038 SAL_WARN_IF(!bRet, "vcl", "no FC_FILE found, falling back to name search");
1040 if (!bRet)
1042 FcChar8* family = NULL;
1043 FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
1045 // get the family name
1046 if( eFamilyRes == FcResultMatch )
1048 OString sFamily(reinterpret_cast<char*>(family));
1049 std::unordered_map< OString, OString, OStringHash >::const_iterator aI =
1050 rWrapper.m_aFontNameToLocalized.find(sFamily);
1051 if (aI != rWrapper.m_aFontNameToLocalized.end())
1052 sFamily = aI->second;
1053 rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
1054 bRet = true;
1058 if (bRet)
1060 int val = 0;
1061 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
1062 rPattern.SetWeight( convertWeight(val) );
1063 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
1064 rPattern.SetItalic( convertSlant(val) );
1065 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
1066 rPattern.SetPitch ( convertSpacing(val) );
1067 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
1068 rPattern.SetWidthType ( convertWidth(val) );
1069 FcBool bEmbolden;
1070 if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
1071 rPattern.mbEmbolden = bEmbolden;
1072 FcMatrix *pMatrix = 0;
1073 if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
1075 rPattern.maItalicMatrix.xx = pMatrix->xx;
1076 rPattern.maItalicMatrix.xy = pMatrix->xy;
1077 rPattern.maItalicMatrix.yx = pMatrix->yx;
1078 rPattern.maItalicMatrix.yy = pMatrix->yy;
1082 // update rMissingCodes by removing resolved unicodes
1083 if( !rMissingCodes.isEmpty() )
1085 sal_uInt32* pRemainingCodes = static_cast<sal_uInt32*>(alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) ));
1086 int nRemainingLen = 0;
1087 FcCharSet* unicodes;
1088 if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &unicodes))
1090 for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1092 // also handle unicode surrogates
1093 const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
1094 if (FcCharSetHasChar(unicodes, nCode) != FcTrue)
1095 pRemainingCodes[ nRemainingLen++ ] = nCode;
1098 OUString sStillMissing(pRemainingCodes, nRemainingLen);
1099 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
1100 if (get_xid_for_dbus())
1102 if (sStillMissing == rMissingCodes) //replaced nothing
1104 //It'd be better if we could ask packagekit using the
1105 //missing codepoints or some such rather than using
1106 //"language" as a proxy to how fontconfig considers
1107 //scripts to default to a given language.
1108 for (sal_Int32 i = 0; i < nRemainingLen; ++i)
1110 LanguageTag aOurTag = getExemplarLangTagForCodePoint(pRemainingCodes[i]);
1111 OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8);
1112 if (m_aPreviousLangSupportRequests.find(sTag) != m_aPreviousLangSupportRequests.end())
1113 continue;
1114 m_aPreviousLangSupportRequests.insert(sTag);
1115 sTag = mapToFontConfigLangTag(aOurTag);
1116 if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
1118 OString sReq = OString(":lang=") + sTag;
1119 m_aCurrentRequests.push_back(sReq);
1120 m_aPreviousLangSupportRequests.insert(sTag);
1124 if (!m_aCurrentRequests.empty())
1126 m_aFontInstallerTimer.Stop();
1127 m_aFontInstallerTimer.Start();
1130 #endif
1131 rMissingCodes = sStillMissing;
1135 FcFontSetDestroy( pSet );
1138 return bRet;
1141 class FontConfigFontOptions : public ImplFontOptions
1143 public:
1144 FontConfigFontOptions() : mpPattern(0) {}
1145 virtual ~FontConfigFontOptions()
1147 FcPatternDestroy(mpPattern);
1149 virtual void *GetPattern(void * face, bool bEmbolden, bool /*bVerticalLayout*/) const SAL_OVERRIDE
1151 FcValue value;
1152 value.type = FcTypeFTFace;
1153 value.u.f = face;
1154 FcPatternDel(mpPattern, FC_FT_FACE);
1155 FcPatternAdd (mpPattern, FC_FT_FACE, value, FcTrue);
1156 FcPatternDel(mpPattern, FC_EMBOLDEN);
1157 FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse);
1158 #if 0
1159 FcPatternDel(mpPattern, FC_VERTICAL_LAYOUT);
1160 FcPatternAddBool(mpPattern, FC_VERTICAL_LAYOUT, bVerticalLayout ? FcTrue : FcFalse);
1161 #endif
1162 return mpPattern;
1164 FcPattern* mpPattern;
1167 ImplFontOptions* PrintFontManager::getFontOptions(
1168 const FastPrintFontInfo& rInfo, int nSize, void (*subcallback)(void*))
1170 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1172 FontConfigFontOptions* pOptions = NULL;
1173 FcConfig* pConfig = FcConfigGetCurrent();
1174 FcPattern* pPattern = FcPatternCreate();
1176 OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1178 std::unordered_map< OString, OString, OStringHash >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
1179 if (aI != rWrapper.m_aLocalizedToCanonical.end())
1180 sFamily = aI->second;
1181 if( !sFamily.isEmpty() )
1182 FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(sFamily.getStr()));
1184 addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1185 FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);
1187 FcBool embitmap = true, antialias = true, autohint = true, hinting = true;
1188 int hintstyle = FC_HINT_FULL;
1190 FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1191 if (subcallback)
1192 subcallback(pPattern);
1193 FcDefaultSubstitute(pPattern);
1195 FcResult eResult = FcResultNoMatch;
1196 FcFontSet* pFontSet = rWrapper.getFontSet();
1197 FcPattern* pResult = FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult );
1198 if( pResult )
1200 FcResult eEmbeddedBitmap = FcPatternGetBool(pResult,
1201 FC_EMBEDDED_BITMAP, 0, &embitmap);
1202 FcResult eAntialias = FcPatternGetBool(pResult,
1203 FC_ANTIALIAS, 0, &antialias);
1204 FcResult eAutoHint = FcPatternGetBool(pResult,
1205 FC_AUTOHINT, 0, &autohint);
1206 FcResult eHinting = FcPatternGetBool(pResult,
1207 FC_HINTING, 0, &hinting);
1208 (void) FcPatternGetInteger(pResult,
1209 FC_HINT_STYLE, 0, &hintstyle);
1211 pOptions = new FontConfigFontOptions;
1213 pOptions->mpPattern = pResult;
1215 if( eEmbeddedBitmap == FcResultMatch )
1216 pOptions->meEmbeddedBitmap = embitmap ? EMBEDDEDBITMAP_TRUE : EMBEDDEDBITMAP_FALSE;
1217 if( eAntialias == FcResultMatch )
1218 pOptions->meAntiAlias = antialias ? ANTIALIAS_TRUE : ANTIALIAS_FALSE;
1219 if( eAutoHint == FcResultMatch )
1220 pOptions->meAutoHint = autohint ? AUTOHINT_TRUE : AUTOHINT_FALSE;
1221 if( eHinting == FcResultMatch )
1222 pOptions->meHinting = hinting ? HINTING_TRUE : HINTING_FALSE;
1223 switch (hintstyle)
1225 case FC_HINT_NONE: pOptions->meHintStyle = HINT_NONE; break;
1226 case FC_HINT_SLIGHT: pOptions->meHintStyle = HINT_SLIGHT; break;
1227 case FC_HINT_MEDIUM: pOptions->meHintStyle = HINT_MEDIUM; break;
1228 default: // fall through
1229 case FC_HINT_FULL: pOptions->meHintStyle = HINT_FULL; break;
1233 // cleanup
1234 FcPatternDestroy( pPattern );
1236 return pOptions;
1239 bool PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const com::sun::star::lang::Locale& rLocale )
1241 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1243 FcConfig* pConfig = FcConfigGetCurrent();
1244 FcPattern* pPattern = FcPatternCreate();
1246 // populate pattern with font characteristics
1247 const LanguageTag aLangTag(rLocale);
1248 const OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
1249 if (!aLangAttrib.isEmpty())
1250 FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
1252 OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1253 if( !aFamily.isEmpty() )
1254 FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(aFamily.getStr()));
1256 addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1258 FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1259 FcDefaultSubstitute(pPattern);
1260 FcResult eResult = FcResultNoMatch;
1261 FcFontSet *pFontSet = rWrapper.getFontSet();
1262 FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult);
1263 bool bSuccess = false;
1264 if( pResult )
1266 FcFontSet* pSet = FcFontSetCreate();
1267 FcFontSetAdd( pSet, pResult );
1268 if( pSet->nfont > 0 )
1270 //extract the closest match
1271 FcChar8* file = NULL;
1272 FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1273 int nCollectionEntry = 0;
1274 FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
1275 if (eIndexRes != FcResultMatch)
1276 nCollectionEntry = 0;
1277 if( eFileRes == FcResultMatch )
1279 OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
1280 splitPath( aOrgPath, aDir, aBase );
1281 int nDirID = getDirectoryAtom( aDir, true );
1282 fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
1283 if( aFont > 0 )
1284 bSuccess = getFontFastInfo( aFont, rInfo );
1287 // info: destroying the pSet destroys pResult implicitly
1288 // since pResult was "added" to pSet
1289 FcFontSetDestroy( pSet );
1292 // cleanup
1293 FcPatternDestroy( pPattern );
1295 return bSuccess;
1298 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */