1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <memory>
21 #include <unx/fontmanager.hxx>
22 #include <unx/helper.hxx>
23 #include <comphelper/sequence.hxx>
24 #include <vcl/svapp.hxx>
25 #include <vcl/vclenum.hxx>
26 #include <fontselect.hxx>
27 #include <i18nlangtag/languagetag.hxx>
28 #include <i18nutil/unicode.hxx>
29 #include <rtl/strbuf.hxx>
30 #include <sal/log.hxx>
31 #include <tools/diagnose_ex.h>
32 #include <unicode/uchar.h>
33 #include <unicode/uscript.h>
34 #include <officecfg/Office/Common.hxx>
35 #include <org/freedesktop/PackageKit/SyncDbusSessionHelper.hpp>
36 
37 using namespace psp;
38 
39 #include <fontconfig/fontconfig.h>
40 
41 #include <cstdio>
42 
43 #include <unotools/configmgr.hxx>
44 
45 #include <osl/process.h>
46 
47 #include <utility>
48 #include <algorithm>
49 
50 using namespace osl;
51 
52 namespace
53 {
54     typedef std::pair<FcChar8*, FcChar8*> lang_and_element;
55 }
56 
57 class FontCfgWrapper
58 {
59     FcFontSet* m_pFontSet;
60 
61     void addFontSet( FcSetName );
62 
63     FontCfgWrapper();
64     ~FontCfgWrapper();
65 
66 public:
67     static FontCfgWrapper& get();
68     static void release();
69 
70     FcFontSet* getFontSet();
71 
72     void clear();
73 
74 public:
75     FcResult LocalizedElementFromPattern(FcPattern const * pPattern, FcChar8 **family,
76                                          const char *elementtype, const char *elementlangtype);
77 //to-do, make private and add some cleaner accessor methods
78     std::unordered_map< OString, OString > m_aFontNameToLocalized;
79     std::unordered_map< OString, OString > m_aLocalizedToCanonical;
80 private:
81     void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);
82 
83     std::unique_ptr<LanguageTag> m_pLanguageTag;
84 };
85 
FontCfgWrapper()86 FontCfgWrapper::FontCfgWrapper()
87     : m_pFontSet( nullptr )
88 {
89     FcInit();
90 }
91 
addFontSet(FcSetName eSetName)92 void FontCfgWrapper::addFontSet( FcSetName eSetName )
93 {
94     // Add only acceptable fonts to our config, for future fontconfig use.
95     FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
96     if( !pOrig )
97         return;
98 
99     // filter the font sets to remove obsolete faces
100     for( int i = 0; i < pOrig->nfont; ++i )
101     {
102         FcPattern* pPattern = pOrig->fonts[i];
103         // #i115131# ignore non-scalable fonts
104         // Scalable fonts are usually outline fonts, but some bitmaps fonts
105         // (like Noto Color Emoji) are also scalable.
106         FcBool bScalable = FcFalse;
107         FcResult eScalableRes = FcPatternGetBool(pPattern, FC_SCALABLE, 0, &bScalable);
108         if ((eScalableRes != FcResultMatch) || (bScalable == FcFalse))
109             continue;
110 
111         // Ignore Type 1 fonts, too.
112         FcChar8* pFormat = nullptr;
113         FcResult eFormatRes = FcPatternGetString(pPattern, FC_FONTFORMAT, 0, &pFormat);
114         if ((eFormatRes == FcResultMatch) && (strcmp(reinterpret_cast<char*>(pFormat), "Type 1") == 0))
115             continue;
116 
117         FcPatternReference( pPattern );
118         FcFontSetAdd( m_pFontSet, pPattern );
119     }
120 
121     // TODO?: FcFontSetDestroy( pOrig );
122 }
123 
124 namespace
125 {
compareFontNames(const FcPattern * a,const FcPattern * b)126     int compareFontNames(const FcPattern *a, const FcPattern *b)
127     {
128         FcChar8 *pNameA=nullptr, *pNameB=nullptr;
129 
130         bool bHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
131         bool bHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
132 
133         if (bHaveA && bHaveB)
134             return strcmp(reinterpret_cast<const char*>(pNameA), reinterpret_cast<const char*>(pNameB));
135 
136         return int(bHaveA) - int(bHaveB);
137     }
138 
139     //Sort fonts so that fonts with the same family name are side-by-side, with
140     //those with higher version numbers first
141     class SortFont
142     {
143     public:
operator ()(const FcPattern * a,const FcPattern * b)144         bool operator()(const FcPattern *a, const FcPattern *b)
145         {
146             int comp = compareFontNames(a, b);
147             if (comp != 0)
148                 return comp < 0;
149 
150             int nVersionA=0, nVersionB=0;
151 
152             bool bHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
153             bool bHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;
154 
155             if (bHaveA && bHaveB)
156                 return nVersionA > nVersionB;
157 
158             return bHaveA > bHaveB;
159         }
160     };
161 
162     //See fdo#30729 for where an old opensymbol installed system-wide can
163     //clobber the new opensymbol installed locally
164 
165     //See if this font is a duplicate with equal attributes which has already been
166     //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet
167     //on being sorted with SortFont
isPreviouslyDuplicateOrObsoleted(FcFontSet const * pFSet,int i)168     bool isPreviouslyDuplicateOrObsoleted(FcFontSet const *pFSet, int i)
169     {
170         const FcPattern *a = pFSet->fonts[i];
171 
172         FcPattern* pTestPatternA = FcPatternDuplicate(a);
173         FcPatternDel(pTestPatternA, FC_FILE);
174         FcPatternDel(pTestPatternA, FC_CHARSET);
175         FcPatternDel(pTestPatternA, FC_CAPABILITY);
176         FcPatternDel(pTestPatternA, FC_FONTVERSION);
177         FcPatternDel(pTestPatternA, FC_LANG);
178 
179         bool bIsDup(false);
180 
181         // fdo#66715: loop for case of several font files for same font
182         for (int j = i - 1; 0 <= j && !bIsDup; --j)
183         {
184             const FcPattern *b = pFSet->fonts[j];
185 
186             if (compareFontNames(a, b) != 0)
187                 break;
188 
189             FcPattern* pTestPatternB = FcPatternDuplicate(b);
190             FcPatternDel(pTestPatternB, FC_FILE);
191             FcPatternDel(pTestPatternB, FC_CHARSET);
192             FcPatternDel(pTestPatternB, FC_CAPABILITY);
193             FcPatternDel(pTestPatternB, FC_FONTVERSION);
194             FcPatternDel(pTestPatternB, FC_LANG);
195 
196             bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);
197 
198             FcPatternDestroy(pTestPatternB);
199         }
200 
201         FcPatternDestroy(pTestPatternA);
202 
203         return bIsDup;
204     }
205 }
206 
getFontSet()207 FcFontSet* FontCfgWrapper::getFontSet()
208 {
209     if( !m_pFontSet )
210     {
211         m_pFontSet = FcFontSetCreate();
212         addFontSet( FcSetSystem );
213         addFontSet( FcSetApplication );
214 
215         std::stable_sort(m_pFontSet->fonts,m_pFontSet->fonts+m_pFontSet->nfont,SortFont());
216     }
217 
218     return m_pFontSet;
219 }
220 
~FontCfgWrapper()221 FontCfgWrapper::~FontCfgWrapper()
222 {
223     clear();
224     //To-Do: get gtk vclplug smoketest to pass
225     //FcFini();
226 }
227 
228 static FontCfgWrapper* pOneInstance = nullptr;
229 
get()230 FontCfgWrapper& FontCfgWrapper::get()
231 {
232     if( ! pOneInstance )
233         pOneInstance = new FontCfgWrapper();
234     return *pOneInstance;
235 }
236 
release()237 void FontCfgWrapper::release()
238 {
239     if( pOneInstance )
240     {
241         delete pOneInstance;
242         pOneInstance = nullptr;
243     }
244 }
245 
246 namespace
247 {
248     FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);
249 
bestname(const std::vector<lang_and_element> & elements,const LanguageTag & rLangTag)250     FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
251     {
252         FcChar8* candidate = elements.begin()->second;
253         /* FIXME-BCP47: once fontconfig supports language tags this
254          * language-territory stuff needs to be changed! */
255         SAL_INFO_IF( !rLangTag.isIsoLocale(), "vcl.fonts", "localizedsorter::bestname - not an ISO locale");
256         OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
257         OString sFullMatch = sLangMatch +
258             "-" +
259             OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
260 
261         bool alreadyclosematch = false;
262         bool found_fallback_englishname = false;
263         for (auto const& element : elements)
264         {
265             const char *pLang = reinterpret_cast<const char*>(element.first);
266             if( sFullMatch == pLang)
267             {
268                 // both language and country match
269                 candidate = element.second;
270                 break;
271             }
272             else if( alreadyclosematch )
273             {
274                 // current candidate matches lang of lang-TERRITORY
275                 // override candidate only if there is a full match
276                 continue;
277             }
278             else if( sLangMatch == pLang)
279             {
280                 // just the language matches
281                 candidate = element.second;
282                 alreadyclosematch = true;
283             }
284             else if( found_fallback_englishname )
285             {
286                 // already found an english fallback, don't override candidate
287                 // unless there is a better language match
288                 continue;
289             }
290             else if( rtl_str_compare( pLang, "en") == 0)
291             {
292                 // select a fallback candidate of the first english element
293                 // name
294                 candidate = element.second;
295                 found_fallback_englishname = true;
296             }
297         }
298         return candidate;
299     }
300 }
301 
302 //Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa
cacheLocalizedFontNames(const FcChar8 * origfontname,const FcChar8 * bestfontname,const std::vector<lang_and_element> & lang_and_elements)303 void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
304     const std::vector< lang_and_element > &lang_and_elements)
305 {
306     for (auto const& element : lang_and_elements)
307     {
308         const char *candidate = reinterpret_cast<const char*>(element.second);
309         if (rtl_str_compare(candidate, reinterpret_cast<const char*>(bestfontname)) != 0)
310             m_aFontNameToLocalized[OString(candidate)] = OString(reinterpret_cast<const char*>(bestfontname));
311     }
312     if (rtl_str_compare(reinterpret_cast<const char*>(origfontname), reinterpret_cast<const char*>(bestfontname)) != 0)
313         m_aLocalizedToCanonical[OString(reinterpret_cast<const char*>(bestfontname))] = OString(reinterpret_cast<const char*>(origfontname));
314 }
315 
LocalizedElementFromPattern(FcPattern const * pPattern,FcChar8 ** element,const char * elementtype,const char * elementlangtype)316 FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern const * pPattern, FcChar8 **element,
317                                                      const char *elementtype, const char *elementlangtype)
318 {                                                /* e. g.:      ^ FC_FAMILY              ^ FC_FAMILYLANG */
319     FcChar8 *origelement;
320     FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
321     *element = origelement;
322 
323     if( eElementRes == FcResultMatch)
324     {
325         FcChar8* elementlang = nullptr;
326         if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
327         {
328             std::vector< lang_and_element > lang_and_elements;
329             lang_and_elements.emplace_back(elementlang, *element);
330             int k = 1;
331             while (true)
332             {
333                 if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
334                     break;
335                 if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
336                     break;
337                 lang_and_elements.emplace_back(elementlang, *element);
338                 ++k;
339             }
340 
341             //possible to-do, sort by UILocale instead of process locale
342             if (!m_pLanguageTag)
343             {
344                 rtl_Locale* pLoc = nullptr;
345                 osl_getProcessLocale(&pLoc);
346                 m_pLanguageTag.reset( new LanguageTag(*pLoc) );
347             }
348             *element = bestname(lang_and_elements, *m_pLanguageTag);
349 
350             //if this element is a fontname, map the other names to this best-name
351             if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
352                 cacheLocalizedFontNames(origelement, *element, lang_and_elements);
353         }
354     }
355 
356     return eElementRes;
357 }
358 
clear()359 void FontCfgWrapper::clear()
360 {
361     m_aFontNameToLocalized.clear();
362     m_aLocalizedToCanonical.clear();
363     if( m_pFontSet )
364     {
365         FcFontSetDestroy( m_pFontSet );
366         m_pFontSet = nullptr;
367     }
368     m_pLanguageTag.reset();
369 }
370 
371 /*
372  * PrintFontManager::initFontconfig
373  */
initFontconfig()374 void PrintFontManager::initFontconfig()
375 {
376     FontCfgWrapper& rWrapper = FontCfgWrapper::get();
377     rWrapper.clear();
378 }
379 
380 namespace
381 {
convertWeight(int weight)382     FontWeight convertWeight(int weight)
383     {
384         // set weight
385         if( weight <= FC_WEIGHT_THIN )
386             return WEIGHT_THIN;
387         else if( weight <= FC_WEIGHT_ULTRALIGHT )
388             return WEIGHT_ULTRALIGHT;
389         else if( weight <= FC_WEIGHT_LIGHT )
390             return WEIGHT_LIGHT;
391         else if( weight <= FC_WEIGHT_BOOK )
392             return WEIGHT_SEMILIGHT;
393         else if( weight <= FC_WEIGHT_NORMAL )
394             return WEIGHT_NORMAL;
395         else if( weight <= FC_WEIGHT_MEDIUM )
396             return WEIGHT_MEDIUM;
397         else if( weight <= FC_WEIGHT_SEMIBOLD )
398             return WEIGHT_SEMIBOLD;
399         else if( weight <= FC_WEIGHT_BOLD )
400             return WEIGHT_BOLD;
401         else if( weight <= FC_WEIGHT_ULTRABOLD )
402             return WEIGHT_ULTRABOLD;
403         return WEIGHT_BLACK;
404     }
405 
convertSlant(int slant)406     FontItalic convertSlant(int slant)
407     {
408         // set italic
409         if( slant == FC_SLANT_ITALIC )
410             return ITALIC_NORMAL;
411         else if( slant == FC_SLANT_OBLIQUE )
412             return ITALIC_OBLIQUE;
413         return ITALIC_NONE;
414     }
415 
convertSpacing(int spacing)416     FontPitch convertSpacing(int spacing)
417     {
418         // set pitch
419         if( spacing == FC_MONO || spacing == FC_CHARCELL )
420             return PITCH_FIXED;
421         return PITCH_VARIABLE;
422     }
423 
424     // translation: fontconfig enum -> vcl enum
convertWidth(int width)425     FontWidth convertWidth(int width)
426     {
427         if (width == FC_WIDTH_ULTRACONDENSED)
428             return WIDTH_ULTRA_CONDENSED;
429         else if (width == FC_WIDTH_EXTRACONDENSED)
430             return WIDTH_EXTRA_CONDENSED;
431         else if (width == FC_WIDTH_CONDENSED)
432             return WIDTH_CONDENSED;
433         else if (width == FC_WIDTH_SEMICONDENSED)
434             return WIDTH_SEMI_CONDENSED;
435         else if (width == FC_WIDTH_SEMIEXPANDED)
436             return WIDTH_SEMI_EXPANDED;
437         else if (width == FC_WIDTH_EXPANDED)
438             return WIDTH_EXPANDED;
439         else if (width == FC_WIDTH_EXTRAEXPANDED)
440             return WIDTH_EXTRA_EXPANDED;
441         else if (width == FC_WIDTH_ULTRAEXPANDED)
442             return WIDTH_ULTRA_EXPANDED;
443         return WIDTH_NORMAL;
444     }
445 }
446 
447 //FontConfig doesn't come with a way to remove an element from a FontSet as far
448 //as I can see
lcl_FcFontSetRemove(FcFontSet * pFSet,int i)449 static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
450 {
451     FcPatternDestroy(pFSet->fonts[i]);
452 
453     int nTail = pFSet->nfont - (i + 1);
454     --pFSet->nfont;
455     if (!nTail)
456         return;
457     memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
458 }
459 
460 namespace
461 {
462     // for variable fonts, FC_INDEX has been changed such that the lower half is now the
463     // index of the font within the collection, and the upper half has been repurposed
464     // as the index within the variations
GetCollectionIndex(unsigned int nEntryId)465     unsigned int GetCollectionIndex(unsigned int nEntryId)
466     {
467         return nEntryId & 0xFFFF;
468     }
469 
GetVariationIndex(unsigned int nEntryId)470     unsigned int GetVariationIndex(unsigned int nEntryId)
471     {
472         return nEntryId >> 16;
473     }
474 }
475 
countFontconfigFonts(std::unordered_map<OString,int> & o_rVisitedPaths)476 void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int>& o_rVisitedPaths )
477 {
478     int nFonts = 0;
479     FontCfgWrapper& rWrapper = FontCfgWrapper::get();
480 
481     FcFontSet* pFSet = rWrapper.getFontSet();
482     const bool bMinimalFontset = utl::ConfigManager::IsFuzzing();
483     if( pFSet )
484     {
485         SAL_INFO("vcl.fonts", "found " << pFSet->nfont << " entries in fontconfig fontset");
486         for( int i = 0; i < pFSet->nfont; i++ )
487         {
488             FcChar8* file = nullptr;
489             FcChar8* family = nullptr;
490             FcChar8* style = nullptr;
491             FcChar8* format = nullptr;
492             int slant = 0;
493             int weight = 0;
494             int width = 0;
495             int spacing = 0;
496             int nEntryId = -1;
497             FcBool scalable = false;
498 
499             FcResult eFileRes         = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
500             FcResult eFamilyRes       = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
501             if (bMinimalFontset && strncmp(reinterpret_cast<char*>(family), "Liberation", strlen("Liberation")))
502                 continue;
503             FcResult eStyleRes        = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
504             FcResult eSlantRes        = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
505             FcResult eWeightRes       = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
506             FcResult eWidthRes        = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
507             FcResult eSpacRes         = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
508             FcResult eScalableRes     = FcPatternGetBool(pFSet->fonts[i], FC_SCALABLE, 0, &scalable);
509             FcResult eIndexRes        = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nEntryId);
510             FcResult eFormatRes       = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
511 
512             if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eScalableRes != FcResultMatch )
513                 continue;
514 
515             SAL_INFO(
516                 "vcl.fonts.detail",
517                 "found font \"" << family << "\" in file " << file << ", weight = "
518                 << (eWeightRes == FcResultMatch ? weight : -1) << ", slant = "
519                 << (eSpacRes == FcResultMatch ? slant : -1) << ", style = \""
520                 << (eStyleRes == FcResultMatch ? reinterpret_cast<const char*>(style) : "<nil>")
521                 << "\",  width = " << (eWeightRes == FcResultMatch ? width : -1) << ", spacing = "
522                 << (eSpacRes == FcResultMatch ? spacing : -1) << ", scalable = "
523                 << (eScalableRes == FcResultMatch ? scalable : -1) << ", format "
524                 << (eFormatRes == FcResultMatch
525                     ? reinterpret_cast<const char*>(format) : "<unknown>"));
526 
527 //            OSL_ASSERT(eScalableRes != FcResultMatch || scalable);
528 
529             // only scalable fonts are usable to psprint anyway
530             if( eScalableRes == FcResultMatch && ! scalable )
531                 continue;
532 
533             if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
534             {
535                 SAL_INFO("vcl.fonts.detail", "Ditching " << file << " as duplicate/obsolete");
536                 continue;
537             }
538 
539             // see if this font is already cached
540             // update attributes
541             OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
542             splitPath( aOrgPath, aDir, aBase );
543 
544             o_rVisitedPaths[aDir] = 1;
545 
546             int nDirID = getDirectoryAtom( aDir );
547             SAL_INFO("vcl.fonts.detail", "file " << aBase << " not cached");
548             // not known, analyze font file to get attributes
549             // not described by fontconfig (e.g. alias names, PSName)
550             if (eFormatRes != FcResultMatch)
551                 format = nullptr;
552             std::vector<std::unique_ptr<PrintFont>> aFonts = analyzeFontFile( nDirID, aBase, reinterpret_cast<char*>(format) );
553             if(aFonts.empty())
554             {
555                 SAL_INFO(
556                     "vcl.fonts", "Warning: file \"" << aOrgPath << "\" is unusable to psprint");
557                 //remove font, reuse index
558                 //we want to remove unusable fonts here, in case there is a usable font
559                 //which duplicates the properties of the unusable one
560 
561                 //not removing the unusable font will risk the usable font being rejected
562                 //as a duplicate by isPreviouslyDuplicateOrObsoleted
563                 lcl_FcFontSetRemove(pFSet, i--);
564                 continue;
565             }
566 
567             std::unique_ptr<PrintFont> xUpdate;
568 
569             if (aFonts.size() == 1) // one font
570                 xUpdate = std::move(aFonts.front());
571             else // more than one font
572             {
573                 // a collection entry, get the correct index
574                 if( eIndexRes == FcResultMatch && nEntryId != -1 )
575                 {
576                     int nCollectionEntry = GetCollectionIndex(nEntryId);
577                     for (auto & font : aFonts)
578                     {
579                         if( font->m_nCollectionEntry == nCollectionEntry )
580                         {
581                             xUpdate = std::move(font);
582                             break;
583                         }
584                     }
585                 }
586 
587                 if (xUpdate)
588                 {
589                     // update collection entry
590                     // additional entries will be created in the cache
591                     // if this is a new index (that is if the loop above
592                     // ran to the end of the list)
593                     xUpdate->m_nCollectionEntry = GetCollectionIndex(nEntryId);
594                 }
595                 else
596                 {
597                     SAL_INFO(
598                         "vcl.fonts",
599                         "multiple fonts for file, but no index in fontconfig pattern ! (index res ="
600                         << eIndexRes << " collection entry = " << nEntryId
601                         << "; file will not be used");
602                     // we have found more than one font in this file
603                     // but fontconfig will not tell us which index is meant
604                     // -> something is in disorder, do not use this font
605                 }
606             }
607 
608             if (xUpdate)
609             {
610                 // set family name
611                 if( eWeightRes == FcResultMatch )
612                     xUpdate->m_eWeight = convertWeight(weight);
613                 if( eWidthRes == FcResultMatch )
614                     xUpdate->m_eWidth = convertWidth(width);
615                 if( eSpacRes == FcResultMatch )
616                     xUpdate->m_ePitch = convertSpacing(spacing);
617                 if( eSlantRes == FcResultMatch )
618                     xUpdate->m_eItalic = convertSlant(slant);
619                 if( eStyleRes == FcResultMatch )
620                     xUpdate->m_aStyleName = OStringToOUString( OString( reinterpret_cast<char*>(style) ), RTL_TEXTENCODING_UTF8 );
621                 if( eIndexRes == FcResultMatch )
622                     xUpdate->m_nVariationEntry = GetVariationIndex(nEntryId);
623 
624                 // sort into known fonts
625                 fontID aFont = m_nNextFontID++;
626                 m_aFonts[ aFont ] = std::move(xUpdate);
627                 m_aFontFileToFontID[ aBase ].insert( aFont );
628                 nFonts++;
629                 SAL_INFO("vcl.fonts.detail", "inserted font " << family << " as fontID " << aFont);
630             }
631         }
632     }
633 
634     // how does one get rid of the config ?
635     SAL_INFO("vcl.fonts", "inserted " << nFonts << " fonts from fontconfig");
636 }
637 
deinitFontconfig()638 void PrintFontManager::deinitFontconfig()
639 {
640     FontCfgWrapper::release();
641 }
642 
addFontconfigDir(const OString & rDirName)643 void PrintFontManager::addFontconfigDir( const OString& rDirName )
644 {
645     const char* pDirName = rDirName.getStr();
646     bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), reinterpret_cast<FcChar8 const *>(pDirName) ) == FcTrue);
647 
648     SAL_INFO("vcl.fonts", "FcConfigAppFontAddDir( \"" << pDirName << "\") => " << bDirOk);
649 
650     if( !bDirOk )
651         return;
652 
653     // load dir-specific fc-config file too if available
654     const OString aConfFileName = rDirName + "/fc_local.conf";
655     FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
656     if( pCfgFile )
657     {
658         fclose( pCfgFile);
659         bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
660                         reinterpret_cast<FcChar8 const *>(aConfFileName.getStr()), FcTrue);
661         if( !bCfgOk )
662             fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk );
663     } else {
664         SAL_INFO("vcl.fonts", "cannot open " << aConfFileName);
665     }
666 }
667 
addtopattern(FcPattern * pPattern,FontItalic eItalic,FontWeight eWeight,FontWidth eWidth,FontPitch ePitch)668 static void addtopattern(FcPattern *pPattern,
669     FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
670 {
671     if( eItalic != ITALIC_DONTKNOW )
672     {
673         int nSlant = FC_SLANT_ROMAN;
674         switch( eItalic )
675         {
676             case ITALIC_NORMAL:
677                 nSlant = FC_SLANT_ITALIC;
678                 break;
679             case ITALIC_OBLIQUE:
680                 nSlant = FC_SLANT_OBLIQUE;
681                 break;
682             default:
683                 break;
684         }
685         FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
686     }
687     if( eWeight != WEIGHT_DONTKNOW )
688     {
689         int nWeight = FC_WEIGHT_NORMAL;
690         switch( eWeight )
691         {
692             case WEIGHT_THIN:           nWeight = FC_WEIGHT_THIN;break;
693             case WEIGHT_ULTRALIGHT:     nWeight = FC_WEIGHT_ULTRALIGHT;break;
694             case WEIGHT_LIGHT:          nWeight = FC_WEIGHT_LIGHT;break;
695             case WEIGHT_SEMILIGHT:      nWeight = FC_WEIGHT_BOOK;break;
696             case WEIGHT_NORMAL:         nWeight = FC_WEIGHT_NORMAL;break;
697             case WEIGHT_MEDIUM:         nWeight = FC_WEIGHT_MEDIUM;break;
698             case WEIGHT_SEMIBOLD:       nWeight = FC_WEIGHT_SEMIBOLD;break;
699             case WEIGHT_BOLD:           nWeight = FC_WEIGHT_BOLD;break;
700             case WEIGHT_ULTRABOLD:      nWeight = FC_WEIGHT_ULTRABOLD;break;
701             case WEIGHT_BLACK:          nWeight = FC_WEIGHT_BLACK;break;
702             default:
703                 break;
704         }
705         FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
706     }
707     if( eWidth != WIDTH_DONTKNOW )
708     {
709         int nWidth = FC_WIDTH_NORMAL;
710         switch( eWidth )
711         {
712             case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
713             case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break;
714             case WIDTH_CONDENSED:       nWidth = FC_WIDTH_CONDENSED;break;
715             case WIDTH_SEMI_CONDENSED:  nWidth = FC_WIDTH_SEMICONDENSED;break;
716             case WIDTH_NORMAL:          nWidth = FC_WIDTH_NORMAL;break;
717             case WIDTH_SEMI_EXPANDED:   nWidth = FC_WIDTH_SEMIEXPANDED;break;
718             case WIDTH_EXPANDED:        nWidth = FC_WIDTH_EXPANDED;break;
719             case WIDTH_EXTRA_EXPANDED:  nWidth = FC_WIDTH_EXTRAEXPANDED;break;
720             case WIDTH_ULTRA_EXPANDED:  nWidth = FC_WIDTH_ULTRAEXPANDED;break;
721             default:
722                 break;
723         }
724         FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
725     }
726     if( ePitch != PITCH_DONTKNOW )
727     {
728         int nSpacing = FC_PROPORTIONAL;
729         switch( ePitch )
730         {
731             case PITCH_FIXED:           nSpacing = FC_MONO;break;
732             case PITCH_VARIABLE:        nSpacing = FC_PROPORTIONAL;break;
733             default:
734                 break;
735         }
736         FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
737         if (nSpacing == FC_MONO)
738             FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>("monospace"));
739     }
740 }
741 
742 namespace
743 {
744     //Someday fontconfig will hopefully use bcp47, see fdo#19869
745     //In the meantime try something that will fit to workaround fdo#35118
mapToFontConfigLangTag(const LanguageTag & rLangTag)746     OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
747     {
748 #if defined(FC_VERSION) && (FC_VERSION >= 20492)
749         std::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
750         OString sLangAttrib;
751 
752         sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
753         if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
754         {
755             return sLangAttrib;
756         }
757 
758         sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
759         if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
760         {
761             return sLangAttrib;
762         }
763 
764         OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
765         OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
766 
767         if (!sRegion.isEmpty())
768         {
769             sLangAttrib = sLang + "-" + sRegion;
770             if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
771             {
772                 return sLangAttrib;
773             }
774         }
775 
776         if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLang.getStr())))
777         {
778             return sLang;
779         }
780 
781         return OString();
782 #else
783         OString sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
784         if (sLangAttrib.equalsIgnoreAsciiCase("pa-in"))
785             sLangAttrib = "pa";
786         return sLangAttrib;
787 #endif
788     }
789 
isEmoji(sal_uInt32 nCurrentChar)790     bool isEmoji(sal_uInt32 nCurrentChar)
791     {
792 #if U_ICU_VERSION_MAJOR_NUM >= 57
793         return u_hasBinaryProperty(nCurrentChar, UCHAR_EMOJI);
794 #else
795 	return false;
796 #endif
797     }
798 
799     //returns true if the given code-point couldn't possibly be in rLangTag.
isImpossibleCodePointForLang(const LanguageTag & rLangTag,sal_uInt32 currentChar)800     bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
801     {
802         //a non-default script is set, lets believe it
803         if (rLangTag.hasScript())
804             return false;
805 
806         int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
807         UScriptCode eScript = static_cast<UScriptCode>(script);
808         bool bIsImpossible = false;
809         OUString sLang = rLangTag.getLanguage();
810         switch (eScript)
811         {
812             //http://en.wiktionary.org/wiki/Category:Oriya_script_languages
813             case USCRIPT_ORIYA:
814                 bIsImpossible =
815                     sLang != "or" &&
816                     sLang != "kxv";
817                 break;
818             //http://en.wiktionary.org/wiki/Category:Telugu_script_languages
819             case USCRIPT_TELUGU:
820                 bIsImpossible =
821                     sLang != "te" &&
822                     sLang != "gon" &&
823                     sLang != "kfc";
824                 break;
825             //http://en.wiktionary.org/wiki/Category:Bengali_script_languages
826             case USCRIPT_BENGALI:
827                 bIsImpossible =
828                     sLang != "bn" &&
829                     sLang != "as" &&
830                     sLang != "bpy" &&
831                     sLang != "ctg" &&
832                     sLang != "sa";
833                 break;
834             default:
835                 break;
836         }
837         SAL_WARN_IF(bIsImpossible, "vcl.fonts", "In glyph fallback throwing away the language property of "
838             << sLang << " because the detected script for '0x"
839             << OUString::number(currentChar, 16)
840             << "' is " << uscript_getName(eScript)
841             << " and that language doesn't make sense. Autodetecting instead.");
842         return bIsImpossible;
843     }
844 
getExemplarLangTagForCodePoint(sal_uInt32 currentChar)845     OUString getExemplarLangTagForCodePoint(sal_uInt32 currentChar)
846     {
847         if (isEmoji(currentChar))
848             return "und-zsye";
849         int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
850         UScriptCode eScript = static_cast<UScriptCode>(script);
851         OStringBuffer aBuf(unicode::getExemplarLanguageForUScriptCode(eScript));
852         if (const char* pScriptCode = uscript_getShortName(eScript))
853             aBuf.append('-').append(pScriptCode);
854         return OStringToOUString(aBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8);
855     }
856 }
857 
IMPL_LINK_NOARG(PrintFontManager,autoInstallFontLangSupport,Timer *,void)858 IMPL_LINK_NOARG(PrintFontManager, autoInstallFontLangSupport, Timer *, void)
859 {
860     try
861     {
862         using namespace org::freedesktop::PackageKit;
863         css::uno::Reference<XSyncDbusSessionHelper> xSyncDbusSessionHelper(SyncDbusSessionHelper::create(comphelper::getProcessComponentContext()));
864         xSyncDbusSessionHelper->InstallFontconfigResources(comphelper::containerToSequence(m_aCurrentRequests), "hide-finished");
865     }
866     catch (const css::uno::Exception&)
867     {
868         TOOLS_INFO_EXCEPTION("vcl.fonts", "InstallFontconfigResources problem");
869         // Disable this method from now on. It's simply not available on some systems
870         // and leads to an error dialog being shown each time this is called tdf#104883
871         std::shared_ptr<comphelper::ConfigurationChanges> batch( comphelper::ConfigurationChanges::create() );
872         officecfg::Office::Common::PackageKit::EnableFontInstallation::set(false, batch);
873         batch->commit();
874     }
875 
876     m_aCurrentRequests.clear();
877 }
878 
Substitute(FontSelectPattern & rPattern,OUString & rMissingCodes)879 void PrintFontManager::Substitute(FontSelectPattern &rPattern, OUString& rMissingCodes)
880 {
881     FontCfgWrapper& rWrapper = FontCfgWrapper::get();
882 
883     // build pattern argument for fontconfig query
884     FcPattern* pPattern = FcPatternCreate();
885 
886     // Prefer scalable fonts
887     FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
888 
889     const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
890     const FcChar8* pTargetNameUtf8 = reinterpret_cast<FcChar8 const *>(aTargetName.getStr());
891     FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
892 
893     LanguageTag aLangTag(rPattern.meLanguage);
894     OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
895 
896     // Add required Unicode characters, if any
897     if ( !rMissingCodes.isEmpty() )
898     {
899         FcCharSet *codePoints = FcCharSetCreate();
900         for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
901         {
902             // also handle unicode surrogates
903             const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
904             FcCharSetAddChar( codePoints, nCode );
905             //if the codepoint is impossible for this lang tag, then clear it
906             //and autodetect something useful
907             if (!aLangAttrib.isEmpty() && (isImpossibleCodePointForLang(aLangTag, nCode) || isEmoji(nCode)))
908                 aLangAttrib.clear();
909             //#i105784#/rhbz#527719  improve selection of fallback font
910             if (aLangAttrib.isEmpty())
911             {
912                 aLangTag.reset(getExemplarLangTagForCodePoint(nCode));
913                 aLangAttrib = mapToFontConfigLangTag(aLangTag);
914             }
915         }
916         FcPatternAddCharSet(pPattern, FC_CHARSET, codePoints);
917         FcCharSetDestroy(codePoints);
918     }
919 
920     if (!aLangAttrib.isEmpty())
921         FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
922 
923     addtopattern(pPattern, rPattern.GetItalic(), rPattern.GetWeight(),
924         rPattern.GetWidthType(), rPattern.GetPitch());
925 
926     // query fontconfig for a substitute
927     FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
928     FcDefaultSubstitute(pPattern);
929 
930     // process the result of the fontconfig query
931     FcResult eResult = FcResultNoMatch;
932     FcFontSet* pFontSet = rWrapper.getFontSet();
933     FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
934     FcPatternDestroy( pPattern );
935 
936     FcFontSet*  pSet = nullptr;
937     if( pResult )
938     {
939         pSet = FcFontSetCreate();
940         // info: destroying the pSet destroys pResult implicitly
941         // since pResult was "added" to pSet
942         FcFontSetAdd( pSet, pResult );
943     }
944 
945     if( pSet )
946     {
947         if( pSet->nfont > 0 )
948         {
949             bool bRet = false;
950 
951             //extract the closest match
952             FcChar8* file = nullptr;
953             FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
954             int nEntryId = 0;
955             FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nEntryId);
956             if (eIndexRes != FcResultMatch)
957                 nEntryId = 0;
958             if( eFileRes == FcResultMatch )
959             {
960                 OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
961                 splitPath( aOrgPath, aDir, aBase );
962                 int nDirID = getDirectoryAtom( aDir );
963                 fontID aFont = findFontFileID(nDirID, aBase, GetCollectionIndex(nEntryId), GetVariationIndex(nEntryId));
964                 if( aFont > 0 )
965                 {
966                     FastPrintFontInfo aInfo;
967                     bRet = getFontFastInfo( aFont, aInfo );
968                     rPattern.maSearchName = aInfo.m_aFamilyName;
969                 }
970             }
971 
972             SAL_WARN_IF(!bRet, "vcl.fonts", "no FC_FILE found, falling back to name search");
973 
974             if (!bRet)
975             {
976                 FcChar8* family = nullptr;
977                 FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
978 
979                 // get the family name
980                 if( eFamilyRes == FcResultMatch )
981                 {
982                     OString sFamily(reinterpret_cast<char*>(family));
983                     std::unordered_map< OString, OString >::const_iterator aI =
984                         rWrapper.m_aFontNameToLocalized.find(sFamily);
985                     if (aI != rWrapper.m_aFontNameToLocalized.end())
986                         sFamily = aI->second;
987                     rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
988                     bRet = true;
989                 }
990             }
991 
992             if (bRet)
993             {
994                 int val = 0;
995                 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
996                     rPattern.SetWeight( convertWeight(val) );
997                 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
998                     rPattern.SetItalic( convertSlant(val) );
999                 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
1000                     rPattern.SetPitch ( convertSpacing(val) );
1001                 if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
1002                     rPattern.SetWidthType ( convertWidth(val) );
1003                 FcBool bEmbolden;
1004                 if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
1005                     rPattern.mbEmbolden = bEmbolden;
1006                 FcMatrix *pMatrix = nullptr;
1007                 if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
1008                 {
1009                     rPattern.maItalicMatrix.xx = pMatrix->xx;
1010                     rPattern.maItalicMatrix.xy = pMatrix->xy;
1011                     rPattern.maItalicMatrix.yx = pMatrix->yx;
1012                     rPattern.maItalicMatrix.yy = pMatrix->yy;
1013                 }
1014             }
1015 
1016             // update rMissingCodes by removing resolved code points
1017             if( !rMissingCodes.isEmpty() )
1018             {
1019                 std::unique_ptr<sal_uInt32[]> const pRemainingCodes(new sal_uInt32[rMissingCodes.getLength()]);
1020                 int nRemainingLen = 0;
1021                 FcCharSet* codePoints;
1022                 if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &codePoints))
1023                 {
1024                     for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1025                     {
1026                         // also handle surrogates
1027                         const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
1028                         if (FcCharSetHasChar(codePoints, nCode) != FcTrue)
1029                             pRemainingCodes[ nRemainingLen++ ] = nCode;
1030                     }
1031                 }
1032                 OUString sStillMissing(pRemainingCodes.get(), nRemainingLen);
1033                 if (!Application::IsHeadlessModeEnabled() && officecfg::Office::Common::PackageKit::EnableFontInstallation::get())
1034                 {
1035                     if (sStillMissing == rMissingCodes) //replaced nothing
1036                     {
1037                         //It'd be better if we could ask packagekit using the
1038                         //missing codepoints or some such rather than using
1039                         //"language" as a proxy to how fontconfig considers
1040                         //scripts to default to a given language.
1041                         for (sal_Int32 i = 0; i < nRemainingLen; ++i)
1042                         {
1043                             LanguageTag aOurTag(getExemplarLangTagForCodePoint(pRemainingCodes[i]));
1044                             OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8);
1045                             if (!m_aPreviousLangSupportRequests.insert(sTag).second)
1046                                 continue;
1047                             sTag = mapToFontConfigLangTag(aOurTag);
1048                             if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
1049                             {
1050                                 OString sReq = OStringLiteral(":lang=") + sTag;
1051                                 m_aCurrentRequests.push_back(OUString::fromUtf8(sReq));
1052                                 m_aPreviousLangSupportRequests.insert(sTag);
1053                             }
1054                         }
1055                     }
1056                     if (!m_aCurrentRequests.empty())
1057                     {
1058                         m_aFontInstallerTimer.Stop();
1059                         m_aFontInstallerTimer.Start();
1060                     }
1061                 }
1062                 rMissingCodes = sStillMissing;
1063             }
1064         }
1065 
1066         FcFontSetDestroy( pSet );
1067     }
1068 
1069     SAL_INFO("vcl.fonts", "PrintFontManager::Substitute: replacing missing font: '"
1070                               << rPattern.maTargetName << "' with '" << rPattern.maSearchName
1071                               << "'");
1072 }
1073 
~FontConfigFontOptions()1074 FontConfigFontOptions::~FontConfigFontOptions()
1075 {
1076     FcPatternDestroy(mpPattern);
1077 }
1078 
GetPattern() const1079 FcPattern *FontConfigFontOptions::GetPattern() const
1080 {
1081     return mpPattern;
1082 }
1083 
SyncPattern(const OString & rFileName,sal_uInt32 nIndex,sal_uInt32 nVariation,bool bEmbolden)1084 void FontConfigFontOptions::SyncPattern(const OString& rFileName, sal_uInt32 nIndex, sal_uInt32 nVariation, bool bEmbolden)
1085 {
1086     FcPatternDel(mpPattern, FC_FILE);
1087     FcPatternAddString(mpPattern, FC_FILE, reinterpret_cast<FcChar8 const *>(rFileName.getStr()));
1088     FcPatternDel(mpPattern, FC_INDEX);
1089     sal_uInt32 nFcIndex = (nVariation << 16) | nIndex;
1090     FcPatternAddInteger(mpPattern, FC_INDEX, nFcIndex);
1091     FcPatternDel(mpPattern, FC_EMBOLDEN);
1092     FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse);
1093 }
1094 
getFontOptions(const FastPrintFontInfo & rInfo,int nSize)1095 std::unique_ptr<FontConfigFontOptions> PrintFontManager::getFontOptions(const FastPrintFontInfo& rInfo, int nSize)
1096 {
1097     FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1098 
1099     std::unique_ptr<FontConfigFontOptions> pOptions;
1100     FcConfig* pConfig = FcConfigGetCurrent();
1101     FcPattern* pPattern = FcPatternCreate();
1102 
1103     OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1104 
1105     std::unordered_map< OString, OString >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
1106     if (aI != rWrapper.m_aLocalizedToCanonical.end())
1107         sFamily = aI->second;
1108     if( !sFamily.isEmpty() )
1109         FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(sFamily.getStr()));
1110 
1111     addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1112     FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);
1113 
1114     int hintstyle = FC_HINT_FULL;
1115 
1116     FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1117     FontConfigFontOptions::cairo_font_options_substitute(pPattern);
1118     FcDefaultSubstitute(pPattern);
1119 
1120     FcResult eResult = FcResultNoMatch;
1121     FcFontSet* pFontSet = rWrapper.getFontSet();
1122     FcPattern* pResult = FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult );
1123     if( pResult )
1124     {
1125         (void) FcPatternGetInteger(pResult,
1126             FC_HINT_STYLE, 0, &hintstyle);
1127 
1128         pOptions.reset(new FontConfigFontOptions(pResult));
1129     }
1130 
1131     // cleanup
1132     FcPatternDestroy( pPattern );
1133 
1134     return pOptions;
1135 }
1136 
matchFont(FastPrintFontInfo & rInfo,const css::lang::Locale & rLocale)1137 void PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const css::lang::Locale& rLocale )
1138 {
1139     FontCfgWrapper& rWrapper = FontCfgWrapper::get();
1140 
1141     FcConfig* pConfig = FcConfigGetCurrent();
1142     FcPattern* pPattern = FcPatternCreate();
1143 
1144     // populate pattern with font characteristics
1145     const LanguageTag aLangTag(rLocale);
1146     const OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
1147     if (!aLangAttrib.isEmpty())
1148         FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
1149 
1150     OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
1151     if( !aFamily.isEmpty() )
1152         FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(aFamily.getStr()));
1153 
1154     addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
1155 
1156     FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
1157     FcDefaultSubstitute(pPattern);
1158     FcResult eResult = FcResultNoMatch;
1159     FcFontSet *pFontSet = rWrapper.getFontSet();
1160     FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult);
1161     if( pResult )
1162     {
1163         FcFontSet* pSet = FcFontSetCreate();
1164         FcFontSetAdd( pSet, pResult );
1165         if( pSet->nfont > 0 )
1166         {
1167             //extract the closest match
1168             FcChar8* file = nullptr;
1169             FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1170             int nEntryId = 0;
1171             FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nEntryId);
1172             if (eIndexRes != FcResultMatch)
1173                 nEntryId = 0;
1174             if( eFileRes == FcResultMatch )
1175             {
1176                 OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
1177                 splitPath( aOrgPath, aDir, aBase );
1178                 int nDirID = getDirectoryAtom( aDir );
1179                 fontID aFont = findFontFileID(nDirID, aBase,
1180                                               GetCollectionIndex(nEntryId),
1181                                               GetVariationIndex(nEntryId));
1182                 if( aFont > 0 )
1183                     getFontFastInfo( aFont, rInfo );
1184             }
1185         }
1186         // info: destroying the pSet destroys pResult implicitly
1187         // since pResult was "added" to pSet
1188         FcFontSetDestroy( pSet );
1189     }
1190 
1191     // cleanup
1192     FcPatternDestroy( pPattern );
1193 }
1194 
1195 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
1196