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 137 : FontCfgWrapper::FontCfgWrapper()
97 : :
98 : m_pOutlineSet( NULL ),
99 137 : m_pLanguageTag( NULL )
100 : {
101 137 : FcInit();
102 137 : }
103 :
104 274 : void FontCfgWrapper::addFontSet( FcSetName eSetName )
105 : {
106 : /*
107 : add only acceptable outlined fonts to our config,
108 : for future fontconfig use
109 : */
110 274 : FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
111 274 : if( !pOrig )
112 274 : return;
113 :
114 : // filter the font sets to remove obsolete faces
115 30962 : for( int i = 0; i < pOrig->nfont; ++i )
116 : {
117 30688 : FcPattern* pPattern = pOrig->fonts[i];
118 : // #i115131# ignore non-outline fonts
119 30688 : FcBool bOutline = FcFalse;
120 30688 : FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
121 30688 : if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
122 0 : continue;
123 30688 : FcPatternReference( pPattern );
124 30688 : FcFontSetAdd( m_pOutlineSet, pPattern );
125 : }
126 :
127 : // TODO?: FcFontSetDestroy( pOrig );
128 : }
129 :
130 : namespace
131 : {
132 388395 : int compareFontNames(const FcPattern *a, const FcPattern *b)
133 : {
134 388395 : FcChar8 *pNameA=NULL, *pNameB=NULL;
135 :
136 388395 : bool nHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
137 388395 : bool nHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
138 :
139 388395 : if (nHaveA && nHaveB)
140 388395 : 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 277836 : bool operator()(const FcPattern *a, const FcPattern *b)
151 : {
152 277836 : int comp = compareFontNames(a, b);
153 277836 : if (comp != 0)
154 238380 : return comp < 0;
155 :
156 39456 : int nVersionA=0, nVersionB=0;
157 :
158 39456 : bool nHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
159 39456 : bool nHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;
160 :
161 39456 : if (nHaveA && nHaveB)
162 39456 : 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 30688 : bool isPreviouslyDuplicateOrObsoleted(FcFontSet *pFSet, int i)
175 : {
176 30688 : const FcPattern *a = pFSet->fonts[i];
177 :
178 30688 : FcPattern* pTestPatternA = FcPatternDuplicate(a);
179 30688 : FcPatternDel(pTestPatternA, FC_FILE);
180 30688 : FcPatternDel(pTestPatternA, FC_CHARSET);
181 30688 : FcPatternDel(pTestPatternA, FC_CAPABILITY);
182 30688 : FcPatternDel(pTestPatternA, FC_FONTVERSION);
183 30688 : FcPatternDel(pTestPatternA, FC_LANG);
184 :
185 30688 : bool bIsDup(false);
186 :
187 : // fdo#66715: loop for case of several font files for same font
188 112340 : for (int j = i - 1; 0 <= j && !bIsDup; --j)
189 : {
190 110559 : const FcPattern *b = pFSet->fonts[j];
191 :
192 110559 : if (compareFontNames(a, b) != 0)
193 28907 : break;
194 :
195 81652 : FcPattern* pTestPatternB = FcPatternDuplicate(b);
196 81652 : FcPatternDel(pTestPatternB, FC_FILE);
197 81652 : FcPatternDel(pTestPatternB, FC_CHARSET);
198 81652 : FcPatternDel(pTestPatternB, FC_CAPABILITY);
199 81652 : FcPatternDel(pTestPatternB, FC_FONTVERSION);
200 81652 : FcPatternDel(pTestPatternB, FC_LANG);
201 :
202 81652 : bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);
203 :
204 81652 : FcPatternDestroy(pTestPatternB);
205 : }
206 :
207 30688 : FcPatternDestroy(pTestPatternA);
208 :
209 30688 : return bIsDup;
210 : }
211 : }
212 :
213 13049 : FcFontSet* FontCfgWrapper::getFontSet()
214 : {
215 13049 : if( !m_pOutlineSet )
216 : {
217 137 : m_pOutlineSet = FcFontSetCreate();
218 137 : addFontSet( FcSetSystem );
219 137 : if( FcGetVersion() > 20400 ) // #i85462# prevent crashes
220 137 : addFontSet( FcSetApplication );
221 :
222 137 : ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont());
223 : }
224 :
225 13049 : return m_pOutlineSet;
226 : }
227 :
228 274 : FontCfgWrapper::~FontCfgWrapper()
229 : {
230 137 : clear();
231 : //To-Do: get gtk vclplug smoketest to pass
232 : //FcFini();
233 137 : }
234 :
235 : static FontCfgWrapper* pOneInstance = NULL;
236 :
237 13186 : FontCfgWrapper& FontCfgWrapper::get()
238 : {
239 13186 : if( ! pOneInstance )
240 137 : pOneInstance = new FontCfgWrapper();
241 13186 : return *pOneInstance;
242 : }
243 :
244 137 : void FontCfgWrapper::release()
245 : {
246 137 : if( pOneInstance )
247 : {
248 137 : delete pOneInstance;
249 137 : pOneInstance = NULL;
250 : }
251 137 : }
252 :
253 : namespace
254 : {
255 : class localizedsorter
256 : {
257 : public:
258 47128 : localizedsorter() {};
259 : FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);
260 : };
261 :
262 47128 : FcChar8* localizedsorter::bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
263 : {
264 47128 : 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 47128 : OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
269 94256 : OString sFullMatch = sLangMatch;
270 47128 : sFullMatch += OString('-');
271 47128 : sFullMatch += OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
272 :
273 47128 : std::vector<lang_and_element>::const_iterator aEnd = elements.end();
274 47128 : bool alreadyclosematch = false;
275 47128 : bool found_fallback_englishname = false;
276 101106 : for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter )
277 : {
278 53978 : const char *pLang = (const char*)aIter->first;
279 53978 : 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 53978 : else if( alreadyclosematch )
286 : {
287 : // current candidate matches lang of lang-TERRITORY
288 : // override candidate only if there is a full match
289 6439 : continue;
290 : }
291 47539 : else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0)
292 : {
293 : // just the language matches
294 46717 : candidate = aIter->second;
295 46717 : alreadyclosematch = true;
296 : }
297 822 : 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 822 : 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 94256 : 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 23564 : void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
317 : const std::vector< lang_and_element > &lang_and_elements)
318 : {
319 23564 : std::vector<lang_and_element>::const_iterator aEnd = lang_and_elements.end();
320 51238 : for (std::vector<lang_and_element>::const_iterator aIter = lang_and_elements.begin(); aIter != aEnd; ++aIter)
321 : {
322 27674 : const char *candidate = (const char*)(aIter->second);
323 27674 : if (rtl_str_compare(candidate, (const char*)bestfontname) != 0)
324 4110 : m_aFontNameToLocalized[OString(candidate)] = OString((const char*)bestfontname);
325 : }
326 23564 : if (rtl_str_compare((const char*)origfontname, (const char*)bestfontname) != 0)
327 411 : m_aLocalizedToCanonical[OString((const char*)bestfontname)] = OString((const char*)origfontname);
328 23564 : }
329 :
330 61376 : 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 61376 : FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
335 61376 : *element = origelement;
336 :
337 61376 : if( eElementRes == FcResultMatch)
338 : {
339 61376 : FcChar8* elementlang = NULL;
340 61376 : if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
341 : {
342 47128 : std::vector< lang_and_element > lang_and_elements;
343 47128 : lang_and_elements.push_back(lang_and_element(elementlang, *element));
344 47128 : int k = 1;
345 : while (true)
346 : {
347 53978 : if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
348 47128 : break;
349 6850 : if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
350 0 : break;
351 6850 : lang_and_elements.push_back(lang_and_element(elementlang, *element));
352 6850 : ++k;
353 : }
354 :
355 : //possible to-do, sort by UILocale instead of process locale
356 47128 : if (!m_pLanguageTag)
357 : {
358 137 : rtl_Locale* pLoc = NULL;
359 137 : osl_getProcessLocale(&pLoc);
360 137 : m_pLanguageTag = new LanguageTag(*pLoc);
361 : }
362 47128 : *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 47128 : if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
366 23564 : cacheLocalizedFontNames(origelement, *element, lang_and_elements);
367 : }
368 : }
369 :
370 61376 : return eElementRes;
371 : }
372 :
373 274 : void FontCfgWrapper::clear()
374 : {
375 274 : m_aFontNameToLocalized.clear();
376 274 : m_aLocalizedToCanonical.clear();
377 274 : if( m_pOutlineSet )
378 : {
379 137 : FcFontSetDestroy( m_pOutlineSet );
380 137 : m_pOutlineSet = NULL;
381 : }
382 274 : delete m_pLanguageTag;
383 274 : m_pLanguageTag = NULL;
384 274 : }
385 :
386 : /*
387 : * PrintFontManager::initFontconfig
388 : */
389 137 : void PrintFontManager::initFontconfig()
390 : {
391 137 : FontCfgWrapper& rWrapper = FontCfgWrapper::get();
392 137 : rWrapper.clear();
393 137 : }
394 :
395 : namespace
396 : {
397 41682 : FontWeight convertWeight(int weight)
398 : {
399 : // set weight
400 41682 : if( weight <= FC_WEIGHT_THIN )
401 0 : return WEIGHT_THIN;
402 41682 : else if( weight <= FC_WEIGHT_ULTRALIGHT )
403 377 : return WEIGHT_ULTRALIGHT;
404 41305 : else if( weight <= FC_WEIGHT_LIGHT )
405 548 : return WEIGHT_LIGHT;
406 40757 : else if( weight <= FC_WEIGHT_BOOK )
407 274 : return WEIGHT_SEMILIGHT;
408 40483 : else if( weight <= FC_WEIGHT_NORMAL )
409 23497 : return WEIGHT_NORMAL;
410 16986 : else if( weight <= FC_WEIGHT_MEDIUM )
411 2098 : return WEIGHT_MEDIUM;
412 14888 : else if( weight <= FC_WEIGHT_SEMIBOLD )
413 685 : return WEIGHT_SEMIBOLD;
414 14203 : else if( weight <= FC_WEIGHT_BOLD )
415 14203 : return WEIGHT_BOLD;
416 0 : else if( weight <= FC_WEIGHT_ULTRABOLD )
417 0 : return WEIGHT_ULTRABOLD;
418 0 : return WEIGHT_BLACK;
419 : }
420 :
421 41682 : FontItalic convertSlant(int slant)
422 : {
423 : // set italic
424 41682 : if( slant == FC_SLANT_ITALIC )
425 8855 : return ITALIC_NORMAL;
426 32827 : else if( slant == FC_SLANT_OBLIQUE )
427 2783 : return ITALIC_OBLIQUE;
428 30044 : return ITALIC_NONE;
429 : }
430 :
431 12168 : FontPitch convertSpacing(int spacing)
432 : {
433 : // set pitch
434 12168 : if( spacing == FC_MONO || spacing == FC_CHARCELL )
435 3638 : return PITCH_FIXED;
436 8530 : return PITCH_VARIABLE;
437 : }
438 :
439 : // translation: fontconfig enum -> vcl enum
440 41682 : FontWidth convertWidth(int width)
441 : {
442 41682 : if (width == FC_WIDTH_ULTRACONDENSED)
443 0 : return WIDTH_ULTRA_CONDENSED;
444 41682 : else if (width == FC_WIDTH_EXTRACONDENSED)
445 0 : return WIDTH_EXTRA_CONDENSED;
446 41682 : else if (width == FC_WIDTH_CONDENSED)
447 1392 : return WIDTH_CONDENSED;
448 40290 : else if (width == FC_WIDTH_SEMICONDENSED)
449 2192 : return WIDTH_SEMI_CONDENSED;
450 38098 : else if (width == FC_WIDTH_SEMIEXPANDED)
451 0 : return WIDTH_SEMI_EXPANDED;
452 38098 : else if (width == FC_WIDTH_EXPANDED)
453 0 : return WIDTH_EXPANDED;
454 38098 : else if (width == FC_WIDTH_EXTRAEXPANDED)
455 0 : return WIDTH_EXTRA_EXPANDED;
456 38098 : else if (width == FC_WIDTH_ULTRAEXPANDED)
457 0 : return WIDTH_ULTRA_EXPANDED;
458 38098 : 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 274 : static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
465 : {
466 274 : FcPatternDestroy(pFSet->fonts[i]);
467 :
468 274 : int nTail = pFSet->nfont - (i + 1);
469 274 : --pFSet->nfont;
470 274 : if (!nTail)
471 274 : return;
472 274 : memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
473 : }
474 :
475 137 : 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 137 : FontCfgWrapper& rWrapper = FontCfgWrapper::get();
481 :
482 137 : FcFontSet* pFSet = rWrapper.getFontSet();
483 137 : if( pFSet )
484 : {
485 : #if OSL_DEBUG_LEVEL > 1
486 : fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont );
487 : #endif
488 30825 : for( int i = 0; i < pFSet->nfont; i++ )
489 : {
490 30688 : FcChar8* file = NULL;
491 30688 : FcChar8* family = NULL;
492 30688 : FcChar8* style = NULL;
493 30688 : FcChar8* format = NULL;
494 30688 : int slant = 0;
495 30688 : int weight = 0;
496 30688 : int width = 0;
497 30688 : int spacing = 0;
498 30688 : int nCollectionEntry = -1;
499 30688 : FcBool outline = false;
500 :
501 30688 : FcResult eFileRes = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
502 30688 : FcResult eFamilyRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
503 30688 : FcResult eStyleRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
504 30688 : FcResult eSlantRes = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
505 30688 : FcResult eWeightRes = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
506 30688 : FcResult eWidthRes = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
507 30688 : FcResult eSpacRes = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
508 30688 : FcResult eOutRes = FcPatternGetBool(pFSet->fonts[i], FC_OUTLINE, 0, &outline);
509 30688 : FcResult eIndexRes = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry);
510 30688 : FcResult eFormatRes = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
511 :
512 30688 : if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch )
513 1918 : 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 30688 : if( eOutRes == FcResultMatch && ! outline )
534 0 : continue;
535 :
536 30688 : if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
537 : {
538 : #if OSL_DEBUG_LEVEL > 2
539 : fprintf(stderr, "Ditching %s as duplicate/obsolete\n", file);
540 : #endif
541 1644 : continue;
542 : }
543 :
544 : // see if this font is already cached
545 : // update attributes
546 29044 : std::list< PrintFont* > aFonts;
547 57814 : OString aDir, aBase, aOrgPath( (sal_Char*)file );
548 29044 : splitPath( aOrgPath, aDir, aBase );
549 :
550 29044 : o_rVisitedPaths[aDir] = 1;
551 :
552 29044 : int nDirID = getDirectoryAtom( aDir, true );
553 29044 : 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 7654 : if (eFormatRes != FcResultMatch)
561 0 : format = NULL;
562 7654 : 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 29044 : 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 274 : lcl_FcFontSetRemove(pFSet, i--);
577 274 : continue;
578 : }
579 :
580 28770 : int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( (sal_Char*)family ), RTL_TEXTENCODING_UTF8 ), true );
581 28770 : PrintFont* pUpdate = aFonts.front();
582 28770 : std::list<PrintFont*>::const_iterator second_font = aFonts.begin();
583 28770 : ++second_font;
584 28770 : if( second_font != aFonts.end() ) // more than one font
585 : {
586 : // a collection entry, get the correct index
587 959 : if( eIndexRes == FcResultMatch && nCollectionEntry != -1 )
588 : {
589 2192 : for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
590 : {
591 4384 : if( (*it)->m_eType == fonttype::TrueType &&
592 2192 : static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry )
593 : {
594 959 : pUpdate = *it;
595 959 : 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 959 : if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here
603 959 : 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 28770 : if( pUpdate )
618 : {
619 : // set family name
620 28770 : if( pUpdate->m_nFamilyName != nFamilyName )
621 : {
622 : }
623 28770 : if( eWeightRes == FcResultMatch )
624 28770 : pUpdate->m_eWeight = convertWeight(weight);
625 28770 : if( eWidthRes == FcResultMatch )
626 28770 : pUpdate->m_eWidth = convertWidth(width);
627 28770 : if( eSpacRes == FcResultMatch )
628 3425 : pUpdate->m_ePitch = convertSpacing(spacing);
629 28770 : if( eSlantRes == FcResultMatch )
630 28770 : pUpdate->m_eItalic = convertSlant(slant);
631 28770 : if( eStyleRes == FcResultMatch )
632 : {
633 28770 : pUpdate->m_aStyleName = OStringToOUString( OString( (sal_Char*)style ), RTL_TEXTENCODING_UTF8 );
634 : }
635 :
636 : // update font cache
637 28770 : m_pFontCache->updateFontCacheEntry( pUpdate, false );
638 : // sort into known fonts
639 28770 : fontID aFont = m_nNextFontID++;
640 28770 : m_aFonts[ aFont ] = pUpdate;
641 28770 : 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 60006 : for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
651 : {
652 31236 : if( *it != pUpdate )
653 : {
654 2466 : m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item
655 2466 : delete *it;
656 : }
657 : }
658 28770 : }
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 137 : }
666 :
667 137 : void PrintFontManager::deinitFontconfig()
668 : {
669 137 : FontCfgWrapper::release();
670 137 : }
671 :
672 411 : bool PrintFontManager::addFontconfigDir( const OString& rDirName )
673 : {
674 : // workaround for a stability problems in older FC versions
675 : // when handling application specific fonts
676 411 : const int nVersion = FcGetVersion();
677 411 : if( nVersion <= 20400 )
678 0 : return false;
679 411 : const char* pDirName = (const char*)rDirName.getStr();
680 411 : 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 411 : if( !bDirOk )
687 0 : return false;
688 :
689 : // load dir-specific fc-config file too if available
690 411 : const OString aConfFileName = rDirName + "/fc_local.conf";
691 411 : FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
692 411 : if( pCfgFile )
693 : {
694 137 : fclose( pCfgFile);
695 : bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
696 137 : (FcChar8*)aConfFileName.getStr(), FcTrue);
697 137 : if( !bCfgOk )
698 0 : fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk );
699 : }
700 :
701 411 : return true;
702 : }
703 :
704 12912 : static void addtopattern(FcPattern *pPattern,
705 : FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
706 : {
707 12912 : if( eItalic != ITALIC_DONTKNOW )
708 : {
709 12912 : int nSlant = FC_SLANT_ROMAN;
710 12912 : switch( eItalic )
711 : {
712 : case ITALIC_NORMAL:
713 612 : nSlant = FC_SLANT_ITALIC;
714 612 : break;
715 : case ITALIC_OBLIQUE:
716 107 : nSlant = FC_SLANT_OBLIQUE;
717 107 : break;
718 : default:
719 12193 : break;
720 : }
721 12912 : FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
722 : }
723 12912 : if( eWeight != WEIGHT_DONTKNOW )
724 : {
725 12634 : int nWeight = FC_WEIGHT_NORMAL;
726 12634 : switch( eWeight )
727 : {
728 322 : case WEIGHT_THIN: nWeight = FC_WEIGHT_THIN;break;
729 23 : case WEIGHT_ULTRALIGHT: nWeight = FC_WEIGHT_ULTRALIGHT;break;
730 61 : case WEIGHT_LIGHT: nWeight = FC_WEIGHT_LIGHT;break;
731 13 : case WEIGHT_SEMILIGHT: nWeight = FC_WEIGHT_BOOK;break;
732 9060 : 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 2940 : case WEIGHT_BOLD: nWeight = FC_WEIGHT_BOLD;break;
736 147 : case WEIGHT_ULTRABOLD: nWeight = FC_WEIGHT_ULTRABOLD;break;
737 68 : case WEIGHT_BLACK: nWeight = FC_WEIGHT_BLACK;break;
738 : default:
739 0 : break;
740 : }
741 12634 : FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
742 : }
743 12912 : if( eWidth != WIDTH_DONTKNOW )
744 : {
745 33 : int nWidth = FC_WIDTH_NORMAL;
746 33 : 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 33 : 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 33 : FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
761 : }
762 12912 : if( ePitch != PITCH_DONTKNOW )
763 : {
764 8734 : int nSpacing = FC_PROPORTIONAL;
765 8734 : switch( ePitch )
766 : {
767 41 : case PITCH_FIXED: nSpacing = FC_MONO;break;
768 8012 : case PITCH_VARIABLE: nSpacing = FC_PROPORTIONAL;break;
769 : default:
770 681 : break;
771 : }
772 8734 : FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
773 8734 : if (nSpacing == FC_MONO)
774 41 : FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)"monospace");
775 : }
776 12912 : }
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 12912 : OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
783 : {
784 : #if defined(FC_VERSION) && (FC_VERSION >= 20492)
785 12912 : boost::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
786 25824 : OString sLangAttrib;
787 :
788 12912 : sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
789 12912 : if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLangAttrib.getStr()))
790 : {
791 1282 : return sLangAttrib;
792 : }
793 :
794 11630 : sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
795 11630 : if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLangAttrib.getStr()))
796 : {
797 11534 : return sLangAttrib;
798 : }
799 :
800 192 : OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
801 192 : OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
802 :
803 96 : 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 96 : if (FcStrSetMember(xLangSet.get(), (const FcChar8*)sLang.getStr()))
813 : {
814 0 : return sLang;
815 : }
816 :
817 13008 : 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 68 : bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
828 : {
829 : //a non-default script is set, lets believe it
830 68 : if (rLangTag.hasScript())
831 0 : return false;
832 :
833 68 : int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
834 68 : UScriptCode eScript = static_cast<UScriptCode>(script);
835 68 : bool bIsImpossible = false;
836 68 : OUString sLang = rLangTag.getLanguage();
837 68 : 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 68 : 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 68 : 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 12912 : bool PrintFontManager::Substitute( FontSelectPattern &rPattern, OUString& rMissingCodes )
952 : {
953 12912 : bool bRet = false;
954 :
955 12912 : FontCfgWrapper& rWrapper = FontCfgWrapper::get();
956 :
957 : // build pattern argument for fontconfig query
958 12912 : FcPattern* pPattern = FcPatternCreate();
959 :
960 : // Prefer scalable fonts
961 12912 : FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
962 :
963 12912 : const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
964 12912 : const FcChar8* pTargetNameUtf8 = (FcChar8*)aTargetName.getStr();
965 12912 : FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
966 :
967 25824 : LanguageTag aLangTag(rPattern.meLanguage);
968 25824 : OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
969 :
970 : // Add required Unicode characters, if any
971 12912 : if ( !rMissingCodes.isEmpty() )
972 : {
973 47 : FcCharSet *unicodes = FcCharSetCreate();
974 162 : for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
975 : {
976 : // also handle unicode surrogates
977 68 : const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
978 68 : FcCharSetAddChar( unicodes, nCode );
979 : //if the codepoint is impossible for this lang tag, then clear it
980 : //and autodetect something useful
981 68 : if (!aLangAttrib.isEmpty() && isImpossibleCodePointForLang(aLangTag, nCode))
982 0 : aLangAttrib = OString();
983 : //#i105784#/rhbz#527719 improve selection of fallback font
984 68 : if (aLangAttrib.isEmpty())
985 : {
986 0 : aLangTag = getExemplarLangTagForCodePoint(nCode);
987 0 : aLangAttrib = mapToFontConfigLangTag(aLangTag);
988 : }
989 : }
990 47 : FcPatternAddCharSet(pPattern, FC_CHARSET, unicodes);
991 47 : FcCharSetDestroy(unicodes);
992 : }
993 :
994 12912 : if (!aLangAttrib.isEmpty())
995 12816 : FcPatternAddString(pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr());
996 :
997 : addtopattern(pPattern, rPattern.GetSlant(), rPattern.GetWeight(),
998 12912 : rPattern.GetWidthType(), rPattern.GetPitch());
999 :
1000 : // query fontconfig for a substitute
1001 12912 : FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
1002 12912 : FcDefaultSubstitute(pPattern);
1003 :
1004 : // process the result of the fontconfig query
1005 12912 : FcResult eResult = FcResultNoMatch;
1006 12912 : FcFontSet* pFontSet = rWrapper.getFontSet();
1007 12912 : FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
1008 12912 : FcPatternDestroy( pPattern );
1009 :
1010 12912 : FcFontSet* pSet = NULL;
1011 12912 : if( pResult )
1012 : {
1013 12912 : pSet = FcFontSetCreate();
1014 : // info: destroying the pSet destroys pResult implicitly
1015 : // since pResult was "added" to pSet
1016 12912 : FcFontSetAdd( pSet, pResult );
1017 : }
1018 :
1019 12912 : if( pSet )
1020 : {
1021 12912 : if( pSet->nfont > 0 )
1022 : {
1023 : //extract the closest match
1024 12912 : FcChar8* file = NULL;
1025 12912 : FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
1026 12912 : int nCollectionEntry = 0;
1027 12912 : FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
1028 12912 : if (eIndexRes != FcResultMatch)
1029 0 : nCollectionEntry = 0;
1030 12912 : if( eFileRes == FcResultMatch )
1031 : {
1032 25824 : OString aDir, aBase, aOrgPath( (sal_Char*)file );
1033 12912 : splitPath( aOrgPath, aDir, aBase );
1034 12912 : int nDirID = getDirectoryAtom( aDir, true );
1035 12912 : fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
1036 12912 : if( aFont > 0 )
1037 : {
1038 12912 : FastPrintFontInfo aInfo;
1039 12912 : bRet = getFontFastInfo( aFont, aInfo );
1040 12912 : rPattern.maSearchName = aInfo.m_aFamilyName;
1041 12912 : }
1042 : }
1043 :
1044 : SAL_WARN_IF(!bRet, "vcl", "no FC_FILE found, falling back to name search");
1045 :
1046 12912 : 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 12912 : if (bRet)
1065 : {
1066 12912 : int val = 0;
1067 12912 : if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
1068 12912 : rPattern.SetWeight( convertWeight(val) );
1069 12912 : if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
1070 12912 : rPattern.SetItalic( convertSlant(val) );
1071 12912 : if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
1072 8743 : rPattern.SetPitch ( convertSpacing(val) );
1073 12912 : if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
1074 12912 : rPattern.SetWidthType ( convertWidth(val) );
1075 : FcBool bEmbolden;
1076 12912 : if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
1077 2 : rPattern.mbEmbolden = bEmbolden;
1078 12912 : FcMatrix *pMatrix = 0;
1079 12912 : if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
1080 : {
1081 1 : rPattern.maItalicMatrix.xx = pMatrix->xx;
1082 1 : rPattern.maItalicMatrix.xy = pMatrix->xy;
1083 1 : rPattern.maItalicMatrix.yx = pMatrix->yx;
1084 1 : rPattern.maItalicMatrix.yy = pMatrix->yy;
1085 : }
1086 : }
1087 :
1088 : // update rMissingCodes by removing resolved unicodes
1089 12912 : if( !rMissingCodes.isEmpty() )
1090 : {
1091 47 : sal_uInt32* pRemainingCodes = (sal_uInt32*)alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) );
1092 47 : int nRemainingLen = 0;
1093 : FcCharSet* unicodes;
1094 47 : if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &unicodes))
1095 : {
1096 162 : for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
1097 : {
1098 : // also handle unicode surrogates
1099 68 : const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
1100 68 : if (FcCharSetHasChar(unicodes, nCode) != FcTrue)
1101 12 : pRemainingCodes[ nRemainingLen++ ] = nCode;
1102 : }
1103 : }
1104 47 : 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 47 : rMissingCodes = sStillMissing;
1138 : }
1139 : }
1140 :
1141 12912 : FcFontSetDestroy( pSet );
1142 : }
1143 :
1144 25824 : 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: */
|