1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2019 The Qt Company Ltd. |
4 | ** Contact: https://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the plugins of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see https://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at https://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or (at your option) the GNU General |
28 | ** Public license version 3 or any later version approved by the KDE Free |
29 | ** Qt Foundation. The licenses are as published by the Free Software |
30 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
31 | ** included in the packaging of this file. Please review the following |
32 | ** information to ensure the GNU General Public License requirements will |
33 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
34 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
35 | ** |
36 | ** $QT_END_LICENSE$ |
37 | ** |
38 | ****************************************************************************/ |
39 | |
40 | #include "qfontconfigdatabase_p.h" |
41 | #include "qfontenginemultifontconfig_p.h" |
42 | |
43 | #include <QtFontDatabaseSupport/private/qfontengine_ft_p.h> |
44 | |
45 | #include <QtCore/QList> |
46 | #include <QtCore/QElapsedTimer> |
47 | #include <QtCore/QFile> |
48 | |
49 | #include <qpa/qplatformnativeinterface.h> |
50 | #include <qpa/qplatformscreen.h> |
51 | #include <qpa/qplatformintegration.h> |
52 | #include <qpa/qplatformservices.h> |
53 | |
54 | #include <QtGui/private/qguiapplication_p.h> |
55 | #include <QtGui/private/qhighdpiscaling_p.h> |
56 | |
57 | #include <QtGui/qguiapplication.h> |
58 | |
59 | #include <QtCore/private/qduplicatetracker_p.h> |
60 | |
61 | #include <fontconfig/fontconfig.h> |
62 | #if FC_VERSION >= 20402 |
63 | #include <fontconfig/fcfreetype.h> |
64 | #endif |
65 | |
66 | QT_BEGIN_NAMESPACE |
67 | |
68 | static const int maxWeight = 99; |
69 | |
70 | static inline int mapToQtWeightForRange(int fcweight, int fcLower, int fcUpper, int qtLower, int qtUpper) |
71 | { |
72 | return qtLower + ((fcweight - fcLower) * (qtUpper - qtLower)) / (fcUpper - fcLower); |
73 | } |
74 | |
75 | static inline int weightFromFcWeight(int fcweight) |
76 | { |
77 | // Font Config uses weights from 0 to 215 (the highest enum value) while QFont ranges from |
78 | // 0 to 99. The spacing between the values for the enums are uneven so a linear mapping from |
79 | // Font Config values to Qt would give surprising results. So, we do a piecewise linear |
80 | // mapping. This ensures that where there is a corresponding enum on both sides (for example |
81 | // FC_WEIGHT_DEMIBOLD and QFont::DemiBold) we map one to the other but other values map |
82 | // to intermediate Qt weights. |
83 | |
84 | if (fcweight <= FC_WEIGHT_THIN) |
85 | return QFont::Thin; |
86 | if (fcweight <= FC_WEIGHT_ULTRALIGHT) |
87 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_THIN, FC_WEIGHT_ULTRALIGHT, qtLower: QFont::Thin, qtUpper: QFont::ExtraLight); |
88 | if (fcweight <= FC_WEIGHT_LIGHT) |
89 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRALIGHT, FC_WEIGHT_LIGHT, qtLower: QFont::ExtraLight, qtUpper: QFont::Light); |
90 | if (fcweight <= FC_WEIGHT_NORMAL) |
91 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_LIGHT, FC_WEIGHT_NORMAL, qtLower: QFont::Light, qtUpper: QFont::Normal); |
92 | if (fcweight <= FC_WEIGHT_MEDIUM) |
93 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_NORMAL, FC_WEIGHT_MEDIUM, qtLower: QFont::Normal, qtUpper: QFont::Medium); |
94 | if (fcweight <= FC_WEIGHT_DEMIBOLD) |
95 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_MEDIUM, FC_WEIGHT_DEMIBOLD, qtLower: QFont::Medium, qtUpper: QFont::DemiBold); |
96 | if (fcweight <= FC_WEIGHT_BOLD) |
97 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_DEMIBOLD, FC_WEIGHT_BOLD, qtLower: QFont::DemiBold, qtUpper: QFont::Bold); |
98 | if (fcweight <= FC_WEIGHT_ULTRABOLD) |
99 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_BOLD, FC_WEIGHT_ULTRABOLD, qtLower: QFont::Bold, qtUpper: QFont::ExtraBold); |
100 | if (fcweight <= FC_WEIGHT_BLACK) |
101 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_ULTRABOLD, FC_WEIGHT_BLACK, qtLower: QFont::ExtraBold, qtUpper: QFont::Black); |
102 | if (fcweight <= FC_WEIGHT_ULTRABLACK) |
103 | return mapToQtWeightForRange(fcweight, FC_WEIGHT_BLACK, FC_WEIGHT_ULTRABLACK, qtLower: QFont::Black, qtUpper: maxWeight); |
104 | return maxWeight; |
105 | } |
106 | |
107 | static inline int stretchFromFcWidth(int fcwidth) |
108 | { |
109 | // Font Config enums for width match pretty closely with those used by Qt so just use |
110 | // Font Config values directly while enforcing the same limits imposed by QFont. |
111 | const int maxStretch = 4000; |
112 | int qtstretch; |
113 | if (fcwidth < 1) |
114 | qtstretch = 1; |
115 | else if (fcwidth > maxStretch) |
116 | qtstretch = maxStretch; |
117 | else |
118 | qtstretch = fcwidth; |
119 | |
120 | return qtstretch; |
121 | } |
122 | |
123 | static const char specialLanguages[][6] = { |
124 | "" , // Unknown |
125 | "" , // Inherited |
126 | "" , // Common |
127 | "en" , // Latin |
128 | "el" , // Greek |
129 | "ru" , // Cyrillic |
130 | "hy" , // Armenian |
131 | "he" , // Hebrew |
132 | "ar" , // Arabic |
133 | "syr" , // Syriac |
134 | "dv" , // Thaana |
135 | "hi" , // Devanagari |
136 | "bn" , // Bengali |
137 | "pa" , // Gurmukhi |
138 | "gu" , // Gujarati |
139 | "or" , // Oriya |
140 | "ta" , // Tamil |
141 | "te" , // Telugu |
142 | "kn" , // Kannada |
143 | "ml" , // Malayalam |
144 | "si" , // Sinhala |
145 | "th" , // Thai |
146 | "lo" , // Lao |
147 | "bo" , // Tibetan |
148 | "my" , // Myanmar |
149 | "ka" , // Georgian |
150 | "ko" , // Hangul |
151 | "am" , // Ethiopic |
152 | "chr" , // Cherokee |
153 | "cr" , // CanadianAboriginal |
154 | "sga" , // Ogham |
155 | "non" , // Runic |
156 | "km" , // Khmer |
157 | "mn" , // Mongolian |
158 | "ja" , // Hiragana |
159 | "ja" , // Katakana |
160 | "zh-TW" , // Bopomofo |
161 | "" , // Han |
162 | "ii" , // Yi |
163 | "ett" , // OldItalic |
164 | "got" , // Gothic |
165 | "en" , // Deseret |
166 | "fil" , // Tagalog |
167 | "hnn" , // Hanunoo |
168 | "bku" , // Buhid |
169 | "tbw" , // Tagbanwa |
170 | "cop" , // Coptic |
171 | "lif" , // Limbu |
172 | "tdd" , // TaiLe |
173 | "grc" , // LinearB |
174 | "uga" , // Ugaritic |
175 | "en" , // Shavian |
176 | "so" , // Osmanya |
177 | "grc" , // Cypriot |
178 | "" , // Braille |
179 | "bug" , // Buginese |
180 | "khb" , // NewTaiLue |
181 | "cu" , // Glagolitic |
182 | "shi" , // Tifinagh |
183 | "syl" , // SylotiNagri |
184 | "peo" , // OldPersian |
185 | "pra" , // Kharoshthi |
186 | "ban" , // Balinese |
187 | "akk" , // Cuneiform |
188 | "phn" , // Phoenician |
189 | "lzh" , // PhagsPa |
190 | "man" , // Nko |
191 | "su" , // Sundanese |
192 | "lep" , // Lepcha |
193 | "sat" , // OlChiki |
194 | "vai" , // Vai |
195 | "saz" , // Saurashtra |
196 | "eky" , // KayahLi |
197 | "rej" , // Rejang |
198 | "xlc" , // Lycian |
199 | "xcr" , // Carian |
200 | "xld" , // Lydian |
201 | "cjm" , // Cham |
202 | "nod" , // TaiTham |
203 | "blt" , // TaiViet |
204 | "ae" , // Avestan |
205 | "egy" , // EgyptianHieroglyphs |
206 | "smp" , // Samaritan |
207 | "lis" , // Lisu |
208 | "bax" , // Bamum |
209 | "jv" , // Javanese |
210 | "mni" , // MeeteiMayek |
211 | "arc" , // ImperialAramaic |
212 | "xsa" , // OldSouthArabian |
213 | "xpr" , // InscriptionalParthian |
214 | "pal" , // InscriptionalPahlavi |
215 | "otk" , // OldTurkic |
216 | "bh" , // Kaithi |
217 | "bbc" , // Batak |
218 | "pra" , // Brahmi |
219 | "myz" , // Mandaic |
220 | "ccp" , // Chakma |
221 | "xmr" , // MeroiticCursive |
222 | "xmr" , // MeroiticHieroglyphs |
223 | "hmd" , // Miao |
224 | "sa" , // Sharada |
225 | "srb" , // SoraSompeng |
226 | "doi" , // Takri |
227 | "lez" , // CaucasianAlbanian |
228 | "bsq" , // BassaVah |
229 | "fr" , // Duployan |
230 | "sq" , // Elbasan |
231 | "sa" , // Grantha |
232 | "hnj" , // PahawhHmong |
233 | "sd" , // Khojki |
234 | "lab" , // LinearA |
235 | "hi" , // Mahajani |
236 | "xmn" , // Manichaean |
237 | "men" , // MendeKikakui |
238 | "mr" , // Modi |
239 | "mru" , // Mro |
240 | "xna" , // OldNorthArabian |
241 | "arc" , // Nabataean |
242 | "arc" , // Palmyrene |
243 | "ctd" , // PauCinHau |
244 | "kv" , // OldPermic |
245 | "pal" , // PsalterPahlavi |
246 | "sa" , // Siddham |
247 | "sd" , // Khudawadi |
248 | "mai" , // Tirhuta |
249 | "hoc" , // WarangCiti |
250 | "" , // Ahom |
251 | "" , // AnatolianHieroglyphs |
252 | "" , // Hatran |
253 | "" , // Multani |
254 | "" , // OldHungarian |
255 | "" , // SignWriting |
256 | "" , // Adlam |
257 | "" , // Bhaiksuki |
258 | "" , // Marchen |
259 | "" , // Newa |
260 | "" , // Osage |
261 | "" , // Tangut |
262 | "" , // MasaramGondi |
263 | "" , // Nushu |
264 | "" , // Soyombo |
265 | "" , // ZanabazarSquare |
266 | "" , // Dogra |
267 | "" , // GunjalaGondi |
268 | "" , // HanifiRohingya |
269 | "" , // Makasar |
270 | "" , // Medefaidrin |
271 | "" , // OldSogdian |
272 | "" , // Sogdian |
273 | "" , // Elymaic |
274 | "" , // Nandinagari |
275 | "" , // NyiakengPuachueHmong |
276 | "" , // Wancho |
277 | "" , // Chorasmian |
278 | "" , // DivesAkuru |
279 | "" , // KhitanSmallScript |
280 | "" // Yezidi |
281 | }; |
282 | Q_STATIC_ASSERT(sizeof specialLanguages / sizeof *specialLanguages == QChar::ScriptCount); |
283 | |
284 | // this could become a list of all languages used for each writing |
285 | // system, instead of using the single most common language. |
286 | static const char languageForWritingSystem[][6] = { |
287 | "" , // Any |
288 | "en" , // Latin |
289 | "el" , // Greek |
290 | "ru" , // Cyrillic |
291 | "hy" , // Armenian |
292 | "he" , // Hebrew |
293 | "ar" , // Arabic |
294 | "syr" , // Syriac |
295 | "div" , // Thaana |
296 | "hi" , // Devanagari |
297 | "bn" , // Bengali |
298 | "pa" , // Gurmukhi |
299 | "gu" , // Gujarati |
300 | "or" , // Oriya |
301 | "ta" , // Tamil |
302 | "te" , // Telugu |
303 | "kn" , // Kannada |
304 | "ml" , // Malayalam |
305 | "si" , // Sinhala |
306 | "th" , // Thai |
307 | "lo" , // Lao |
308 | "bo" , // Tibetan |
309 | "my" , // Myanmar |
310 | "ka" , // Georgian |
311 | "km" , // Khmer |
312 | "zh-cn" , // SimplifiedChinese |
313 | "zh-tw" , // TraditionalChinese |
314 | "ja" , // Japanese |
315 | "ko" , // Korean |
316 | "vi" , // Vietnamese |
317 | "" , // Symbol |
318 | "sga" , // Ogham |
319 | "non" , // Runic |
320 | "man" // N'Ko |
321 | }; |
322 | Q_STATIC_ASSERT(sizeof languageForWritingSystem / sizeof *languageForWritingSystem == QFontDatabase::WritingSystemsCount); |
323 | |
324 | #if FC_VERSION >= 20297 |
325 | // Newer FontConfig let's us sort out fonts that report certain scripts support, |
326 | // but no open type tables for handling them correctly. |
327 | // Check the reported script presence in the FC_CAPABILITY's "otlayout:" section. |
328 | static const char capabilityForWritingSystem[][5] = { |
329 | "" , // Any |
330 | "" , // Latin |
331 | "" , // Greek |
332 | "" , // Cyrillic |
333 | "" , // Armenian |
334 | "" , // Hebrew |
335 | "" , // Arabic |
336 | "syrc" , // Syriac |
337 | "thaa" , // Thaana |
338 | "deva" , // Devanagari |
339 | "beng" , // Bengali |
340 | "guru" , // Gurmukhi |
341 | "gujr" , // Gujarati |
342 | "orya" , // Oriya |
343 | "taml" , // Tamil |
344 | "telu" , // Telugu |
345 | "knda" , // Kannada |
346 | "mlym" , // Malayalam |
347 | "sinh" , // Sinhala |
348 | "" , // Thai |
349 | "" , // Lao |
350 | "tibt" , // Tibetan |
351 | "mymr" , // Myanmar |
352 | "" , // Georgian |
353 | "khmr" , // Khmer |
354 | "" , // SimplifiedChinese |
355 | "" , // TraditionalChinese |
356 | "" , // Japanese |
357 | "" , // Korean |
358 | "" , // Vietnamese |
359 | "" , // Symbol |
360 | "" , // Ogham |
361 | "" , // Runic |
362 | "nko " // N'Ko |
363 | }; |
364 | Q_STATIC_ASSERT(sizeof(capabilityForWritingSystem) / sizeof(*capabilityForWritingSystem) == QFontDatabase::WritingSystemsCount); |
365 | #endif |
366 | |
367 | static const char *getFcFamilyForStyleHint(const QFont::StyleHint style) |
368 | { |
369 | const char *stylehint = nullptr; |
370 | switch (style) { |
371 | case QFont::SansSerif: |
372 | stylehint = "sans-serif" ; |
373 | break; |
374 | case QFont::Serif: |
375 | stylehint = "serif" ; |
376 | break; |
377 | case QFont::TypeWriter: |
378 | case QFont::Monospace: |
379 | stylehint = "monospace" ; |
380 | break; |
381 | case QFont::Cursive: |
382 | stylehint = "cursive" ; |
383 | break; |
384 | case QFont::Fantasy: |
385 | stylehint = "fantasy" ; |
386 | break; |
387 | default: |
388 | break; |
389 | } |
390 | return stylehint; |
391 | } |
392 | |
393 | static inline bool requiresOpenType(int writingSystem) |
394 | { |
395 | return ((writingSystem >= QFontDatabase::Syriac && writingSystem <= QFontDatabase::Sinhala) |
396 | || writingSystem == QFontDatabase::Khmer || writingSystem == QFontDatabase::Nko); |
397 | } |
398 | |
399 | static void populateFromPattern(FcPattern *pattern) |
400 | { |
401 | QString familyName; |
402 | QString familyNameLang; |
403 | FcChar8 *value = nullptr; |
404 | int weight_value; |
405 | int slant_value; |
406 | int spacing_value; |
407 | int width_value; |
408 | FcChar8 *file_value; |
409 | int indexValue; |
410 | FcChar8 *foundry_value; |
411 | FcChar8 *style_value; |
412 | FcBool scalable; |
413 | FcBool antialias; |
414 | |
415 | if (FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &value) != FcResultMatch) |
416 | return; |
417 | |
418 | familyName = QString::fromUtf8(str: (const char *)value); |
419 | |
420 | if (FcPatternGetString(p: pattern, FC_FAMILYLANG, n: 0, s: &value) == FcResultMatch) |
421 | familyNameLang = QString::fromUtf8(str: (const char *)value); |
422 | |
423 | slant_value = FC_SLANT_ROMAN; |
424 | weight_value = FC_WEIGHT_REGULAR; |
425 | spacing_value = FC_PROPORTIONAL; |
426 | file_value = nullptr; |
427 | indexValue = 0; |
428 | scalable = FcTrue; |
429 | |
430 | |
431 | if (FcPatternGetInteger(p: pattern, FC_SLANT, n: 0, i: &slant_value) != FcResultMatch) |
432 | slant_value = FC_SLANT_ROMAN; |
433 | if (FcPatternGetInteger(p: pattern, FC_WEIGHT, n: 0, i: &weight_value) != FcResultMatch) |
434 | weight_value = FC_WEIGHT_REGULAR; |
435 | if (FcPatternGetInteger(p: pattern, FC_WIDTH, n: 0, i: &width_value) != FcResultMatch) |
436 | width_value = FC_WIDTH_NORMAL; |
437 | if (FcPatternGetInteger(p: pattern, FC_SPACING, n: 0, i: &spacing_value) != FcResultMatch) |
438 | spacing_value = FC_PROPORTIONAL; |
439 | if (FcPatternGetString(p: pattern, FC_FILE, n: 0, s: &file_value) != FcResultMatch) |
440 | file_value = nullptr; |
441 | if (FcPatternGetInteger(p: pattern, FC_INDEX, n: 0, i: &indexValue) != FcResultMatch) |
442 | indexValue = 0; |
443 | if (FcPatternGetBool(p: pattern, FC_SCALABLE, n: 0, b: &scalable) != FcResultMatch) |
444 | scalable = FcTrue; |
445 | if (FcPatternGetString(p: pattern, FC_FOUNDRY, n: 0, s: &foundry_value) != FcResultMatch) |
446 | foundry_value = nullptr; |
447 | if (FcPatternGetString(p: pattern, FC_STYLE, n: 0, s: &style_value) != FcResultMatch) |
448 | style_value = nullptr; |
449 | if (FcPatternGetBool(p: pattern,FC_ANTIALIAS,n: 0,b: &antialias) != FcResultMatch) |
450 | antialias = true; |
451 | |
452 | QSupportedWritingSystems writingSystems; |
453 | FcLangSet *langset = nullptr; |
454 | FcResult res = FcPatternGetLangSet(p: pattern, FC_LANG, n: 0, ls: &langset); |
455 | if (res == FcResultMatch) { |
456 | bool hasLang = false; |
457 | #if FC_VERSION >= 20297 |
458 | FcChar8 *cap = nullptr; |
459 | FcResult capRes = FcResultNoMatch; |
460 | #endif |
461 | for (int j = 1; j < QFontDatabase::WritingSystemsCount; ++j) { |
462 | const FcChar8 *lang = (const FcChar8*) languageForWritingSystem[j]; |
463 | if (lang) { |
464 | FcLangResult langRes = FcLangSetHasLang(ls: langset, lang); |
465 | if (langRes != FcLangDifferentLang) { |
466 | #if FC_VERSION >= 20297 |
467 | if (*capabilityForWritingSystem[j] && requiresOpenType(writingSystem: j)) { |
468 | if (cap == nullptr) |
469 | capRes = FcPatternGetString(p: pattern, FC_CAPABILITY, n: 0, s: &cap); |
470 | if (capRes == FcResultMatch && strstr(haystack: reinterpret_cast<const char *>(cap), needle: capabilityForWritingSystem[j]) == nullptr) |
471 | continue; |
472 | } |
473 | #endif |
474 | writingSystems.setSupported(QFontDatabase::WritingSystem(j)); |
475 | hasLang = true; |
476 | } |
477 | } |
478 | } |
479 | if (!hasLang) |
480 | // none of our known languages, add it to the other set |
481 | writingSystems.setSupported(QFontDatabase::Other); |
482 | } else { |
483 | // we set Other to supported for symbol fonts. It makes no |
484 | // sense to merge these with other ones, as they are |
485 | // special in a way. |
486 | writingSystems.setSupported(QFontDatabase::Other); |
487 | } |
488 | |
489 | FontFile *fontFile = new FontFile; |
490 | fontFile->fileName = QString::fromLocal8Bit(str: (const char *)file_value); |
491 | fontFile->indexValue = indexValue; |
492 | |
493 | QFont::Style style = (slant_value == FC_SLANT_ITALIC) |
494 | ? QFont::StyleItalic |
495 | : ((slant_value == FC_SLANT_OBLIQUE) |
496 | ? QFont::StyleOblique |
497 | : QFont::StyleNormal); |
498 | // Note: weight should really be an int but registerFont incorrectly uses an enum |
499 | QFont::Weight weight = QFont::Weight(weightFromFcWeight(fcweight: weight_value)); |
500 | |
501 | double pixel_size = 0; |
502 | if (!scalable) |
503 | FcPatternGetDouble (p: pattern, FC_PIXEL_SIZE, n: 0, d: &pixel_size); |
504 | |
505 | bool fixedPitch = spacing_value >= FC_MONO; |
506 | // Note: stretch should really be an int but registerFont incorrectly uses an enum |
507 | QFont::Stretch stretch = QFont::Stretch(stretchFromFcWidth(fcwidth: width_value)); |
508 | QString styleName = style_value ? QString::fromUtf8(str: (const char *) style_value) : QString(); |
509 | QPlatformFontDatabase::registerFont(familyname: familyName,stylename: styleName,foundryname: QLatin1String((const char *)foundry_value),weight,style,stretch,antialiased: antialias,scalable,pixelSize: pixel_size,fixedPitch,writingSystems,handle: fontFile); |
510 | // qDebug() << familyName << (const char *)foundry_value << weight << style << &writingSystems << scalable << true << pixel_size; |
511 | |
512 | for (int k = 1; FcPatternGetString(p: pattern, FC_FAMILY, n: k, s: &value) == FcResultMatch; ++k) { |
513 | const QString altFamilyName = QString::fromUtf8(str: (const char *)value); |
514 | // Extra family names can be aliases or subfamilies. |
515 | // If it is a subfamily, register it as a separate font, so only members of the subfamily are |
516 | // matched when the subfamily is requested. |
517 | QString altStyleName; |
518 | if (FcPatternGetString(p: pattern, FC_STYLE, n: k, s: &value) == FcResultMatch) |
519 | altStyleName = QString::fromUtf8(str: (const char *)value); |
520 | else |
521 | altStyleName = styleName; |
522 | |
523 | QString altFamilyNameLang; |
524 | if (FcPatternGetString(p: pattern, FC_FAMILYLANG, n: k, s: &value) == FcResultMatch) |
525 | altFamilyNameLang = QString::fromUtf8(str: (const char *)value); |
526 | else |
527 | altFamilyNameLang = familyNameLang; |
528 | |
529 | if (familyNameLang == altFamilyNameLang && altStyleName != styleName) { |
530 | FontFile *altFontFile = new FontFile(*fontFile); |
531 | QPlatformFontDatabase::registerFont(familyname: altFamilyName, stylename: altStyleName, foundryname: QLatin1String((const char *)foundry_value),weight,style,stretch,antialiased: antialias,scalable,pixelSize: pixel_size,fixedPitch,writingSystems,handle: altFontFile); |
532 | } else { |
533 | QPlatformFontDatabase::registerAliasToFontFamily(familyName, alias: altFamilyName); |
534 | } |
535 | } |
536 | |
537 | } |
538 | |
539 | QFontconfigDatabase::~QFontconfigDatabase() |
540 | { |
541 | FcConfigDestroy(config: FcConfigGetCurrent()); |
542 | } |
543 | |
544 | void QFontconfigDatabase::populateFontDatabase() |
545 | { |
546 | FcInit(); |
547 | FcFontSet *fonts; |
548 | |
549 | { |
550 | FcObjectSet *os = FcObjectSetCreate(); |
551 | FcPattern *pattern = FcPatternCreate(); |
552 | const char *properties [] = { |
553 | FC_FAMILY, FC_STYLE, FC_WEIGHT, FC_SLANT, |
554 | FC_SPACING, FC_FILE, FC_INDEX, |
555 | FC_LANG, FC_CHARSET, FC_FOUNDRY, FC_SCALABLE, FC_PIXEL_SIZE, |
556 | FC_WIDTH, FC_FAMILYLANG, |
557 | #if FC_VERSION >= 20297 |
558 | FC_CAPABILITY, |
559 | #endif |
560 | (const char *)nullptr |
561 | }; |
562 | const char **p = properties; |
563 | while (*p) { |
564 | FcObjectSetAdd(os, object: *p); |
565 | ++p; |
566 | } |
567 | fonts = FcFontList(config: nullptr, p: pattern, os); |
568 | FcObjectSetDestroy(os); |
569 | FcPatternDestroy(p: pattern); |
570 | } |
571 | |
572 | for (int i = 0; i < fonts->nfont; i++) |
573 | populateFromPattern(pattern: fonts->fonts[i]); |
574 | |
575 | FcFontSetDestroy (s: fonts); |
576 | |
577 | struct FcDefaultFont { |
578 | const char *qtname; |
579 | const char *rawname; |
580 | bool fixed; |
581 | }; |
582 | const FcDefaultFont defaults[] = { |
583 | { .qtname: "Serif" , .rawname: "serif" , .fixed: false }, |
584 | { .qtname: "Sans Serif" , .rawname: "sans-serif" , .fixed: false }, |
585 | { .qtname: "Monospace" , .rawname: "monospace" , .fixed: true }, |
586 | { .qtname: nullptr, .rawname: nullptr, .fixed: false } |
587 | }; |
588 | const FcDefaultFont *f = defaults; |
589 | // aliases only make sense for 'common', not for any of the specials |
590 | QSupportedWritingSystems ws; |
591 | ws.setSupported(QFontDatabase::Latin); |
592 | |
593 | while (f->qtname) { |
594 | QString familyQtName = QString::fromLatin1(str: f->qtname); |
595 | registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleNormal,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr); |
596 | registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleItalic,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr); |
597 | registerFont(familyname: familyQtName,stylename: QString(),foundryname: QString(),weight: QFont::Normal,style: QFont::StyleOblique,stretch: QFont::Unstretched,antialiased: true,scalable: true,pixelSize: 0,fixedPitch: f->fixed,writingSystems: ws,handle: nullptr); |
598 | ++f; |
599 | } |
600 | |
601 | //QPA has very lazy population of the font db. We want it to be initialized when |
602 | //QApplication is constructed, so that the population procedure can do something like this to |
603 | //set the default font |
604 | // const FcDefaultFont *s = defaults; |
605 | // QFont font("Sans Serif"); |
606 | // font.setPointSize(9); |
607 | // QApplication::setFont(font); |
608 | } |
609 | |
610 | void QFontconfigDatabase::invalidate() |
611 | { |
612 | // Clear app fonts. |
613 | FcConfigAppFontClear(config: nullptr); |
614 | } |
615 | |
616 | QFontEngineMulti *QFontconfigDatabase::fontEngineMulti(QFontEngine *fontEngine, QChar::Script script) |
617 | { |
618 | return new QFontEngineMultiFontConfig(fontEngine, script); |
619 | } |
620 | |
621 | namespace { |
622 | QFontEngine::HintStyle defaultHintStyleFromMatch(QFont::HintingPreference hintingPreference, FcPattern *match, bool useXftConf) |
623 | { |
624 | switch (hintingPreference) { |
625 | case QFont::PreferNoHinting: |
626 | return QFontEngine::HintNone; |
627 | case QFont::PreferVerticalHinting: |
628 | return QFontEngine::HintLight; |
629 | case QFont::PreferFullHinting: |
630 | return QFontEngine::HintFull; |
631 | case QFont::PreferDefaultHinting: |
632 | break; |
633 | } |
634 | |
635 | if (QHighDpiScaling::isActive()) |
636 | return QFontEngine::HintNone; |
637 | |
638 | int hint_style = 0; |
639 | if (FcPatternGetInteger (p: match, FC_HINT_STYLE, n: 0, i: &hint_style) == FcResultMatch) { |
640 | switch (hint_style) { |
641 | case FC_HINT_NONE: |
642 | return QFontEngine::HintNone; |
643 | case FC_HINT_SLIGHT: |
644 | return QFontEngine::HintLight; |
645 | case FC_HINT_MEDIUM: |
646 | return QFontEngine::HintMedium; |
647 | case FC_HINT_FULL: |
648 | return QFontEngine::HintFull; |
649 | default: |
650 | Q_UNREACHABLE(); |
651 | break; |
652 | } |
653 | } |
654 | |
655 | if (useXftConf) { |
656 | void *hintStyleResource = |
657 | QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "hintstyle" , |
658 | screen: QGuiApplication::primaryScreen()); |
659 | int hintStyle = int(reinterpret_cast<qintptr>(hintStyleResource)); |
660 | if (hintStyle > 0) |
661 | return QFontEngine::HintStyle(hintStyle - 1); |
662 | } |
663 | |
664 | return QFontEngine::HintFull; |
665 | } |
666 | |
667 | QFontEngine::SubpixelAntialiasingType subpixelTypeFromMatch(FcPattern *match, bool useXftConf) |
668 | { |
669 | int subpixel = FC_RGBA_UNKNOWN; |
670 | if (FcPatternGetInteger(p: match, FC_RGBA, n: 0, i: &subpixel) == FcResultMatch) { |
671 | switch (subpixel) { |
672 | case FC_RGBA_UNKNOWN: |
673 | case FC_RGBA_NONE: |
674 | return QFontEngine::Subpixel_None; |
675 | case FC_RGBA_RGB: |
676 | return QFontEngine::Subpixel_RGB; |
677 | case FC_RGBA_BGR: |
678 | return QFontEngine::Subpixel_BGR; |
679 | case FC_RGBA_VRGB: |
680 | return QFontEngine::Subpixel_VRGB; |
681 | case FC_RGBA_VBGR: |
682 | return QFontEngine::Subpixel_VBGR; |
683 | default: |
684 | Q_UNREACHABLE(); |
685 | break; |
686 | } |
687 | } |
688 | |
689 | if (useXftConf) { |
690 | void *subpixelTypeResource = |
691 | QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "subpixeltype" , |
692 | screen: QGuiApplication::primaryScreen()); |
693 | int subpixelType = int(reinterpret_cast<qintptr>(subpixelTypeResource)); |
694 | if (subpixelType > 0) |
695 | return QFontEngine::SubpixelAntialiasingType(subpixelType - 1); |
696 | } |
697 | |
698 | return QFontEngine::Subpixel_None; |
699 | } |
700 | } // namespace |
701 | |
702 | QFontEngine *QFontconfigDatabase::fontEngine(const QFontDef &f, void *usrPtr) |
703 | { |
704 | if (!usrPtr) |
705 | return nullptr; |
706 | |
707 | FontFile *fontfile = static_cast<FontFile *> (usrPtr); |
708 | QFontEngine::FaceId fid; |
709 | fid.filename = QFile::encodeName(fileName: fontfile->fileName); |
710 | fid.index = fontfile->indexValue; |
711 | |
712 | // FIXME: Unify with logic in QFontEngineFT::create() |
713 | QFontEngineFT *engine = new QFontEngineFT(f); |
714 | engine->face_id = fid; |
715 | |
716 | setupFontEngine(engine, fontDef: f); |
717 | |
718 | if (!engine->init(faceId: fid, antiaalias: engine->antialias, defaultFormat: engine->defaultFormat) || engine->invalid()) { |
719 | delete engine; |
720 | engine = nullptr; |
721 | } |
722 | |
723 | return engine; |
724 | } |
725 | |
726 | QFontEngine *QFontconfigDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, QFont::HintingPreference hintingPreference) |
727 | { |
728 | QFontEngineFT *engine = static_cast<QFontEngineFT*>(QFreeTypeFontDatabase::fontEngine(fontData, pixelSize, hintingPreference)); |
729 | if (engine == nullptr) |
730 | return nullptr; |
731 | |
732 | setupFontEngine(engine, fontDef: engine->fontDef); |
733 | |
734 | return engine; |
735 | } |
736 | |
737 | QStringList QFontconfigDatabase::fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const |
738 | { |
739 | QStringList fallbackFamilies; |
740 | FcPattern *pattern = FcPatternCreate(); |
741 | if (!pattern) |
742 | return fallbackFamilies; |
743 | |
744 | FcValue value; |
745 | value.type = FcTypeString; |
746 | const QByteArray cs = family.toUtf8(); |
747 | value.u.s = (const FcChar8 *)cs.data(); |
748 | FcPatternAdd(p: pattern,FC_FAMILY,value,append: true); |
749 | |
750 | int slant_value = FC_SLANT_ROMAN; |
751 | if (style == QFont::StyleItalic) |
752 | slant_value = FC_SLANT_ITALIC; |
753 | else if (style == QFont::StyleOblique) |
754 | slant_value = FC_SLANT_OBLIQUE; |
755 | FcPatternAddInteger(p: pattern, FC_SLANT, i: slant_value); |
756 | |
757 | Q_ASSERT(uint(script) < QChar::ScriptCount); |
758 | if (*specialLanguages[script] != '\0') { |
759 | FcLangSet *ls = FcLangSetCreate(); |
760 | FcLangSetAdd(ls, lang: (const FcChar8*)specialLanguages[script]); |
761 | FcPatternAddLangSet(p: pattern, FC_LANG, ls); |
762 | FcLangSetDestroy(ls); |
763 | } else if (!family.isEmpty()) { |
764 | // If script is Common or Han, then it may include languages like CJK, |
765 | // we should attach system default language set to the pattern |
766 | // to obtain correct font fallback list (i.e. if LANG=zh_CN |
767 | // then we normally want to use a Chinese font for CJK text; |
768 | // while a Japanese font should be used for that if LANG=ja) |
769 | FcPattern *dummy = FcPatternCreate(); |
770 | FcDefaultSubstitute(pattern: dummy); |
771 | FcChar8 *lang = nullptr; |
772 | FcResult res = FcPatternGetString(p: dummy, FC_LANG, n: 0, s: &lang); |
773 | if (res == FcResultMatch) |
774 | FcPatternAddString(p: pattern, FC_LANG, s: lang); |
775 | FcPatternDestroy(p: dummy); |
776 | } |
777 | |
778 | const char *stylehint = getFcFamilyForStyleHint(style: styleHint); |
779 | if (stylehint) { |
780 | value.u.s = (const FcChar8 *)stylehint; |
781 | FcPatternAddWeak(p: pattern, FC_FAMILY, value, FcTrue); |
782 | } |
783 | |
784 | FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern); |
785 | FcDefaultSubstitute(pattern); |
786 | |
787 | FcResult result = FcResultMatch; |
788 | FcFontSet *fontSet = FcFontSort(config: nullptr,p: pattern,FcFalse,csp: nullptr,result: &result); |
789 | FcPatternDestroy(p: pattern); |
790 | |
791 | if (fontSet) { |
792 | QDuplicateTracker<QString> duplicates; |
793 | duplicates.reserve(n: fontSet->nfont + 1); |
794 | (void)duplicates.hasSeen(s: family.toCaseFolded()); |
795 | for (int i = 0; i < fontSet->nfont; i++) { |
796 | FcChar8 *value = nullptr; |
797 | if (FcPatternGetString(p: fontSet->fonts[i], FC_FAMILY, n: 0, s: &value) != FcResultMatch) |
798 | continue; |
799 | // capitalize(value); |
800 | const QString familyName = QString::fromUtf8(str: (const char *)value); |
801 | const QString familyNameCF = familyName.toCaseFolded(); |
802 | if (!duplicates.hasSeen(s: familyNameCF)) { |
803 | fallbackFamilies << familyName; |
804 | } |
805 | } |
806 | FcFontSetDestroy(s: fontSet); |
807 | } |
808 | // qDebug() << "fallbackFamilies for:" << family << style << styleHint << script << fallbackFamilies; |
809 | |
810 | return fallbackFamilies; |
811 | } |
812 | |
813 | static FcPattern *queryFont(const FcChar8 *file, const QByteArray &data, int id, FcBlanks *blanks, int *count) |
814 | { |
815 | #if FC_VERSION < 20402 |
816 | Q_UNUSED(data) |
817 | return FcFreeTypeQuery(file, id, blanks, count); |
818 | #else |
819 | if (data.isEmpty()) |
820 | return FcFreeTypeQuery(file, id, blanks, count); |
821 | |
822 | FT_Library lib = qt_getFreetype(); |
823 | |
824 | FcPattern *pattern = nullptr; |
825 | |
826 | FT_Face face; |
827 | if (!FT_New_Memory_Face(library: lib, file_base: (const FT_Byte *)data.constData(), file_size: data.size(), face_index: id, aface: &face)) { |
828 | *count = face->num_faces; |
829 | |
830 | pattern = FcFreeTypeQueryFace(face, file, id, blanks); |
831 | |
832 | FT_Done_Face(face); |
833 | } |
834 | |
835 | return pattern; |
836 | #endif |
837 | } |
838 | |
839 | QStringList QFontconfigDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName) |
840 | { |
841 | QStringList families; |
842 | |
843 | FcFontSet *set = FcConfigGetFonts(config: nullptr, set: FcSetApplication); |
844 | if (!set) { |
845 | FcConfigAppFontAddFile(config: nullptr, file: (const FcChar8 *)":/non-existent" ); |
846 | set = FcConfigGetFonts(config: nullptr, set: FcSetApplication); // try again |
847 | if (!set) |
848 | return families; |
849 | } |
850 | |
851 | int id = 0; |
852 | FcBlanks *blanks = FcConfigGetBlanks(config: nullptr); |
853 | int count = 0; |
854 | |
855 | FcPattern *pattern; |
856 | do { |
857 | pattern = queryFont(file: (const FcChar8 *)QFile::encodeName(fileName).constData(), |
858 | data: fontData, id, blanks, count: &count); |
859 | if (!pattern) |
860 | return families; |
861 | |
862 | FcChar8 *fam = nullptr; |
863 | if (FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &fam) == FcResultMatch) { |
864 | QString family = QString::fromUtf8(str: reinterpret_cast<const char *>(fam)); |
865 | families << family; |
866 | } |
867 | populateFromPattern(pattern); |
868 | |
869 | FcFontSetAdd(s: set, font: pattern); |
870 | |
871 | ++id; |
872 | } while (id < count); |
873 | |
874 | return families; |
875 | } |
876 | |
877 | QString QFontconfigDatabase::resolveFontFamilyAlias(const QString &family) const |
878 | { |
879 | QString resolved = QFreeTypeFontDatabase::resolveFontFamilyAlias(family); |
880 | if (!resolved.isEmpty() && resolved != family) |
881 | return resolved; |
882 | FcPattern *pattern = FcPatternCreate(); |
883 | if (!pattern) |
884 | return family; |
885 | |
886 | if (!family.isEmpty()) { |
887 | const QByteArray cs = family.toUtf8(); |
888 | FcPatternAddString(p: pattern, FC_FAMILY, s: (const FcChar8 *) cs.constData()); |
889 | } |
890 | FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern); |
891 | FcDefaultSubstitute(pattern); |
892 | |
893 | FcChar8 *familyAfterSubstitution = nullptr; |
894 | FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &familyAfterSubstitution); |
895 | resolved = QString::fromUtf8(str: (const char *) familyAfterSubstitution); |
896 | FcPatternDestroy(p: pattern); |
897 | |
898 | return resolved; |
899 | } |
900 | |
901 | QFont QFontconfigDatabase::defaultFont() const |
902 | { |
903 | // Hack to get system default language until FcGetDefaultLangs() |
904 | // is exported (https://bugs.freedesktop.org/show_bug.cgi?id=32853) |
905 | // or https://bugs.freedesktop.org/show_bug.cgi?id=35482 is fixed |
906 | FcPattern *dummy = FcPatternCreate(); |
907 | FcDefaultSubstitute(pattern: dummy); |
908 | FcChar8 *lang = nullptr; |
909 | FcResult res = FcPatternGetString(p: dummy, FC_LANG, n: 0, s: &lang); |
910 | |
911 | FcPattern *pattern = FcPatternCreate(); |
912 | if (res == FcResultMatch) { |
913 | // Make defaultFont pattern matching locale language aware, because |
914 | // certain FC_LANG based custom rules may happen in FcConfigSubstitute() |
915 | FcPatternAddString(p: pattern, FC_LANG, s: lang); |
916 | } |
917 | FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern); |
918 | FcDefaultSubstitute(pattern); |
919 | |
920 | FcChar8 *familyAfterSubstitution = nullptr; |
921 | FcPatternGetString(p: pattern, FC_FAMILY, n: 0, s: &familyAfterSubstitution); |
922 | QString resolved = QString::fromUtf8(str: (const char *) familyAfterSubstitution); |
923 | FcPatternDestroy(p: pattern); |
924 | FcPatternDestroy(p: dummy); |
925 | |
926 | return QFont(resolved); |
927 | } |
928 | |
929 | void QFontconfigDatabase::setupFontEngine(QFontEngineFT *engine, const QFontDef &fontDef) const |
930 | { |
931 | bool antialias = !(fontDef.styleStrategy & QFont::NoAntialias); |
932 | bool forcedAntialiasSetting = !antialias || QHighDpiScaling::isActive(); |
933 | |
934 | const QPlatformServices *services = QGuiApplicationPrivate::platformIntegration()->services(); |
935 | bool useXftConf = false; |
936 | |
937 | if (services) { |
938 | const QList<QByteArray> desktopEnv = services->desktopEnvironment().split(sep: ':'); |
939 | useXftConf = desktopEnv.contains(t: "GNOME" ) || desktopEnv.contains(t: "UNITY" ) || desktopEnv.contains(t: "XFCE" ); |
940 | } |
941 | |
942 | if (useXftConf && !forcedAntialiasSetting) { |
943 | void *antialiasResource = |
944 | QGuiApplication::platformNativeInterface()->nativeResourceForScreen(resource: "antialiasingEnabled" , |
945 | screen: QGuiApplication::primaryScreen()); |
946 | int antialiasingEnabled = int(reinterpret_cast<qintptr>(antialiasResource)); |
947 | if (antialiasingEnabled > 0) |
948 | antialias = antialiasingEnabled - 1; |
949 | } |
950 | |
951 | QFontEngine::GlyphFormat format; |
952 | // try and get the pattern |
953 | FcPattern *pattern = FcPatternCreate(); |
954 | |
955 | FcValue value; |
956 | value.type = FcTypeString; |
957 | QByteArray cs = fontDef.family.toUtf8(); |
958 | value.u.s = (const FcChar8 *)cs.data(); |
959 | FcPatternAdd(p: pattern,FC_FAMILY,value,append: true); |
960 | |
961 | QFontEngine::FaceId fid = engine->faceId(); |
962 | |
963 | if (!fid.filename.isEmpty()) { |
964 | value.u.s = (const FcChar8 *)fid.filename.data(); |
965 | FcPatternAdd(p: pattern,FC_FILE,value,append: true); |
966 | |
967 | value.type = FcTypeInteger; |
968 | value.u.i = fid.index; |
969 | FcPatternAdd(p: pattern,FC_INDEX,value,append: true); |
970 | } |
971 | |
972 | if (fontDef.pixelSize > 0.1) |
973 | FcPatternAddDouble(p: pattern, FC_PIXEL_SIZE, d: fontDef.pixelSize); |
974 | |
975 | FcResult result; |
976 | |
977 | FcConfigSubstitute(config: nullptr, p: pattern, kind: FcMatchPattern); |
978 | FcDefaultSubstitute(pattern); |
979 | |
980 | FcPattern *match = FcFontMatch(config: nullptr, p: pattern, result: &result); |
981 | if (match) { |
982 | engine->setDefaultHintStyle(defaultHintStyleFromMatch(hintingPreference: (QFont::HintingPreference)fontDef.hintingPreference, match, useXftConf)); |
983 | |
984 | FcBool fc_autohint; |
985 | if (FcPatternGetBool(p: match, FC_AUTOHINT,n: 0, b: &fc_autohint) == FcResultMatch) |
986 | engine->forceAutoHint = fc_autohint; |
987 | |
988 | #if defined(FT_LCD_FILTER_H) |
989 | int lcdFilter; |
990 | if (FcPatternGetInteger(p: match, FC_LCD_FILTER, n: 0, i: &lcdFilter) == FcResultMatch) |
991 | engine->lcdFilterType = lcdFilter; |
992 | #endif |
993 | |
994 | if (!forcedAntialiasSetting) { |
995 | FcBool fc_antialias; |
996 | if (FcPatternGetBool(p: match, FC_ANTIALIAS,n: 0, b: &fc_antialias) == FcResultMatch) |
997 | antialias = fc_antialias; |
998 | } |
999 | |
1000 | if (antialias) { |
1001 | QFontEngine::SubpixelAntialiasingType subpixelType = QFontEngine::Subpixel_None; |
1002 | if (!(fontDef.styleStrategy & QFont::NoSubpixelAntialias)) |
1003 | subpixelType = subpixelTypeFromMatch(match, useXftConf); |
1004 | engine->subpixelType = subpixelType; |
1005 | |
1006 | format = (subpixelType == QFontEngine::Subpixel_None) |
1007 | ? QFontEngine::Format_A8 |
1008 | : QFontEngine::Format_A32; |
1009 | } else |
1010 | format = QFontEngine::Format_Mono; |
1011 | |
1012 | FcPatternDestroy(p: match); |
1013 | } else |
1014 | format = antialias ? QFontEngine::Format_A8 : QFontEngine::Format_Mono; |
1015 | |
1016 | FcPatternDestroy(p: pattern); |
1017 | |
1018 | engine->antialias = antialias; |
1019 | engine->defaultFormat = format; |
1020 | engine->glyphFormat = format; |
1021 | } |
1022 | |
1023 | QT_END_NAMESPACE |
1024 | |