1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 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 "qwinrtfontdatabase_p.h"
41
42 #include <QtFontDatabaseSupport/private/qfontengine_ft_p.h>
43
44 #include <QtCore/QCoreApplication>
45 #include <QtCore/QFile>
46
47 #include <QtCore/QUuid>
48 #include <dwrite_1.h>
49 #include <wrl.h>
50 using namespace Microsoft::WRL;
51
52 QT_BEGIN_NAMESPACE
53
54 Q_LOGGING_CATEGORY(lcQpaFonts, "qt.qpa.fonts")
55
operator <<(QDebug d,const QFontDef & def)56 QDebug operator<<(QDebug d, const QFontDef &def)
57 {
58 QDebugStateSaver saver(d);
59 d.nospace();
60 d << "Family=" << def.family << " Stylename=" << def.styleName
61 << " pointsize=" << def.pointSize << " pixelsize=" << def.pixelSize
62 << " styleHint=" << def.styleHint << " weight=" << def.weight
63 << " stretch=" << def.stretch << " hintingPreference="
64 << def.hintingPreference;
65 return d;
66 }
67
68 // Based on unicode range tables at http://www.microsoft.com/typography/otspec/os2.htm#ur
writingSystemFromUnicodeRange(const DWRITE_UNICODE_RANGE & range)69 static QFontDatabase::WritingSystem writingSystemFromUnicodeRange(const DWRITE_UNICODE_RANGE &range)
70 {
71 if (range.first >= 0x0000 && range.last <= 0x007F)
72 return QFontDatabase::Latin;
73 if (range.first >= 0x0370 && range.last <= 0x03FF)
74 return QFontDatabase::Greek;
75 if (range.first >= 0x0400 && range.last <= 0x04FF)
76 return QFontDatabase::Cyrillic;
77 if (range.first >= 0x0530 && range.last <= 0x058F)
78 return QFontDatabase::Armenian;
79 if (range.first >= 0x0590 && range.last <= 0x05FF)
80 return QFontDatabase::Hebrew;
81 if (range.first >= 0x0600 && range.last <= 0x06FF)
82 return QFontDatabase::Arabic;
83 if (range.first >= 0x0700 && range.last <= 0x074F)
84 return QFontDatabase::Syriac;
85 if (range.first >= 0x0780 && range.last <= 0x07BF)
86 return QFontDatabase::Thaana;
87 if (range.first >= 0x0900 && range.last <= 0x097F)
88 return QFontDatabase::Devanagari;
89 if (range.first >= 0x0980 && range.last <= 0x09FF)
90 return QFontDatabase::Bengali;
91 if (range.first >= 0x0A00 && range.last <= 0x0A7F)
92 return QFontDatabase::Gurmukhi;
93 if (range.first >= 0x0A80 && range.last <= 0x0AFF)
94 return QFontDatabase::Gujarati;
95 if (range.first >= 0x0B00 && range.last <= 0x0B7F)
96 return QFontDatabase::Oriya;
97 if (range.first >= 0x0B80 && range.last <= 0x0BFF)
98 return QFontDatabase::Tamil;
99 if (range.first >= 0x0C00 && range.last <= 0x0C7F)
100 return QFontDatabase::Telugu;
101 if (range.first >= 0x0C80 && range.last <= 0x0CFF)
102 return QFontDatabase::Kannada;
103 if (range.first >= 0x0D00 && range.last <= 0x0D7F)
104 return QFontDatabase::Malayalam;
105 if (range.first >= 0x0D80 && range.last <= 0x0DFF)
106 return QFontDatabase::Sinhala;
107 if (range.first >= 0x0E00 && range.last <= 0x0E7F)
108 return QFontDatabase::Thai;
109 if (range.first >= 0x0E80 && range.last <= 0x0EFF)
110 return QFontDatabase::Lao;
111 if (range.first >= 0x0F00 && range.last <= 0x0FFF)
112 return QFontDatabase::Tibetan;
113 if (range.first >= 0x1000 && range.last <= 0x109F)
114 return QFontDatabase::Myanmar;
115 if (range.first >= 0x10A0 && range.last <= 0x10FF)
116 return QFontDatabase::Georgian;
117 if (range.first >= 0x1780 && range.last <= 0x17FF)
118 return QFontDatabase::Khmer;
119 if (range.first >= 0x4E00 && range.last <= 0x9FFF)
120 return QFontDatabase::SimplifiedChinese;
121 if (range.first >= 0xAC00 && range.last <= 0xD7AF)
122 return QFontDatabase::Korean;
123 if (range.first >= 0x1680 && range.last <= 0x169F)
124 return QFontDatabase::Ogham;
125 if (range.first >= 0x16A0 && range.last <= 0x16FF)
126 return QFontDatabase::Runic;
127 if (range.first >= 0x07C0 && range.last <= 0x07FF)
128 return QFontDatabase::Nko;
129
130 return QFontDatabase::Other;
131 }
132
~QWinRTFontDatabase()133 QWinRTFontDatabase::~QWinRTFontDatabase()
134 {
135 qCDebug(lcQpaFonts) << __FUNCTION__;
136
137 foreach (IDWriteFontFile *fontFile, m_fonts.keys())
138 fontFile->Release();
139
140 foreach (IDWriteFontFamily *fontFamily, m_fontFamilies)
141 fontFamily->Release();
142 }
143
fontDir() const144 QString QWinRTFontDatabase::fontDir() const
145 {
146 qCDebug(lcQpaFonts) << __FUNCTION__;
147 QString fontDirectory = QFreeTypeFontDatabase::fontDir();
148 if (!QFile::exists(fontDirectory)) {
149 // Fall back to app directory + fonts, and just app directory after that
150 const QString applicationDirPath = QCoreApplication::applicationDirPath();
151 fontDirectory = applicationDirPath + QLatin1String("/fonts");
152 if (!QFile::exists(fontDirectory)) {
153 if (m_fontFamilies.isEmpty())
154 qWarning("No fonts directory found in application package.");
155 fontDirectory = applicationDirPath;
156 }
157 }
158 return fontDirectory;
159 }
160
defaultFont() const161 QFont QWinRTFontDatabase::defaultFont() const
162 {
163 return QFont(QStringLiteral("Segoe UI"));
164 }
165
fontsAlwaysScalable() const166 bool QWinRTFontDatabase::fontsAlwaysScalable() const
167 {
168 return true;
169 }
170
populateFontDatabase()171 void QWinRTFontDatabase::populateFontDatabase()
172 {
173 qCDebug(lcQpaFonts) << __FUNCTION__;
174
175 ComPtr<IDWriteFactory1> factory;
176 HRESULT hr = DWriteCreateFactory(DWRITE_FACTORY_TYPE_ISOLATED, __uuidof(IDWriteFactory1), &factory);
177 if (FAILED(hr)) {
178 qWarning("Failed to create DirectWrite factory: %s", qPrintable(qt_error_string(hr)));
179 QFreeTypeFontDatabase::populateFontDatabase();
180 return;
181 }
182
183 ComPtr<IDWriteFontCollection> fontCollection;
184 hr = factory->GetSystemFontCollection(&fontCollection);
185 if (FAILED(hr)) {
186 qWarning("Failed to open system font collection: %s", qPrintable(qt_error_string(hr)));
187 QFreeTypeFontDatabase::populateFontDatabase();
188 return;
189 }
190
191 int fontFamilyCount = fontCollection->GetFontFamilyCount();
192 for (int i = 0; i < fontFamilyCount; ++i) {
193 ComPtr<IDWriteFontFamily> fontFamily;
194 hr = fontCollection->GetFontFamily(i, &fontFamily);
195 if (FAILED(hr)) {
196 qWarning("Unable to get font family: %s", qPrintable(qt_error_string(hr)));
197 continue;
198 }
199
200 ComPtr<IDWriteLocalizedStrings> names;
201 hr = fontFamily->GetFamilyNames(&names);
202 if (FAILED(hr)) {
203 qWarning("Unable to get font family names: %s", qPrintable(qt_error_string(hr)));
204 continue;
205 }
206 quint32 familyNameLength;
207 hr = names->GetStringLength(0, &familyNameLength);
208 if (FAILED(hr)) {
209 qWarning("Unable to get family name length: %s", qPrintable(qt_error_string(hr)));
210 continue;
211 }
212 QVector<wchar_t> familyBuffer(familyNameLength + 1);
213 hr = names->GetString(0, familyBuffer.data(), familyBuffer.size());
214 if (FAILED(hr)) {
215 qWarning("Unable to create font family name: %s", qPrintable(qt_error_string(hr)));
216 continue;
217 }
218 QString familyName = QString::fromWCharArray(familyBuffer.data(), familyNameLength);
219
220 m_fontFamilies.insert(familyName, fontFamily.Detach());
221
222 registerFontFamily(familyName);
223 }
224
225 QFreeTypeFontDatabase::populateFontDatabase();
226 }
227
populateFamily(const QString & familyName)228 void QWinRTFontDatabase::populateFamily(const QString &familyName)
229 {
230 qCDebug(lcQpaFonts) << __FUNCTION__ << familyName;
231
232 IDWriteFontFamily *fontFamily = m_fontFamilies.value(familyName);
233 if (!fontFamily) {
234 qWarning("The font family %s was not found.", qPrintable(familyName));
235 return;
236 }
237
238 bool fontRegistered = false;
239 const int fontCount = fontFamily->GetFontCount();
240 for (int j = 0; j < fontCount; ++j) {
241 ComPtr<IDWriteFont> font;
242 HRESULT hr = fontFamily->GetFont(j, &font);
243 if (FAILED(hr)) {
244 qWarning("Unable to get font: %s", qPrintable(qt_error_string(hr)));
245 continue;
246 }
247
248 // Skip simulated faces
249 if (font->GetSimulations() != DWRITE_FONT_SIMULATIONS_NONE)
250 continue;
251
252 ComPtr<IDWriteFontFace> baseFontFace;
253 hr = font->CreateFontFace(&baseFontFace);
254 if (FAILED(hr)) {
255 qWarning("Unable to create base font face: %s", qPrintable(qt_error_string(hr)));
256 continue;
257 }
258 ComPtr<IDWriteFontFace1> fontFace;
259 hr = baseFontFace.As(&fontFace);
260 if (FAILED(hr)) {
261 qWarning("Unable to create font face: %s", qPrintable(qt_error_string(hr)));
262 continue;
263 }
264
265 // We can't deal with multi-file fonts
266 quint32 fileCount;
267 hr = fontFace->GetFiles(&fileCount, NULL);
268 if (FAILED(hr)) {
269 qWarning("Unable to get font file count: %s", qPrintable(qt_error_string(hr)));
270 continue;
271 }
272 if (fileCount != 1)
273 continue;
274
275 ComPtr<IDWriteLocalizedStrings> informationalStrings;
276 BOOL exists;
277 hr = font->GetInformationalStrings(DWRITE_INFORMATIONAL_STRING_MANUFACTURER,
278 &informationalStrings, &exists);
279 if (FAILED(hr)) {
280 qWarning("Unable to get font foundry: %s", qPrintable(qt_error_string(hr)));
281 continue;
282 }
283 QString foundryName;
284 if (exists) {
285 quint32 length;
286 hr = informationalStrings->GetStringLength(0, &length);
287 if (FAILED(hr))
288 qWarning("Unable to get foundry name length: %s", qPrintable(qt_error_string(hr)));
289 if (SUCCEEDED(hr)) {
290 QVector<wchar_t> buffer(length + 1);
291 hr = informationalStrings->GetString(0, buffer.data(), buffer.size());
292 if (FAILED(hr))
293 qWarning("Unable to get foundry name: %s", qPrintable(qt_error_string(hr)));
294 if (SUCCEEDED(hr))
295 foundryName = QString::fromWCharArray(buffer.data(), length);
296 }
297 }
298
299 QFont::Weight weight = QPlatformFontDatabase::weightFromInteger(font->GetWeight());
300
301 QFont::Style style;
302 switch (font->GetStyle()) {
303 default:
304 case DWRITE_FONT_STYLE_NORMAL:
305 style = QFont::StyleNormal;
306 break;
307 case DWRITE_FONT_STYLE_OBLIQUE:
308 style = QFont::StyleOblique;
309 break;
310 case DWRITE_FONT_STYLE_ITALIC:
311 style = QFont::StyleItalic;
312 break;
313 }
314
315 QFont::Stretch stretch;
316 switch (font->GetStretch()) {
317 default:
318 case DWRITE_FONT_STRETCH_UNDEFINED:
319 case DWRITE_FONT_STRETCH_NORMAL:
320 stretch = QFont::Unstretched;
321 break;
322 case DWRITE_FONT_STRETCH_ULTRA_CONDENSED:
323 stretch = QFont::UltraCondensed;
324 break;
325 case DWRITE_FONT_STRETCH_EXTRA_CONDENSED:
326 stretch = QFont::ExtraCondensed;
327 break;
328 case DWRITE_FONT_STRETCH_CONDENSED:
329 stretch = QFont::Condensed;
330 break;
331 case DWRITE_FONT_STRETCH_SEMI_CONDENSED:
332 stretch = QFont::SemiCondensed;
333 break;
334 case DWRITE_FONT_STRETCH_SEMI_EXPANDED:
335 stretch = QFont::SemiExpanded;
336 break;
337 case DWRITE_FONT_STRETCH_EXPANDED:
338 stretch = QFont::Expanded;
339 break;
340 case DWRITE_FONT_STRETCH_EXTRA_EXPANDED:
341 stretch = QFont::ExtraExpanded;
342 break;
343 case DWRITE_FONT_STRETCH_ULTRA_EXPANDED:
344 stretch = QFont::UltraExpanded;
345 break;
346 }
347
348 const bool fixedPitch = fontFace->IsMonospacedFont();
349
350 // Get writing systems from unicode ranges
351 quint32 actualRangeCount;
352 hr = fontFace->GetUnicodeRanges(0, nullptr, &actualRangeCount);
353 Q_ASSERT(hr == E_NOT_SUFFICIENT_BUFFER);
354 QVector<DWRITE_UNICODE_RANGE> unicodeRanges(actualRangeCount);
355 hr = fontFace->GetUnicodeRanges(actualRangeCount, unicodeRanges.data(), &actualRangeCount);
356 if (FAILED(hr)) {
357 qWarning("Unable to get font unicode range: %s", qPrintable(qt_error_string(hr)));
358 continue;
359 }
360 QSupportedWritingSystems writingSystems;
361 for (quint32 i = 0; i < actualRangeCount; ++i) {
362 const QFontDatabase::WritingSystem writingSystem = writingSystemFromUnicodeRange(unicodeRanges.at(i));
363 writingSystems.setSupported(writingSystem);
364 }
365 if (writingSystems.supported(QFontDatabase::SimplifiedChinese)) {
366 writingSystems.setSupported(QFontDatabase::TraditionalChinese);
367 writingSystems.setSupported(QFontDatabase::Japanese);
368 }
369 if (writingSystems.supported(QFontDatabase::Latin))
370 writingSystems.setSupported(QFontDatabase::Vietnamese);
371
372 IDWriteFontFile *fontFile;
373 hr = fontFace->GetFiles(&fileCount, &fontFile);
374 if (FAILED(hr)) {
375 qWarning("Unable to get font file: %s", qPrintable(qt_error_string(hr)));
376 continue;
377 }
378
379 FontDescription description = { fontFace->GetIndex(), QUuid::createUuid().toByteArray() };
380 m_fonts.insert(fontFile, description);
381 registerFont(familyName, QString(), foundryName, weight, style, stretch,
382 true, true, 0, fixedPitch, writingSystems, fontFile);
383 fontRegistered = true;
384 }
385
386 // Always populate something to avoid an assert
387 if (!fontRegistered) {
388 registerFont(familyName, QString(), QString(), QFont::Normal, QFont::StyleNormal,
389 QFont::Unstretched, false, false, 0, false, QSupportedWritingSystems(), 0);
390 }
391 }
392
fontEngine(const QFontDef & fontDef,void * handle)393 QFontEngine *QWinRTFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
394 {
395 qCDebug(lcQpaFonts) << __FUNCTION__ << "FONTDEF" << fontDef << handle;
396
397 if (!handle) // Happens if a font family population failed
398 return 0;
399
400 IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle);
401 if (!m_fonts.contains(fontFile))
402 return QFreeTypeFontDatabase::fontEngine(fontDef, handle);
403
404 const void *referenceKey;
405 quint32 referenceKeySize;
406 HRESULT hr = fontFile->GetReferenceKey(&referenceKey, &referenceKeySize);
407 if (FAILED(hr)) {
408 qWarning("Unable to get font file reference key: %s", qPrintable(qt_error_string(hr)));
409 return 0;
410 }
411
412 ComPtr<IDWriteFontFileLoader> loader;
413 hr = fontFile->GetLoader(&loader);
414 if (FAILED(hr)) {
415 qWarning("Unable to get font file loader: %s", qPrintable(qt_error_string(hr)));
416 return 0;
417 }
418
419 ComPtr<IDWriteFontFileStream> stream;
420 hr =loader->CreateStreamFromKey(referenceKey, referenceKeySize, &stream);
421 if (FAILED(hr)) {
422 qWarning("Unable to get font file stream: %s", qPrintable(qt_error_string(hr)));
423 return 0;
424 }
425
426 quint64 fileSize;
427 hr = stream->GetFileSize(&fileSize);
428 if (FAILED(hr)) {
429 qWarning("Unable to get font file size: %s", qPrintable(qt_error_string(hr)));
430 return 0;
431 }
432
433 const void *data;
434 void *context;
435 hr = stream->ReadFileFragment(&data, 0, fileSize, &context);
436 if (FAILED(hr)) {
437 qWarning("Unable to get font file data: %s", qPrintable(qt_error_string(hr)));
438 return 0;
439 }
440 const QByteArray fontData((const char *)data, fileSize);
441 stream->ReleaseFileFragment(context);
442
443 QFontEngine::FaceId faceId;
444 const FontDescription description = m_fonts.value(fontFile);
445 faceId.uuid = description.uuid;
446 faceId.index = description.index;
447
448 return QFontEngineFT::create(fontDef, faceId, fontData);
449 }
450
familyForStyleHint(QFont::StyleHint styleHint)451 QString QWinRTFontDatabase::familyForStyleHint(QFont::StyleHint styleHint)
452 {
453 switch (styleHint) {
454 case QFont::Times:
455 return QStringLiteral("Times New Roman");
456 case QFont::Courier:
457 return QStringLiteral("Courier New");
458 case QFont::Monospace:
459 return QStringLiteral("Courier New");
460 case QFont::Cursive:
461 return QStringLiteral("Comic Sans MS");
462 case QFont::Fantasy:
463 return QStringLiteral("Impact");
464 case QFont::Decorative:
465 return QStringLiteral("Old English");
466 case QFont::Helvetica:
467 return QStringLiteral("Segoe UI");
468 case QFont::System:
469 default:
470 break;
471 }
472 return QStringLiteral("Segoe UI");
473 }
474
fallbacksForFamily(const QString & family,QFont::Style style,QFont::StyleHint styleHint,QChar::Script script) const475 QStringList QWinRTFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style,
476 QFont::StyleHint styleHint,
477 QChar::Script script) const
478 {
479 Q_UNUSED(style)
480 Q_UNUSED(script)
481
482 qCDebug(lcQpaFonts) << __FUNCTION__ << family;
483
484 QStringList result;
485 result.append(QWinRTFontDatabase::familyForStyleHint(styleHint));
486 result.append(QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script));
487 return result;
488 }
489
releaseHandle(void * handle)490 void QWinRTFontDatabase::releaseHandle(void *handle)
491 {
492 qCDebug(lcQpaFonts) << __FUNCTION__ << handle;
493
494 if (!handle)
495 return;
496
497 IDWriteFontFile *fontFile = reinterpret_cast<IDWriteFontFile *>(handle);
498 if (m_fonts.contains(fontFile)) {
499 m_fonts.remove(fontFile);
500 fontFile->Release();
501 return;
502 }
503
504 QFreeTypeFontDatabase::releaseHandle(handle);
505 }
506
507 QT_END_NAMESPACE
508