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