Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qfreetypefontdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include <QtGui/private/qguiapplication_p.h>
7#include <qpa/qplatformscreen.h>
8
9#include <QtCore/QFile>
10#include <QtCore/QLibraryInfo>
11#include <QtCore/QDir>
12#include <QtCore/QtEndian>
13#include <QtCore/QLoggingCategory>
14#include <QtCore/QUuid>
15#include <QtCore/QHash>
16
17#undef QT_NO_FREETYPE
19
20#include <ft2build.h>
21#include FT_TRUETYPE_TABLES_H
22#include FT_ERRORS_H
23
24#include FT_MULTIPLE_MASTERS_H
25#include FT_SFNT_NAMES_H
26#include FT_TRUETYPE_IDS_H
27
29
30using namespace Qt::StringLiterals;
31
32void QFreeTypeFontDatabase::populateFontDatabase()
33{
34 QString fontpath = fontDir();
35 QDir dir(fontpath);
36
37 if (!dir.exists()) {
38 qWarning("QFontDatabase: Cannot find font directory %s.\n"
39 "Note that Qt no longer ships fonts. Deploy some (from https://dejavu-fonts.github.io/ for example) or switch to fontconfig.",
40 qPrintable(fontpath));
41 return;
42 }
43
44 static const QString nameFilters[] = {
45 u"*.ttf"_s,
46 u"*.pfa"_s,
47 u"*.pfb"_s,
48 u"*.otf"_s,
49 };
50
51 const auto fis = dir.entryInfoList(QStringList::fromReadOnlyData(nameFilters), QDir::Files);
52 for (const QFileInfo &fi : fis) {
53 const QByteArray file = QFile::encodeName(fi.absoluteFilePath());
54 QFreeTypeFontDatabase::addTTFile(QByteArray(), file);
55 }
56}
57
58QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr)
59{
60 FontFile *fontfile = static_cast<FontFile *>(usrPtr);
61 QFontEngine::FaceId faceId;
62 faceId.filename = QFile::encodeName(fontfile->fileName);
63 faceId.index = fontfile->indexValue;
64 faceId.instanceIndex = fontfile->instanceIndex;
65 faceId.variableAxes = fontDef.variableAxisValues;
66
67 // Make sure the FaceId compares uniquely in cases where a
68 // file name is not provided.
69 if (faceId.filename.isEmpty()) {
70 QUuid::Id128Bytes id{};
71 memcpy(&id, &usrPtr, sizeof(usrPtr));
72 faceId.uuid = QUuid(id).toByteArray();
73 }
74
75 return QFontEngineFT::create(fontDef, faceId, fontfile->data);
76}
77
78QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
79 QFont::HintingPreference hintingPreference)
80{
81 return QFontEngineFT::create(fontData, pixelSize, hintingPreference, {});
82}
83
84QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
85{
86 return QFreeTypeFontDatabase::addTTFile(fontData, fileName.toLocal8Bit(), applicationFont);
87}
88
89void QFreeTypeFontDatabase::releaseHandle(void *handle)
90{
91 FontFile *file = static_cast<FontFile *>(handle);
92 delete file;
93}
94
96
97void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
98 int faceIndex,
99 const QString &family,
100 const QString &styleName,
101 QFont::Weight weight,
102 QFont::Stretch stretch,
103 QFont::Style style,
104 bool fixedPitch,
105 bool isColor,
106 const QSupportedWritingSystems &writingSystems,
107 const QByteArray &fileName,
108 const QByteArray &fontData)
109{
110 FT_Face face = reinterpret_cast<FT_Face>(face_);
111
112 // Note: The following does not actually depend on API from 2.9, but the
113 // FT_Set_Named_Instance() was added in 2.9, so to avoid populating the database with
114 // named instances that cannot be selected, we disable the feature on older Freetype
115 // versions.
116#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
117 FT_MM_Var *var = nullptr;
118 FT_Get_MM_Var(face, &var);
119 if (var != nullptr) {
120 std::unique_ptr<FT_MM_Var, void(*)(FT_MM_Var*)> varGuard(var, [](FT_MM_Var *res) {
121 FT_Done_MM_Var(qt_getFreetype(), res);
122 });
123
124 QMultiHash<FT_UInt, QString> instanceNamesForId;
125 FT_UInt count = FT_Get_Sfnt_Name_Count(face);
126 for (FT_UInt j = 0; j < count; ++j) {
127 FT_SfntName name;
128 if (FT_Get_Sfnt_Name(face, j, &name))
129 continue;
130
131 // Only support UTF-16 for now
132 const bool isUtf16 = (name.platform_id == TT_PLATFORM_MICROSOFT
133 && (name.encoding_id == TT_MS_ID_UNICODE_CS || name.encoding_id == TT_MS_ID_UCS_4))
134 || name.platform_id == TT_PLATFORM_APPLE_UNICODE;
135 if (!isUtf16)
136 continue;
137
138 // Sfnt names stored as UTF-16BE
139 QString instanceName;
140 for (FT_UInt k = 0; k + 1 < name.string_len; k += 2)
141 instanceName += QChar((name.string[k] << 8) + name.string[k + 1]);
142
143 if (instanceName != styleName)
144 instanceNamesForId.insert(name.name_id, instanceName);
145 }
146
147 for (FT_UInt i = 0; i < var->num_namedstyles; ++i) {
148 const FT_UInt id = var->namedstyle[i].strid;
149 QFont::Weight instanceWeight = weight;
150 QFont::Stretch instanceStretch = stretch;
151 QFont::Style instanceStyle = style;
152 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
153 if (var->axis[axis].tag == QFont::Tag("wght").value()) {
154 instanceWeight = QFont::Weight(var->namedstyle[i].coords[axis] >> 16);
155 } else if (var->axis[axis].tag == QFont::Tag("wdth").value()) {
156 instanceStretch = QFont::Stretch(var->namedstyle[i].coords[axis] >> 16);
157 } else if (var->axis[axis].tag == QFont::Tag("ital").value()) {
158 FT_UInt ital = var->namedstyle[i].coords[axis] >> 16;
159 if (ital == 1)
160 instanceStyle = QFont::StyleItalic;
161 else
162 instanceStyle = QFont::StyleNormal;
163 }
164 }
165
166 const auto [begin, end] = std::as_const(instanceNamesForId).equal_range(id);
167 for (auto it = begin; it != end; ++it) {
168 const QString &instanceName = it.value();
169
170 FontFile *variantFontFile = new FontFile{
171 QFile::decodeName(fileName),
172 faceIndex,
173 int(i),
174 fontData
175 };
176
177 qCDebug(lcFontDb) << "Registering named instance" << i
178 << ":" << instanceName
179 << "for font family" << family
180 << "with weight" << instanceWeight
181 << ", style" << instanceStyle
182 << ", stretch" << instanceStretch;
183
184 registerFont(family,
185 instanceName,
186 QString(),
187 instanceWeight,
188 instanceStyle,
189 instanceStretch,
190 true,
191 true,
192 0,
193 fixedPitch,
194 isColor,
195 writingSystems,
196 variantFontFile);
197 }
198 }
199 }
200#else
201 Q_UNUSED(face);
202 Q_UNUSED(family);
203 Q_UNUSED(styleName);
204 Q_UNUSED(weight);
205 Q_UNUSED(stretch);
206 Q_UNUSED(style);
207 Q_UNUSED(fixedPitch);
208 Q_UNUSED(writingSystems);
209 Q_UNUSED(fontData);
210#endif
211
212}
213
214QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont)
215{
216 FT_Library library = qt_getFreetype();
217
218 int index = 0;
219 int numFaces = 0;
220 QStringList families;
221 do {
222 FT_Face face;
223 FT_Error error;
224 if (!fontData.isEmpty()) {
225 error = FT_New_Memory_Face(library, (const FT_Byte *)fontData.constData(), fontData.size(), index, &face);
226 } else {
227 error = FT_New_Face(library, file.constData(), index, &face);
228 }
229 if (error != FT_Err_Ok) {
230 qDebug() << "FT_New_Face failed with index" << index << ':' << Qt::hex << error;
231 break;
232 }
233 numFaces = face->num_faces;
234
235#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20501
236 bool isColor = FT_HAS_COLOR(face);
237#else
238 bool isColor = false;
239#endif
240
241 QFont::Weight weight = QFont::Normal;
242
243 QFont::Style style = QFont::StyleNormal;
244 if (face->style_flags & FT_STYLE_FLAG_ITALIC)
245 style = QFont::StyleItalic;
246
247 if (face->style_flags & FT_STYLE_FLAG_BOLD)
248 weight = QFont::Bold;
249
250 bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
251 QSupportedWritingSystems writingSystems;
252 // detect symbol fonts
253 for (int i = 0; i < face->num_charmaps; ++i) {
254 FT_CharMap cm = face->charmaps[i];
255 if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM
256 || cm->encoding == FT_ENCODING_MS_SYMBOL) {
257 writingSystems.setSupported(QFontDatabase::Symbol);
258 break;
259 }
260 }
261
262 QFont::Stretch stretch = QFont::Unstretched;
263 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
264 if (os2) {
265 quint32 unicodeRange[4] = {
266 quint32(os2->ulUnicodeRange1),
267 quint32(os2->ulUnicodeRange2),
268 quint32(os2->ulUnicodeRange3),
269 quint32(os2->ulUnicodeRange4)
270 };
271 quint32 codePageRange[2] = {
272 quint32(os2->ulCodePageRange1),
273 quint32(os2->ulCodePageRange2)
274 };
275
276 writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
277
278 if (os2->usWeightClass) {
279 weight = static_cast<QFont::Weight>(os2->usWeightClass);
280 } else if (os2->panose[2]) {
281 int w = os2->panose[2];
282 if (w <= 1)
283 weight = QFont::Thin;
284 else if (w <= 2)
285 weight = QFont::ExtraLight;
286 else if (w <= 3)
287 weight = QFont::Light;
288 else if (w <= 5)
289 weight = QFont::Normal;
290 else if (w <= 6)
291 weight = QFont::Medium;
292 else if (w <= 7)
293 weight = QFont::DemiBold;
294 else if (w <= 8)
295 weight = QFont::Bold;
296 else if (w <= 9)
297 weight = QFont::ExtraBold;
298 else if (w <= 10)
299 weight = QFont::Black;
300 }
301
302 switch (os2->usWidthClass) {
303 case 1:
304 stretch = QFont::UltraCondensed;
305 break;
306 case 2:
307 stretch = QFont::ExtraCondensed;
308 break;
309 case 3:
310 stretch = QFont::Condensed;
311 break;
312 case 4:
313 stretch = QFont::SemiCondensed;
314 break;
315 case 5:
316 stretch = QFont::Unstretched;
317 break;
318 case 6:
319 stretch = QFont::SemiExpanded;
320 break;
321 case 7:
322 stretch = QFont::Expanded;
323 break;
324 case 8:
325 stretch = QFont::ExtraExpanded;
326 break;
327 case 9:
328 stretch = QFont::UltraExpanded;
329 break;
330 }
331 }
332
333 QString family = QString::fromLatin1(face->family_name);
334 FontFile *fontFile = new FontFile{
335 QFile::decodeName(file),
336 index,
337 -1,
338 fontData
339 };
340
341 QString styleName = QString::fromLatin1(face->style_name);
342
343 if (applicationFont != nullptr) {
344 QFontDatabasePrivate::ApplicationFont::Properties properties;
345 properties.familyName = family;
346 properties.styleName = styleName;
347 properties.weight = weight;
348 properties.stretch = stretch;
349 properties.style = style;
350
351 applicationFont->properties.append(properties);
352 }
353
354 registerFont(family, styleName, QString(), weight, style, stretch, true, true, 0, fixedPitch, isColor, writingSystems, fontFile);
355
356 addNamedInstancesForFace(face, index, family, styleName, weight, stretch, style, fixedPitch, isColor, writingSystems, file, fontData);
357
358 families.append(family);
359
360 FT_Done_Face(face);
361 ++index;
362 } while (index < numFaces);
363 return families;
364}
365
366bool QFreeTypeFontDatabase::supportsColrv0Fonts() const
367{
368#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 21000
369 return true;
370#else
371 return false;
372#endif
373}
374
375bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const
376{
377#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
378 return true;
379#else
380 return false;
381#endif
382}
383
384QT_END_NAMESPACE
Combined button and popup list for selecting options.
FT_Library qt_getFreetype()