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