update credits
[LibreOffice.git] / vcl / generic / fontmanager / fontconfig.cxx
blob3d3bd81fb83560f8a770c97c568f52e31f990455
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 .
21 #include "fontcache.hxx"
22 #include "impfont.hxx"
23 #include <vcl/fontmanager.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/sysdata.hxx>
26 #include <vcl/vclenum.hxx>
27 #include <vcl/wrkwin.hxx>
28 #include "outfont.hxx"
29 #include <i18nlangtag/languagetag.hxx>
30 #include <i18nutil/unicode.hxx>
31 #include <rtl/strbuf.hxx>
32 #include <unicode/uchar.h>
33 #include <unicode/uscript.h>
35 using namespace psp;
37 #include <fontconfig/fontconfig.h>
38 #include <ft2build.h>
39 #include <fontconfig/fcfreetype.h>
40 // allow compile on baseline (currently with fontconfig 2.2.0)
41 #ifndef FC_WEIGHT_BOOK // TODO: remove when baseline moves to fc>=2.2.1
42 #define FC_WEIGHT_BOOK 75
43 #endif
44 #ifndef FC_EMBEDDED_BITMAP // TODO: remove when baseline moves to fc>=2.3.92
45 #define FC_EMBEDDED_BITMAP "embeddedbitmap"
46 #endif
47 #ifndef FC_FAMILYLANG // TODO: remove when baseline moves to fc>=2.2.97
48 #define FC_FAMILYLANG "familylang"
49 #endif
50 #ifndef FC_CAPABILITY // TODO: remove when baseline moves to fc>=2.2.97
51 #define FC_CAPABILITY "capability"
52 #endif
53 #ifndef FC_STYLELANG // TODO: remove when baseline moves to fc>=2.2.97
54 #define FC_STYLELANG "stylelang"
55 #endif
56 #ifndef FC_HINT_STYLE // TODO: remove when baseline moves to fc>=2.2.91
57 #define FC_HINT_STYLE "hintstyle"
58 #define FC_HINT_NONE 0
59 #define FC_HINT_SLIGHT 1
60 #define FC_HINT_MEDIUM 2
61 #define FC_HINT_FULL 3
62 #endif
63 #ifndef FC_FT_FACE
64 #define FC_FT_FACE "ftface"
65 #endif
66 #ifndef FC_EMBOLDEN
67 #define FC_EMBOLDEN "embolden"
68 #endif
69 #ifndef FC_MATRIX
70 #define FC_MATRIX "matrix"
71 #endif
72 #ifndef FC_FONTFORMAT
73 #define FC_FONTFORMAT "fontformat"
74 #endif
76 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
77 #include <dbus/dbus-glib.h>
78 #endif
80 #include <cstdio>
81 #include <cstdarg>
83 #include "unotools/atom.hxx"
85 #include "osl/module.h"
86 #include "osl/thread.h"
87 #include "osl/process.h"
89 #include "rtl/ustrbuf.hxx"
91 #include "sal/alloca.h"
93 #include <utility>
94 #include <algorithm>
96 using namespace osl;
98 namespace
100 typedef std::pair<FcChar8*, FcChar8*> lang_and_element;
103 class FontCfgWrapper
105 FcFontSet* m_pOutlineSet;
107 void addFontSet( FcSetName );
109 FontCfgWrapper();
110 ~FontCfgWrapper();
112 public:
113 static FontCfgWrapper& get();
114 static void release();
116 FcFontSet* getFontSet();
118 void clear();
120 public:
121 FcResult LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **family,
122 const char *elementtype, const char *elementlangtype);
123 //to-do, make private and add some cleanish accessor methods
124 boost::unordered_map< OString, OString, OStringHash > m_aFontNameToLocalized;
125 boost::unordered_map< OString, OString, OStringHash > m_aLocalizedToCanonical;
126 private:
127 void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);
129 LanguageTag* m_pLanguageTag;
132 FontCfgWrapper::FontCfgWrapper()
134 m_pOutlineSet( NULL ),
135 m_pLanguageTag( NULL )
137 FcInit();
140 void FontCfgWrapper::addFontSet( FcSetName eSetName )
143 add only acceptable outlined fonts to our config,
144 for future fontconfig use
146 FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
147 if( !pOrig )
148 return;
150 // filter the font sets to remove obsolete faces
151 for( int i = 0; i < pOrig->nfont; ++i )
153 FcPattern* pPattern = pOrig->fonts[i];
154 // #i115131# ignore non-outline fonts
155 FcBool bOutline = FcFalse;
156 FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
157 if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
158 continue;
159 FcPatternReference( pPattern );
160 FcFontSetAdd( m_pOutlineSet, pPattern );
163 // TODO?: FcFontSetDestroy( pOrig );
166 namespace
168 int compareFontNames(const FcPattern *a, const FcPattern *b)
170 FcChar8 *pNameA=NULL, *pNameB=NULL;
172 int nHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
173 int nHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
175 if (nHaveA && nHaveB)
176 return strcmp((const char*)pNameA, (const char*)pNameB);
178 return nHaveA - nHaveB;
181 //Sort fonts so that fonts with the same family name are side-by-side, with
182 //those with higher version numbers first
183 class SortFont : public ::std::binary_function< const FcPattern*, const FcPattern*, bool >
185 public:
186 bool operator()(const FcPattern *a, const FcPattern *b)
188 int comp = compareFontNames(a, b);
189 if (comp != 0)
190 return comp < 0;
192 int nVersionA=0, nVersionB=0;
194 int nHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
195 int nHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;
197 if (nHaveA && nHaveB)
198 return nVersionA > nVersionB;
200 return nHaveA > nHaveB;
204 //See fdo#30729 for where an old opensymbol installed system-wide can
205 //clobber the new opensymbol installed locally
207 //See if this font is a duplicate with equal attributes which has already been
208 //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet
209 //on being sorted with SortFont
210 bool isPreviouslyDuplicateOrObsoleted(FcFontSet *pFSet, int i)
212 const FcPattern *a = pFSet->fonts[i];
214 FcPattern* pTestPatternA = FcPatternDuplicate(a);
215 FcPatternDel(pTestPatternA, FC_FILE);
216 FcPatternDel(pTestPatternA, FC_CHARSET);
217 FcPatternDel(pTestPatternA, FC_CAPABILITY);
218 FcPatternDel(pTestPatternA, FC_FONTVERSION);
219 FcPatternDel(pTestPatternA, FC_LANG);
221 bool bIsDup(false);
223 // fdo#66715: loop for case of several font files for same font
224 for (int j = i - 1; 0 <= j && !bIsDup; --j)
226 const FcPattern *b = pFSet->fonts[j];
228 if (compareFontNames(a, b) != 0)
229 break;
231 FcPattern* pTestPatternB = FcPatternDuplicate(b);
232 FcPatternDel(pTestPatternB, FC_FILE);
233 FcPatternDel(pTestPatternB, FC_CHARSET);
234 FcPatternDel(pTestPatternB, FC_CAPABILITY);
235 FcPatternDel(pTestPatternB, FC_FONTVERSION);
236 FcPatternDel(pTestPatternB, FC_LANG);
238 bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);
240 FcPatternDestroy(pTestPatternB);
243 FcPatternDestroy(pTestPatternA);
245 return bIsDup;
249 FcFontSet* FontCfgWrapper::getFontSet()
251 if( !m_pOutlineSet )
253 m_pOutlineSet = FcFontSetCreate();
254 addFontSet( FcSetSystem );
255 if( FcGetVersion() > 20400 ) // #i85462# prevent crashes
256 addFontSet( FcSetApplication );
258 ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont());
261 return m_pOutlineSet;
264 FontCfgWrapper::~FontCfgWrapper()
266 clear();
267 //To-Do: get gtk vclplug smoketest to pass
268 //FcFini();
271 static FontCfgWrapper* pOneInstance = NULL;
273 FontCfgWrapper& FontCfgWrapper::get()
275 if( ! pOneInstance )
276 pOneInstance = new FontCfgWrapper();
277 return *pOneInstance;
280 void FontCfgWrapper::release()
282 if( pOneInstance )
284 delete pOneInstance;
285 pOneInstance = NULL;
289 namespace
291 class localizedsorter
293 public:
294 localizedsorter() {};
295 FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);
298 FcChar8* localizedsorter::bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
300 FcChar8* candidate = elements.begin()->second;
301 /* FIXME-BCP47: once fontconfig supports language tags this
302 * language-territory stuff needs to be changed! */
303 SAL_INFO_IF( !rLangTag.isIsoLocale(), "i18n", "localizedsorter::bestname - not an ISO locale");
304 OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
305 OString sFullMatch = sLangMatch;
306 sFullMatch += OString('-');
307 sFullMatch += OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
309 std::vector<lang_and_element>::const_iterator aEnd = elements.end();
310 bool alreadyclosematch = false;
311 bool found_fallback_englishname = false;
312 for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter )
314 const char *pLang = (const char*)aIter->first;
315 if( rtl_str_compare( pLang, sFullMatch.getStr() ) == 0)
317 // both language and country match
318 candidate = aIter->second;
319 break;
321 else if( alreadyclosematch )
323 // current candidate matches lang of lang-TERRITORY
324 // override candidate only if there is a full match
325 continue;
327 else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0)
329 // just the language matches
330 candidate = aIter->second;
331 alreadyclosematch = true;
333 else if( found_fallback_englishname )
335 // already found an english fallback, don't override candidate
336 // unless there is a better language match
337 continue;
339 else if( rtl_str_compare( pLang, "en") == 0)
341 // select a fallback candidate of the first english element
342 // name
343 candidate = aIter->second;
344 found_fallback_englishname = true;
347 return candidate;
351 //Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa
352 void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
353 const std::vector< lang_and_element > &lang_and_elements)
355 std::vector<lang_and_element>::const_iterator aEnd = lang_and_elements.end();
356 for (std::vector<lang_and_element>::const_iterator aIter = lang_and_elements.begin(); aIter != aEnd; ++aIter)
358 const char *candidate = (const char*)(aIter->second);
359 if (rtl_str_compare(candidate, (const char*)bestfontname) != 0)
360 m_aFontNameToLocalized[OString(candidate)] = OString((const char*)bestfontname);
362 if (rtl_str_compare((const char*)origfontname, (const char*)bestfontname) != 0)
363 m_aLocalizedToCanonical[OString((const char*)bestfontname)] = OString((const char*)origfontname);
366 FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **element,
367 const char *elementtype, const char *elementlangtype)
368 { /* e. g.: ^ FC_FAMILY ^ FC_FAMILYLANG */
369 FcChar8 *origelement;
370 FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
371 *element = origelement;
373 if( eElementRes == FcResultMatch)
375 FcChar8* elementlang = NULL;
376 if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
378 std::vector< lang_and_element > lang_and_elements;
379 lang_and_elements.push_back(lang_and_element(elementlang, *element));
380 int k = 1;
381 while (1)
383 if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
384 break;
385 if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
386 break;
387 lang_and_elements.push_back(lang_and_element(elementlang, *element));
388 ++k;
391 //possible to-do, sort by UILocale instead of process locale
392 if (!m_pLanguageTag)
394 rtl_Locale* pLoc = NULL;
395 osl_getProcessLocale(&pLoc);
396 m_pLanguageTag = new LanguageTag(*pLoc);
398 *element = localizedsorter().bestname(lang_and_elements, *m_pLanguageTag);
400 //if this element is a fontname, map the other names to this best-name
401 if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
402 cacheLocalizedFontNames(origelement, *element, lang_and_elements);
406 return eElementRes;
409 void FontCfgWrapper::clear()
411 m_aFontNameToLocalized.clear();
412 m_aLocalizedToCanonical.clear();
413 if( m_pOutlineSet )
415 FcFontSetDestroy( m_pOutlineSet );
416 m_pOutlineSet = NULL;
418 delete m_pLanguageTag;
419 m_pLanguageTag = NULL;
423 * PrintFontManager::initFontconfig
425 void PrintFontManager::initFontconfig()
427 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
428 rWrapper.clear();
431 namespace
433 FontWeight convertWeight(int weight)
435 // set weight
436 if( weight <= FC_WEIGHT_THIN )
437 return WEIGHT_THIN;
438 else if( weight <= FC_WEIGHT_ULTRALIGHT )
439 return WEIGHT_ULTRALIGHT;
440 else if( weight <= FC_WEIGHT_LIGHT )
441 return WEIGHT_LIGHT;
442 else if( weight <= FC_WEIGHT_BOOK )
443 return WEIGHT_SEMILIGHT;
444 else if( weight <= FC_WEIGHT_NORMAL )
445 return WEIGHT_NORMAL;
446 else if( weight <= FC_WEIGHT_MEDIUM )
447 return WEIGHT_MEDIUM;
448 else if( weight <= FC_WEIGHT_SEMIBOLD )
449 return WEIGHT_SEMIBOLD;
450 else if( weight <= FC_WEIGHT_BOLD )
451 return WEIGHT_BOLD;
452 else if( weight <= FC_WEIGHT_ULTRABOLD )
453 return WEIGHT_ULTRABOLD;
454 return WEIGHT_BLACK;
457 FontItalic convertSlant(int slant)
459 // set italic
460 if( slant == FC_SLANT_ITALIC )
461 return ITALIC_NORMAL;
462 else if( slant == FC_SLANT_OBLIQUE )
463 return ITALIC_OBLIQUE;
464 return ITALIC_NONE;
467 FontPitch convertSpacing(int spacing)
469 // set pitch
470 if( spacing == FC_MONO || spacing == FC_CHARCELL )
471 return PITCH_FIXED;
472 return PITCH_VARIABLE;
475 // translation: fontconfig enum -> vcl enum
476 FontWidth convertWidth(int width)
478 if (width == FC_WIDTH_ULTRACONDENSED)
479 return WIDTH_ULTRA_CONDENSED;
480 else if (width == FC_WIDTH_EXTRACONDENSED)
481 return WIDTH_EXTRA_CONDENSED;
482 else if (width == FC_WIDTH_CONDENSED)
483 return WIDTH_CONDENSED;
484 else if (width == FC_WIDTH_SEMICONDENSED)
485 return WIDTH_SEMI_CONDENSED;
486 else if (width == FC_WIDTH_SEMIEXPANDED)
487 return WIDTH_SEMI_EXPANDED;
488 else if (width == FC_WIDTH_EXPANDED)
489 return WIDTH_EXPANDED;
490 else if (width == FC_WIDTH_EXTRAEXPANDED)
491 return WIDTH_EXTRA_EXPANDED;
492 else if (width == FC_WIDTH_ULTRAEXPANDED)
493 return WIDTH_ULTRA_EXPANDED;
494 return WIDTH_NORMAL;
498 //FontConfig doesn't come with a way to remove an element from a FontSet as far
499 //as I can see
500 static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
502 FcPatternDestroy(pFSet->fonts[i]);
504 int nTail = pFSet->nfont - (i + 1);
505 --pFSet->nfont;
506 if (!nTail)
507 return;
508 memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
511 void PrintFontManager::countFontconfigFonts( boost::unordered_map<OString, int, OStringHash>& o_rVisitedPaths )
513 #if OSL_DEBUG_LEVEL > 1
514 int nFonts = 0;
515 #endif
516 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
518 FcFontSet* pFSet = rWrapper.getFontSet();
519 if( pFSet )
521 #if OSL_DEBUG_LEVEL > 1
522 fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont );
523 #endif
524 for( int i = 0; i < pFSet->nfont; i++ )
526 FcChar8* file = NULL;
527 FcChar8* family = NULL;
528 FcChar8* style = NULL;
529 FcChar8* format = NULL;
530 int slant = 0;
531 int weight = 0;
532 int width = 0;
533 int spacing = 0;
534 int nCollectionEntry = -1;
535 FcBool outline = false;
537 FcResult eFileRes = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
538 FcResult eFamilyRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
539 FcResult eStyleRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
540 FcResult eSlantRes = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
541 FcResult eWeightRes = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
542 FcResult eWidthRes = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
543 FcResult eSpacRes = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
544 FcResult eOutRes = FcPatternGetBool(pFSet->fonts[i], FC_OUTLINE, 0, &outline);
545 FcResult eIndexRes = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry);
546 FcResult eFormatRes = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
548 if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch )
549 continue;
551 #if (OSL_DEBUG_LEVEL > 2)
552 fprintf( stderr, "found font \"%s\" in file %s\n"
553 " weight = %d, slant = %d, style = \"%s\"\n"
554 " width = %d, spacing = %d, outline = %d, format %s\n"
555 , family, file
556 , eWeightRes == FcResultMatch ? weight : -1
557 , eSpacRes == FcResultMatch ? slant : -1
558 , eStyleRes == FcResultMatch ? (const char*) style : "<nil>"
559 , eWeightRes == FcResultMatch ? width : -1
560 , eSpacRes == FcResultMatch ? spacing : -1
561 , eOutRes == FcResultMatch ? outline : -1
562 , eFormatRes == FcResultMatch ? (const char*)format : "<unknown>"
564 #endif
566 // OSL_ASSERT(eOutRes != FcResultMatch || outline);
568 // only outline fonts are usable to psprint anyway
569 if( eOutRes == FcResultMatch && ! outline )
570 continue;
572 if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
574 #if OSL_DEBUG_LEVEL > 2
575 fprintf(stderr, "Ditching %s as duplicate/obsolete\n", file);
576 #endif
577 continue;
580 // see if this font is already cached
581 // update attributes
582 std::list< PrintFont* > aFonts;
583 OString aDir, aBase, aOrgPath( (sal_Char*)file );
584 splitPath( aOrgPath, aDir, aBase );
586 o_rVisitedPaths[aDir] = 1;
588 int nDirID = getDirectoryAtom( aDir, true );
589 if( ! m_pFontCache->getFontCacheFile( nDirID, aBase, aFonts ) )
591 #if OSL_DEBUG_LEVEL > 2
592 fprintf( stderr, "file %s not cached\n", aBase.getStr() );
593 #endif
594 // not known, analyze font file to get attributes
595 // not described by fontconfig (e.g. alias names, PSName)
596 if (eFormatRes != FcResultMatch)
597 format = NULL;
598 analyzeFontFile( nDirID, aBase, aFonts, (const char*)format );
599 #if OSL_DEBUG_LEVEL > 1
600 if( aFonts.empty() )
601 fprintf( stderr, "Warning: file \"%s\" is unusable to psprint\n", aOrgPath.getStr() );
602 #endif
604 if( aFonts.empty() )
606 //remove font, reuse index
607 //we want to remove unusable fonts here, in case there is a usable font
608 //which duplicates the properties of the unusable one
610 //not removing the unusable font will risk the usable font being rejected
611 //as a duplicate by isPreviouslyDuplicateOrObsoleted
612 lcl_FcFontSetRemove(pFSet, i--);
613 continue;
616 int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( (sal_Char*)family ), RTL_TEXTENCODING_UTF8 ), sal_True );
617 PrintFont* pUpdate = aFonts.front();
618 std::list<PrintFont*>::const_iterator second_font = aFonts.begin();
619 ++second_font;
620 if( second_font != aFonts.end() ) // more than one font
622 // a collection entry, get the correct index
623 if( eIndexRes == FcResultMatch && nCollectionEntry != -1 )
625 for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
627 if( (*it)->m_eType == fonttype::TrueType &&
628 static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry )
630 pUpdate = *it;
631 break;
634 // update collection entry
635 // additional entries will be created in the cache
636 // if this is a new index (that is if the loop above
637 // ran to the end of the list)
638 if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here
639 static_cast<TrueTypeFontFile*>(pUpdate)->m_nCollectionEntry = nCollectionEntry;
641 else
643 #if OSL_DEBUG_LEVEL > 1
644 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 );
645 #endif
646 // we have found more than one font in this file
647 // but fontconfig will not tell us which index is meant
648 // -> something is in disorder, do not use this font
649 pUpdate = NULL;
653 if( pUpdate )
655 // set family name
656 if( pUpdate->m_nFamilyName != nFamilyName )
659 if( eWeightRes == FcResultMatch )
660 pUpdate->m_eWeight = convertWeight(weight);
661 if( eWidthRes == FcResultMatch )
662 pUpdate->m_eWidth = convertWidth(width);
663 if( eSpacRes == FcResultMatch )
664 pUpdate->m_ePitch = convertSpacing(spacing);
665 if( eSlantRes == FcResultMatch )
666 pUpdate->m_eItalic = convertSlant(slant);
667 if( eStyleRes == FcResultMatch )
669 pUpdate->m_aStyleName = OStringToOUString( OString( (sal_Char*)style ), RTL_TEXTENCODING_UTF8 );
672 // update font cache
673 m_pFontCache->updateFontCacheEntry( pUpdate, false );
674 // sort into known fonts
675 fontID aFont = m_nNextFontID++;
676 m_aFonts[ aFont ] = pUpdate;
677 m_aFontFileToFontID[ aBase ].insert( aFont );
678 #if OSL_DEBUG_LEVEL > 1
679 nFonts++;
680 #endif
681 #if OSL_DEBUG_LEVEL > 2
682 fprintf( stderr, "inserted font %s as fontID %d\n", family, aFont );
683 #endif
685 // clean up the fonts we did not put into the list
686 for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
688 if( *it != pUpdate )
690 m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item
691 delete *it;
697 // how does one get rid of the config ?
698 #if OSL_DEBUG_LEVEL > 1
699 fprintf( stderr, "inserted %d fonts from fontconfig\n", nFonts );
700 #endif
703 void PrintFontManager::deinitFontconfig()
705 FontCfgWrapper::release();
708 bool PrintFontManager::addFontconfigDir( const OString& rDirName )
710 // workaround for a stability problems in older FC versions
711 // when handling application specifc fonts
712 const int nVersion = FcGetVersion();
713 if( nVersion <= 20400 )
714 return false;
715 const char* pDirName = (const char*)rDirName.getStr();
716 bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), (FcChar8*)pDirName ) == FcTrue);
718 #if OSL_DEBUG_LEVEL > 1
719 fprintf( stderr, "FcConfigAppFontAddDir( \"%s\") => %d\n", pDirName, bDirOk );
720 #endif
722 if( !bDirOk )
723 return false;
725 // load dir-specific fc-config file too if available
726 const OString aConfFileName = rDirName + "/fc_local.conf";
727 FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
728 if( pCfgFile )
730 fclose( pCfgFile);
731 bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
732 (FcChar8*)aConfFileName.getStr(), FcTrue);
733 if( !bCfgOk )
734 fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk );
737 return true;
740 static void addtopattern(FcPattern *pPattern,
741 FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
743 if( eItalic != ITALIC_DONTKNOW )
745 int nSlant = FC_SLANT_ROMAN;
746 switch( eItalic )
748 case ITALIC_NORMAL:
749 nSlant = FC_SLANT_ITALIC;
750 break;
751 case ITALIC_OBLIQUE:
752 nSlant = FC_SLANT_OBLIQUE;
753 break;
754 default:
755 break;
757 FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
759 if( eWeight != WEIGHT_DONTKNOW )
761 int nWeight = FC_WEIGHT_NORMAL;
762 switch( eWeight )
764 case WEIGHT_THIN: nWeight = FC_WEIGHT_THIN;break;
765 case WEIGHT_ULTRALIGHT: nWeight = FC_WEIGHT_ULTRALIGHT;break;
766 case WEIGHT_LIGHT: nWeight = FC_WEIGHT_LIGHT;break;
767 case WEIGHT_SEMILIGHT: nWeight = FC_WEIGHT_BOOK;break;
768 case WEIGHT_NORMAL: nWeight = FC_WEIGHT_NORMAL;break;
769 case WEIGHT_MEDIUM: nWeight = FC_WEIGHT_MEDIUM;break;
770 case WEIGHT_SEMIBOLD: nWeight = FC_WEIGHT_SEMIBOLD;break;
771 case WEIGHT_BOLD: nWeight = FC_WEIGHT_BOLD;break;
772 case WEIGHT_ULTRABOLD: nWeight = FC_WEIGHT_ULTRABOLD;break;
773 case WEIGHT_BLACK: nWeight = FC_WEIGHT_BLACK;break;
774 default:
775 break;
777 FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
779 if( eWidth != WIDTH_DONTKNOW )
781 int nWidth = FC_WIDTH_NORMAL;
782 switch( eWidth )
784 case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
785 case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break;
786 case WIDTH_CONDENSED: nWidth = FC_WIDTH_CONDENSED;break;
787 case WIDTH_SEMI_CONDENSED: nWidth = FC_WIDTH_SEMICONDENSED;break;
788 case WIDTH_NORMAL: nWidth = FC_WIDTH_NORMAL;break;
789 case WIDTH_SEMI_EXPANDED: nWidth = FC_WIDTH_SEMIEXPANDED;break;
790 case WIDTH_EXPANDED: nWidth = FC_WIDTH_EXPANDED;break;
791 case WIDTH_EXTRA_EXPANDED: nWidth = FC_WIDTH_EXTRAEXPANDED;break;
792 case WIDTH_ULTRA_EXPANDED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
793 default:
794 break;
796 FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
798 if( ePitch != PITCH_DONTKNOW )
800 int nSpacing = FC_PROPORTIONAL;
801 switch( ePitch )
803 case PITCH_FIXED: nSpacing = FC_MONO;break;
804 case PITCH_VARIABLE: nSpacing = FC_PROPORTIONAL;break;
805 default:
806 break;
808 FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
809 if (nSpacing == FC_MONO)
810 FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)"monospace");
814 namespace
816 //Someday fontconfig will hopefully use bcp47, see fdo#19869
817 //In the meantime try something that will fit to workaround fdo#35118
818 OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
820 #if defined(FC_VERSION) && (FC_VERSION >= 20492)
821 boost::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
822 OString sLangAttrib;
824 sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
825 if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLangAttrib.getStr()))
827 return sLangAttrib;
830 sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
831 if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLangAttrib.getStr()))
833 return sLangAttrib;
836 OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
837 OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
839 if (!sRegion.isEmpty())
841 sLangAttrib = sLang + OString('-') + sRegion;
842 if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLangAttrib.getStr()))
844 return sLangAttrib;
848 if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLang.getStr()))
850 return sLang;
853 return OString();
854 #else
855 OString sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
856 if (sLangAttrib.equalsIgnoreAsciiCase("pa-in"))
857 sLangAttrib = "pa";
858 return sLangAttrib;
859 #endif
862 //returns true if the given code-point couldn't possibly be in rLangTag.
863 bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
865 //a non-default script is set, lets believe it
866 if (rLangTag.hasScript())
867 return false;
869 int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
870 UScriptCode eScript = static_cast<UScriptCode>(script);
871 bool bIsImpossible = false;
872 OUString sLang = rLangTag.getLanguage();
873 switch (eScript)
875 //http://en.wiktionary.org/wiki/Category:Oriya_script_languages
876 case USCRIPT_ORIYA:
877 bIsImpossible =
878 sLang != "or" &&
879 sLang != "kxv";
880 break;
881 //http://en.wiktionary.org/wiki/Category:Telugu_script_languages
882 case USCRIPT_TELUGU:
883 bIsImpossible =
884 sLang != "te" &&
885 sLang != "gon" &&
886 sLang != "kfc";
887 break;
888 //http://en.wiktionary.org/wiki/Category:Bengali_script_languages
889 case USCRIPT_BENGALI:
890 bIsImpossible =
891 sLang != "bn" &&
892 sLang != "as" &&
893 sLang != "bpy" &&
894 sLang != "ctg" &&
895 sLang != "sa";
896 break;
897 default:
898 break;
900 SAL_WARN_IF(bIsImpossible, "vcl", "Throwing away user set language of "
901 << sLang << " for finding a font for glyph fallback and autodetecting instead");
902 return bIsImpossible;
905 LanguageTag getExemplerLangTagForCodePoint(sal_uInt32 currentChar)
907 int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
908 UScriptCode eScript = static_cast<UScriptCode>(script);
909 OStringBuffer aBuf(unicode::getExemplerLanguageForUScriptCode(eScript));
910 const char* pScriptCode = uscript_getShortName(eScript);
911 if (pScriptCode)
912 aBuf.append('-').append(pScriptCode);
913 return LanguageTag(OStringToOUString(aBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8));
916 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
917 guint get_xid_for_dbus()
919 const Window *pTopWindow = Application::IsHeadlessModeEnabled() ? NULL : Application::GetActiveTopWindow();
920 const SystemEnvData* pEnvData = pTopWindow ? pTopWindow->GetSystemData() : NULL;
921 return pEnvData ? pEnvData->aWindow : 0;
923 #endif
926 IMPL_LINK_NOARG(PrintFontManager, autoInstallFontLangSupport)
928 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
929 guint xid = get_xid_for_dbus();
931 if (!xid)
932 return -1;
934 GError *error = NULL;
935 /* get the DBUS session connection */
936 DBusGConnection *session_connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
937 if (error != NULL)
939 g_debug ("DBUS cannot connect : %s", error->message);
940 g_error_free (error);
941 return -1;
944 /* get the proxy with gnome-session-manager */
945 DBusGProxy *proxy = dbus_g_proxy_new_for_name(session_connection,
946 "org.freedesktop.PackageKit",
947 "/org/freedesktop/PackageKit",
948 "org.freedesktop.PackageKit.Modify");
949 if (proxy == NULL)
951 g_debug("Could not get DBUS proxy: org.freedesktop.PackageKit");
952 return -1;
955 gchar **fonts = (gchar**)g_malloc((m_aCurrentRequests.size() + 1) * sizeof(gchar*));
956 gchar **font = fonts;
957 for (std::vector<OString>::const_iterator aI = m_aCurrentRequests.begin(); aI != m_aCurrentRequests.end(); ++aI)
958 *font++ = (gchar*)aI->getStr();
959 *font = NULL;
960 gboolean res = dbus_g_proxy_call(proxy, "InstallFontconfigResources", &error,
961 G_TYPE_UINT, xid, /* xid */
962 G_TYPE_STRV, fonts, /* data */
963 G_TYPE_STRING, "hide-finished", /* interaction */
964 G_TYPE_INVALID,
965 G_TYPE_INVALID);
966 /* check the return value */
967 if (!res)
968 g_debug("InstallFontconfigResources method failed");
970 /* check the error value */
971 if (error != NULL)
973 g_debug("InstallFontconfigResources problem : %s", error->message);
974 g_error_free(error);
977 g_free(fonts);
978 g_object_unref(G_OBJECT (proxy));
979 m_aCurrentRequests.clear();
980 #endif
981 return 0;
984 bool PrintFontManager::Substitute( FontSelectPattern &rPattern, OUString& rMissingCodes )
986 bool bRet = false;
988 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
990 // build pattern argument for fontconfig query
991 FcPattern* pPattern = FcPatternCreate();
993 // Prefer scalable fonts
994 FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
996 const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
997 const FcChar8* pTargetNameUtf8 = (FcChar8*)aTargetName.getStr();
998 FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
1000 LanguageTag aLangTag(rPattern.meLanguage);
1001 OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
1003 // Add required Unicode characters, if any
1004 if ( !rMissingCodes.isEmpty() )
1006 FcCharSet *unicodes = FcCharSetCreate();
1007 for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1009 // also handle unicode surrogates
1010 const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
1011 FcCharSetAddChar( unicodes, nCode );
1012 //if the codepoint is impossible for this lang tag, then clear it
1013 //and autodetect something useful
1014 if (!aLangAttrib.isEmpty() && isImpossibleCodePointForLang(aLangTag, nCode))
1015 aLangAttrib = OString();
1016 //#i105784#/rhbz#527719 improve selection of fallback font
1017 if (aLangAttrib.isEmpty())
1019 aLangTag = getExemplerLangTagForCodePoint(nCode);
1020 aLangAttrib = mapToFontConfigLangTag(aLangTag);
1023 FcPatternAddCharSet(pPattern, FC_CHARSET, unicodes);
1024 FcCharSetDestroy(unicodes);
1027 if (!aLangAttrib.isEmpty())
1028 FcPatternAddString(pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr());
1030 addtopattern(pPattern, rPattern.GetSlant(), rPattern.GetWeight(),
1031 rPattern.GetWidthType(), rPattern.GetPitch());
1033 // query fontconfig for a substitute
1034 FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
1035 FcDefaultSubstitute(pPattern);
1037 // process the result of the fontconfig query
1038 FcResult eResult = FcResultNoMatch;
1039 FcFontSet* pFontSet = rWrapper.getFontSet();
1040 FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
1041 FcPatternDestroy( pPattern );
1043 FcFontSet* pSet = NULL;
1044 if( pResult )
1046 pSet = FcFontSetCreate();
1047 // info: destroying the pSet destroys pResult implicitly
1048 // since pResult was "added" to pSet
1049 FcFontSetAdd( pSet, pResult );
1052 if( pSet )
1054 if( pSet->nfont > 0 )
1056 //extract the closest match
1057 FcChar8* file = NULL;
1058 FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1059 int nCollectionEntry = 0;
1060 FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
1061 if (eIndexRes != FcResultMatch)
1062 nCollectionEntry = 0;
1063 if( eFileRes == FcResultMatch )
1065 OString aDir, aBase, aOrgPath( (sal_Char*)file );
1066 splitPath( aOrgPath, aDir, aBase );
1067 int nDirID = getDirectoryAtom( aDir, true );
1068 fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
1069 if( aFont > 0 )
1071 FastPrintFontInfo aInfo;
1072 bRet = getFontFastInfo( aFont, aInfo );
1073 rPattern.maSearchName = aInfo.m_aFamilyName;
1077 SAL_WARN_IF(!bRet, "vcl", "no FC_FILE found, falling back to name search");
1079 if (!bRet)
1081 FcChar8* family = NULL;
1082 FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
1084 // get the family name
1085 if( eFamilyRes == FcResultMatch )
1087 OString sFamily((sal_Char*)family);
1088 boost::unordered_map< OString, OString, OStringHash >::const_iterator aI =
1089 rWrapper.m_aFontNameToLocalized.find(sFamily);
1090 if (aI != rWrapper.m_aFontNameToLocalized.end())
1091 sFamily = aI->second;
1092 rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
1093 bRet = true;
1097 if (bRet)
1099 int val = 0;
1100 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
1101 rPattern.SetWeight( convertWeight(val) );
1102 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
1103 rPattern.SetItalic( convertSlant(val) );
1104 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
1105 rPattern.SetPitch ( convertSpacing(val) );
1106 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
1107 rPattern.SetWidthType ( convertWidth(val) );
1108 FcBool bEmbolden;
1109 if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
1110 rPattern.mbEmbolden = bEmbolden;
1111 FcMatrix *pMatrix = 0;
1112 if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
1114 rPattern.maItalicMatrix.xx = pMatrix->xx;
1115 rPattern.maItalicMatrix.xy = pMatrix->xy;
1116 rPattern.maItalicMatrix.yx = pMatrix->yx;
1117 rPattern.maItalicMatrix.yy = pMatrix->yy;
1121 // update rMissingCodes by removing resolved unicodes
1122 if( !rMissingCodes.isEmpty() )
1124 sal_uInt32* pRemainingCodes = (sal_uInt32*)alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) );
1125 int nRemainingLen = 0;
1126 FcCharSet* unicodes;
1127 if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &unicodes))
1129 for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1131 // also handle unicode surrogates
1132 const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
1133 if (FcCharSetHasChar(unicodes, nCode) != FcTrue)
1134 pRemainingCodes[ nRemainingLen++ ] = nCode;
1137 OUString sStillMissing(pRemainingCodes, nRemainingLen);
1138 #if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
1139 if (get_xid_for_dbus())
1141 if (sStillMissing == rMissingCodes) //replaced nothing
1143 //It'd be better if we could ask packagekit using the
1144 //missing codepoints or some such rather than using
1145 //"language" as a proxy to how fontconfig considers
1146 //scripts to default to a given language.
1147 for (sal_Int32 i = 0; i < nRemainingLen; ++i)
1149 LanguageTag aOurTag = getExemplerLangTagForCodePoint(pRemainingCodes[i]);
1150 OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8);
1151 if (m_aPreviousLangSupportRequests.find(sTag) != m_aPreviousLangSupportRequests.end())
1152 continue;
1153 m_aPreviousLangSupportRequests.insert(sTag);
1154 sTag = mapToFontConfigLangTag(aOurTag);
1155 if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
1157 OString sReq = OString(":lang=") + sTag;
1158 m_aCurrentRequests.push_back(sReq);
1159 m_aPreviousLangSupportRequests.insert(sTag);
1163 if (!m_aCurrentRequests.empty())
1165 m_aFontInstallerTimer.Stop();
1166 m_aFontInstallerTimer.Start();
1169 #endif
1170 rMissingCodes = sStillMissing;
1174 FcFontSetDestroy( pSet );
1177 return bRet;
1180 class FontConfigFontOptions : public ImplFontOptions
1182 public:
1183 FontConfigFontOptions() : mpPattern(0) {}
1184 ~FontConfigFontOptions()
1186 FcPatternDestroy(mpPattern);
1188 virtual void *GetPattern(void * face, bool bEmbolden, bool /*bVerticalLayout*/) const
1190 FcValue value;
1191 value.type = FcTypeFTFace;
1192 value.u.f = face;
1193 FcPatternDel(mpPattern, FC_FT_FACE);
1194 FcPatternAdd (mpPattern, FC_FT_FACE, value, FcTrue);
1195 FcPatternDel(mpPattern, FC_EMBOLDEN);
1196 FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse);
1197 #if 0
1198 FcPatternDel(mpPattern, FC_VERTICAL_LAYOUT);
1199 FcPatternAddBool(mpPattern, FC_VERTICAL_LAYOUT, bVerticalLayout ? FcTrue : FcFalse);
1200 #endif
1201 return mpPattern;
1203 FcPattern* mpPattern;
1206 ImplFontOptions* PrintFontManager::getFontOptions(
1207 const FastPrintFontInfo& rInfo, int nSize, void (*subcallback)(void*)) const
1209 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1211 FontConfigFontOptions* pOptions = NULL;
1212 FcConfig* pConfig = FcConfigGetCurrent();
1213 FcPattern* pPattern = FcPatternCreate();
1215 OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1217 boost::unordered_map< OString, OString, OStringHash >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
1218 if (aI != rWrapper.m_aLocalizedToCanonical.end())
1219 sFamily = aI->second;
1220 if( !sFamily.isEmpty() )
1221 FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)sFamily.getStr());
1223 addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1224 FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);
1226 FcBool embitmap = true, antialias = true, autohint = true, hinting = true;
1227 int hintstyle = FC_HINT_FULL;
1229 FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1230 if (subcallback)
1231 subcallback(pPattern);
1232 FcDefaultSubstitute(pPattern);
1234 FcResult eResult = FcResultNoMatch;
1235 FcFontSet* pFontSet = rWrapper.getFontSet();
1236 FcPattern* pResult = FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult );
1237 if( pResult )
1239 FcResult eEmbeddedBitmap = FcPatternGetBool(pResult,
1240 FC_EMBEDDED_BITMAP, 0, &embitmap);
1241 FcResult eAntialias = FcPatternGetBool(pResult,
1242 FC_ANTIALIAS, 0, &antialias);
1243 FcResult eAutoHint = FcPatternGetBool(pResult,
1244 FC_AUTOHINT, 0, &autohint);
1245 FcResult eHinting = FcPatternGetBool(pResult,
1246 FC_HINTING, 0, &hinting);
1247 /*FcResult eHintStyle =*/ FcPatternGetInteger(pResult,
1248 FC_HINT_STYLE, 0, &hintstyle);
1250 pOptions = new FontConfigFontOptions;
1252 pOptions->mpPattern = pResult;
1254 if( eEmbeddedBitmap == FcResultMatch )
1255 pOptions->meEmbeddedBitmap = embitmap ? EMBEDDEDBITMAP_TRUE : EMBEDDEDBITMAP_FALSE;
1256 if( eAntialias == FcResultMatch )
1257 pOptions->meAntiAlias = antialias ? ANTIALIAS_TRUE : ANTIALIAS_FALSE;
1258 if( eAutoHint == FcResultMatch )
1259 pOptions->meAutoHint = autohint ? AUTOHINT_TRUE : AUTOHINT_FALSE;
1260 if( eHinting == FcResultMatch )
1261 pOptions->meHinting = hinting ? HINTING_TRUE : HINTING_FALSE;
1262 switch (hintstyle)
1264 case FC_HINT_NONE: pOptions->meHintStyle = HINT_NONE; break;
1265 case FC_HINT_SLIGHT: pOptions->meHintStyle = HINT_SLIGHT; break;
1266 case FC_HINT_MEDIUM: pOptions->meHintStyle = HINT_MEDIUM; break;
1267 default: // fall through
1268 case FC_HINT_FULL: pOptions->meHintStyle = HINT_FULL; break;
1272 // cleanup
1273 FcPatternDestroy( pPattern );
1275 return pOptions;
1278 bool PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const com::sun::star::lang::Locale& rLocale )
1280 FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1282 FcConfig* pConfig = FcConfigGetCurrent();
1283 FcPattern* pPattern = FcPatternCreate();
1285 // populate pattern with font characteristics
1286 const LanguageTag aLangTag(rLocale);
1287 const OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
1288 if (!aLangAttrib.isEmpty())
1289 FcPatternAddString(pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr());
1291 OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1292 if( !aFamily.isEmpty() )
1293 FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)aFamily.getStr());
1295 addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1297 FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1298 FcDefaultSubstitute(pPattern);
1299 FcResult eResult = FcResultNoMatch;
1300 FcFontSet *pFontSet = rWrapper.getFontSet();
1301 FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult);
1302 bool bSuccess = false;
1303 if( pResult )
1305 FcFontSet* pSet = FcFontSetCreate();
1306 FcFontSetAdd( pSet, pResult );
1307 if( pSet->nfont > 0 )
1309 //extract the closest match
1310 FcChar8* file = NULL;
1311 FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1312 int nCollectionEntry = 0;
1313 FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
1314 if (eIndexRes != FcResultMatch)
1315 nCollectionEntry = 0;
1316 if( eFileRes == FcResultMatch )
1318 OString aDir, aBase, aOrgPath( (sal_Char*)file );
1319 splitPath( aOrgPath, aDir, aBase );
1320 int nDirID = getDirectoryAtom( aDir, true );
1321 fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
1322 if( aFont > 0 )
1323 bSuccess = getFontFastInfo( aFont, rInfo );
1326 // info: destroying the pSet destroys pResult implicitly
1327 // since pResult was "added" to pSet
1328 FcFontSetDestroy( pSet );
1331 // cleanup
1332 FcPatternDestroy( pPattern );
1334 return bSuccess;
1337 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */