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 | |
14 | #undef QT_NO_FREETYPE |
15 | #include "qfontengine_ft_p.h" |
16 | |
17 | #include <ft2build.h> |
18 | #include FT_TRUETYPE_TABLES_H |
19 | #include FT_ERRORS_H |
20 | |
21 | QT_BEGIN_NAMESPACE |
22 | |
23 | using namespace Qt::StringLiterals; |
24 | |
25 | void QFreeTypeFontDatabase::populateFontDatabase() |
26 | { |
27 | QString fontpath = fontDir(); |
28 | QDir dir(fontpath); |
29 | |
30 | if (!dir.exists()) { |
31 | qWarning(msg: "QFontDatabase: Cannot find font directory %s.\n" |
32 | "Note that Qt no longer ships fonts. Deploy some (from https://dejavu-fonts.github.io/ for example) or switch to fontconfig." , |
33 | qPrintable(fontpath)); |
34 | return; |
35 | } |
36 | |
37 | static const QString nameFilters[] = { |
38 | u"*.ttf"_s , |
39 | u"*.pfa"_s , |
40 | u"*.pfb"_s , |
41 | u"*.otf"_s , |
42 | }; |
43 | |
44 | const auto fis = dir.entryInfoList(nameFilters: QStringList::fromReadOnlyData(t: nameFilters), filters: QDir::Files); |
45 | for (const QFileInfo &fi : fis) { |
46 | const QByteArray file = QFile::encodeName(fileName: fi.absoluteFilePath()); |
47 | QFreeTypeFontDatabase::addTTFile(fontData: QByteArray(), file); |
48 | } |
49 | } |
50 | |
51 | QFontEngine *QFreeTypeFontDatabase::fontEngine(const QFontDef &fontDef, void *usrPtr) |
52 | { |
53 | FontFile *fontfile = static_cast<FontFile *>(usrPtr); |
54 | QFontEngine::FaceId faceId; |
55 | faceId.filename = QFile::encodeName(fileName: fontfile->fileName); |
56 | faceId.index = fontfile->indexValue; |
57 | |
58 | return QFontEngineFT::create(fontDef, faceId, fontData: fontfile->data); |
59 | } |
60 | |
61 | QFontEngine *QFreeTypeFontDatabase::fontEngine(const QByteArray &fontData, qreal pixelSize, |
62 | QFont::HintingPreference hintingPreference) |
63 | { |
64 | return QFontEngineFT::create(fontData, pixelSize, hintingPreference); |
65 | } |
66 | |
67 | QStringList QFreeTypeFontDatabase::addApplicationFont(const QByteArray &fontData, const QString &fileName, QFontDatabasePrivate::ApplicationFont *applicationFont) |
68 | { |
69 | return QFreeTypeFontDatabase::addTTFile(fontData, file: fileName.toLocal8Bit(), applicationFont); |
70 | } |
71 | |
72 | void QFreeTypeFontDatabase::releaseHandle(void *handle) |
73 | { |
74 | FontFile *file = static_cast<FontFile *>(handle); |
75 | delete file; |
76 | } |
77 | |
78 | extern FT_Library qt_getFreetype(); |
79 | |
80 | QStringList QFreeTypeFontDatabase::addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont) |
81 | { |
82 | FT_Library library = qt_getFreetype(); |
83 | |
84 | int index = 0; |
85 | int numFaces = 0; |
86 | QStringList families; |
87 | do { |
88 | FT_Face face; |
89 | FT_Error error; |
90 | if (!fontData.isEmpty()) { |
91 | error = FT_New_Memory_Face(library, file_base: (const FT_Byte *)fontData.constData(), file_size: fontData.size(), face_index: index, aface: &face); |
92 | } else { |
93 | error = FT_New_Face(library, filepathname: file.constData(), face_index: index, aface: &face); |
94 | } |
95 | if (error != FT_Err_Ok) { |
96 | qDebug() << "FT_New_Face failed with index" << index << ':' << Qt::hex << error; |
97 | break; |
98 | } |
99 | numFaces = face->num_faces; |
100 | |
101 | QFont::Weight weight = QFont::Normal; |
102 | |
103 | QFont::Style style = QFont::StyleNormal; |
104 | if (face->style_flags & FT_STYLE_FLAG_ITALIC) |
105 | style = QFont::StyleItalic; |
106 | |
107 | if (face->style_flags & FT_STYLE_FLAG_BOLD) |
108 | weight = QFont::Bold; |
109 | |
110 | bool fixedPitch = (face->face_flags & FT_FACE_FLAG_FIXED_WIDTH); |
111 | QSupportedWritingSystems writingSystems; |
112 | // detect symbol fonts |
113 | for (int i = 0; i < face->num_charmaps; ++i) { |
114 | FT_CharMap cm = face->charmaps[i]; |
115 | if (cm->encoding == FT_ENCODING_ADOBE_CUSTOM |
116 | || cm->encoding == FT_ENCODING_MS_SYMBOL) { |
117 | writingSystems.setSupported(QFontDatabase::Symbol); |
118 | break; |
119 | } |
120 | } |
121 | |
122 | QFont::Stretch stretch = QFont::Unstretched; |
123 | TT_OS2 *os2 = (TT_OS2 *)FT_Get_Sfnt_Table(face, ft_sfnt_os2); |
124 | if (os2) { |
125 | quint32 unicodeRange[4] = { |
126 | quint32(os2->ulUnicodeRange1), |
127 | quint32(os2->ulUnicodeRange2), |
128 | quint32(os2->ulUnicodeRange3), |
129 | quint32(os2->ulUnicodeRange4) |
130 | }; |
131 | quint32 [2] = { |
132 | quint32(os2->ulCodePageRange1), |
133 | quint32(os2->ulCodePageRange2) |
134 | }; |
135 | |
136 | writingSystems = QPlatformFontDatabase::writingSystemsFromTrueTypeBits(unicodeRange, codePageRange); |
137 | |
138 | if (os2->usWeightClass) { |
139 | weight = static_cast<QFont::Weight>(os2->usWeightClass); |
140 | } else if (os2->panose[2]) { |
141 | int w = os2->panose[2]; |
142 | if (w <= 1) |
143 | weight = QFont::Thin; |
144 | else if (w <= 2) |
145 | weight = QFont::ExtraLight; |
146 | else if (w <= 3) |
147 | weight = QFont::Light; |
148 | else if (w <= 5) |
149 | weight = QFont::Normal; |
150 | else if (w <= 6) |
151 | weight = QFont::Medium; |
152 | else if (w <= 7) |
153 | weight = QFont::DemiBold; |
154 | else if (w <= 8) |
155 | weight = QFont::Bold; |
156 | else if (w <= 9) |
157 | weight = QFont::ExtraBold; |
158 | else if (w <= 10) |
159 | weight = QFont::Black; |
160 | } |
161 | |
162 | switch (os2->usWidthClass) { |
163 | case 1: |
164 | stretch = QFont::UltraCondensed; |
165 | break; |
166 | case 2: |
167 | stretch = QFont::ExtraCondensed; |
168 | break; |
169 | case 3: |
170 | stretch = QFont::Condensed; |
171 | break; |
172 | case 4: |
173 | stretch = QFont::SemiCondensed; |
174 | break; |
175 | case 5: |
176 | stretch = QFont::Unstretched; |
177 | break; |
178 | case 6: |
179 | stretch = QFont::SemiExpanded; |
180 | break; |
181 | case 7: |
182 | stretch = QFont::Expanded; |
183 | break; |
184 | case 8: |
185 | stretch = QFont::ExtraExpanded; |
186 | break; |
187 | case 9: |
188 | stretch = QFont::UltraExpanded; |
189 | break; |
190 | } |
191 | } |
192 | |
193 | QString family = QString::fromLatin1(ba: face->family_name); |
194 | FontFile *fontFile = new FontFile{ |
195 | .fileName: QFile::decodeName(localFileName: file), |
196 | .indexValue: index, |
197 | .data: fontData |
198 | }; |
199 | |
200 | QString styleName = QString::fromLatin1(ba: face->style_name); |
201 | |
202 | if (applicationFont != nullptr) { |
203 | QFontDatabasePrivate::ApplicationFont::Properties properties; |
204 | properties.familyName = family; |
205 | properties.styleName = styleName; |
206 | properties.weight = weight; |
207 | properties.stretch = stretch; |
208 | properties.style = style; |
209 | |
210 | applicationFont->properties.append(t: properties); |
211 | } |
212 | |
213 | registerFont(familyname: family, stylename: styleName, foundryname: QString(), weight, style, stretch, antialiased: true, scalable: true, pixelSize: 0, fixedPitch, writingSystems, handle: fontFile); |
214 | families.append(t: family); |
215 | |
216 | FT_Done_Face(face); |
217 | ++index; |
218 | } while (index < numFaces); |
219 | return families; |
220 | } |
221 | |
222 | QT_END_NAMESPACE |
223 | |