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