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