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