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
4#include "qfreetypefontdatabase_p.h"
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
17#include "qfontengine_ft_p.h"
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
27QT_BEGIN_NAMESPACE
28
29Q_DECLARE_LOGGING_CATEGORY(lcFontDb)
30
31using namespace Qt::StringLiterals;
32
33void QFreeTypeFontDatabase::populateFontDatabase()
34{
35 QString fontpath = fontDir();
36 QDir dir(fontpath);
37
38 if (!dir.exists()) {
39 qWarning(msg: "QFontDatabase: Cannot find font directory %s.\n"
40 "Note that Qt no longer ships fonts. Deploy some (from https://dejavu-fonts.github.io/ for example) or switch to fontconfig.",
41 qPrintable(fontpath));
42 return;
43 }
44
45 static const QString nameFilters[] = {
46 u"*.ttf"_s,
47 u"*.pfa"_s,
48 u"*.pfb"_s,
49 u"*.otf"_s,
50 };
51
52 const auto fis = dir.entryInfoList(nameFilters: QStringList::fromReadOnlyData(t: nameFilters), filters: QDir::Files);
53 for (const QFileInfo &fi : fis) {
54 const QByteArray file = QFile::encodeName(fileName: fi.absoluteFilePath());
55 QFreeTypeFontDatabase::addTTFile(fontData: QByteArray(), file);
56 }
57}
58
59QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr)
60{
61 FontFile *fontfile = static_cast<FontFile *>(usrPtr);
62 QFontEngine::FaceId faceId;
63 faceId.filename = QFile::encodeName(fileName: fontfile->fileName);
64 faceId.index = fontfile->indexValue;
65 faceId.instanceIndex = fontfile->instanceIndex;
66 faceId.variableAxes = fontDef.variableAxisValues;
67
68 // Make sure the FaceId compares uniquely in cases where a
69 // file name is not provided.
70 if (faceId.filename.isEmpty()) {
71 QUuid::Id128Bytes id{};
72 memcpy(dest: &id, src: &usrPtr, n: sizeof(usrPtr));
73 faceId.uuid = QUuid(id).toByteArray();
74 }
75
76 return QFontEngineFT::create(fontDef, faceId, fontData: fontfile->data);
77}
78
79QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize,
80 QFont::HintingPreference hintingPreference)
81{
82 return QFontEngineFT::create(fontData, pixelSize, hintingPreference, variableAxisValue: {});
83}
84
85QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont)
86{
87 return QFreeTypeFontDatabase::addTTFile(fontData, file: fileName.toLocal8Bit(), applicationFont);
88}
89
90void QFreeTypeFontDatabase::releaseHandle(void *handle)
91{
92 FontFile *file = static_cast<FontFile *>(handle);
93 delete file;
94}
95
96extern FT_Library qt_getFreetype();
97
98void QFreeTypeFontDatabase::addNamedInstancesForFace(void *face_,
99 int faceIndex,
100 const QString &family,
101 const QString &styleName,
102 QFont::Weight weight,
103 QFont::Stretch stretch,
104 QFont::Style style,
105 bool fixedPitch,
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, amaster: &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(library: qt_getFreetype(), amaster: res);
122 });
123
124 for (FT_UInt i = 0; i < var->num_namedstyles; ++i) {
125 FT_UInt id = var->namedstyle[i].strid;
126
127 QFont::Weight instanceWeight = weight;
128 QFont::Stretch instanceStretch = stretch;
129 QFont::Style instanceStyle = style;
130 for (FT_UInt axis = 0; axis < var->num_axis; ++axis) {
131 if (var->axis[axis].tag == QFont::Tag("wght").value()) {
132 instanceWeight = QFont::Weight(var->namedstyle[i].coords[axis] >> 16);
133 } else if (var->axis[axis].tag == QFont::Tag("wdth").value()) {
134 instanceStretch = QFont::Stretch(var->namedstyle[i].coords[axis] >> 16);
135 } else if (var->axis[axis].tag == QFont::Tag("ital").value()) {
136 FT_UInt ital = var->namedstyle[i].coords[axis] >> 16;
137 if (ital == 1)
138 instanceStyle = QFont::StyleItalic;
139 else
140 instanceStyle = QFont::StyleNormal;
141 }
142 }
143
144 FT_UInt count = FT_Get_Sfnt_Name_Count(face);
145 for (FT_UInt j = 0; j < count; ++j) {
146 FT_SfntName name;
147 if (FT_Get_Sfnt_Name(face, idx: j, aname: &name))
148 continue;
149
150 if (name.name_id != id)
151 continue;
152
153 // Only support Unicode for now
154 if (name.encoding_id != TT_MS_ID_UNICODE_CS)
155 continue;
156
157 // Sfnt names stored as UTF-16BE
158 QString instanceName;
159 for (FT_UInt k = 0; k < name.string_len; k += 2)
160 instanceName += QChar((name.string[k] << 8) + name.string[k + 1]);
161 if (instanceName != styleName) {
162 FontFile *variantFontFile = new FontFile{
163 .fileName: QFile::decodeName(localFileName: fileName),
164 .indexValue: faceIndex,
165 .instanceIndex: int(i),
166 .data: fontData
167 };
168
169 qCDebug(lcFontDb) << "Registering named instance" << i
170 << ":" << instanceName
171 << "for font family" << family
172 << "with weight" << instanceWeight
173 << ", style" << instanceStyle
174 << ", stretch" << instanceStretch;
175
176 registerFont(familyname: family,
177 stylename: instanceName,
178 foundryname: QString(),
179 weight: instanceWeight,
180 style: instanceStyle,
181 stretch: instanceStretch,
182 antialiased: true,
183 scalable: true,
184 pixelSize: 0,
185 fixedPitch,
186 writingSystems,
187 handle: 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, file_base: (const FT_Byte *)fontData.constData(), file_size: fontData.size(), face_index: index, aface: &face);
218 } else {
219 error = FT_New_Face(library, filepathname: file.constData(), face_index: index, aface: &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 QFont::Weight weight = QFont::Normal;
228
229 QFont::Style style = QFont::StyleNormal;
230 if (face->style_flags & FT_STYLE_FLAG_ITALIC)
231 style = QFont::StyleItalic;
232
233 if (face->style_flags & FT_STYLE_FLAG_BOLD)
234 weight = QFont::Bold;
235
236 bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH);
237 QSupportedWritingSystems writingSystems;
238 // detect symbol fonts
239 for (int i = 0; i < face->num_charmaps; ++i) {
240 FT_CharMap cm = face->charmaps[i];
241 if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM
242 || cm->encoding == FT_ENCODING_MS_SYMBOL) {
243 writingSystems.setSupported(QFontDatabase::Symbol);
244 break;
245 }
246 }
247
248 QFont::Stretch stretch = QFont::Unstretched;
249 TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2);
250 if (os2) {
251 quint32 unicodeRange[4] = {
252 quint32(os2->ulUnicodeRange1),
253 quint32(os2->ulUnicodeRange2),
254 quint32(os2->ulUnicodeRange3),
255 quint32(os2->ulUnicodeRange4)
256 };
257 quint32 codePageRange[2] = {
258 quint32(os2->ulCodePageRange1),
259 quint32(os2->ulCodePageRange2)
260 };
261
262 writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange);
263
264 if (os2->usWeightClass) {
265 weight = static_cast<QFont::Weight>(os2->usWeightClass);
266 } else if (os2->panose[2]) {
267 int w = os2->panose[2];
268 if (w <= 1)
269 weight = QFont::Thin;
270 else if (w <= 2)
271 weight = QFont::ExtraLight;
272 else if (w <= 3)
273 weight = QFont::Light;
274 else if (w <= 5)
275 weight = QFont::Normal;
276 else if (w <= 6)
277 weight = QFont::Medium;
278 else if (w <= 7)
279 weight = QFont::DemiBold;
280 else if (w <= 8)
281 weight = QFont::Bold;
282 else if (w <= 9)
283 weight = QFont::ExtraBold;
284 else if (w <= 10)
285 weight = QFont::Black;
286 }
287
288 switch (os2->usWidthClass) {
289 case 1:
290 stretch = QFont::UltraCondensed;
291 break;
292 case 2:
293 stretch = QFont::ExtraCondensed;
294 break;
295 case 3:
296 stretch = QFont::Condensed;
297 break;
298 case 4:
299 stretch = QFont::SemiCondensed;
300 break;
301 case 5:
302 stretch = QFont::Unstretched;
303 break;
304 case 6:
305 stretch = QFont::SemiExpanded;
306 break;
307 case 7:
308 stretch = QFont::Expanded;
309 break;
310 case 8:
311 stretch = QFont::ExtraExpanded;
312 break;
313 case 9:
314 stretch = QFont::UltraExpanded;
315 break;
316 }
317 }
318
319 QString family = QString::fromLatin1(ba: face->family_name);
320 FontFile *fontFile = new FontFile{
321 .fileName: QFile::decodeName(localFileName: file),
322 .indexValue: index,
323 .instanceIndex: -1,
324 .data: fontData
325 };
326
327 QString styleName = QString::fromLatin1(ba: face->style_name);
328
329 if (applicationFont != nullptr) {
330 QFontDatabasePrivate::ApplicationFont::Properties properties;
331 properties.familyName = family;
332 properties.styleName = styleName;
333 properties.weight = weight;
334 properties.stretch = stretch;
335 properties.style = style;
336
337 applicationFont->properties.append(t: properties);
338 }
339
340 registerFont(familyname: family, stylename: styleName, foundryname: QString(), weight, style, stretch, antialiased: true, scalable: true, pixelSize: 0, fixedPitch, writingSystems, handle: fontFile);
341
342 addNamedInstancesForFace(face_: face, faceIndex: index, family, styleName, weight, stretch, style, fixedPitch, writingSystems, fileName: file, fontData);
343
344 families.append(t: family);
345
346 FT_Done_Face(face);
347 ++index;
348 } while (index < numFaces);
349 return families;
350}
351
352bool QFreeTypeFontDatabase::supportsVariableApplicationFonts() const
353{
354#if (FREETYPE_MAJOR*10000 + FREETYPE_MINOR*100 + FREETYPE_PATCH) >= 20900
355 return true;
356#else
357 return false;
358#endif
359}
360
361QT_END_NAMESPACE
362

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/gui/text/freetype/qfreetypefontdatabase.cpp