Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qwasmfontdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2018 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qwasmfontdatabase.h"
5#include "qwasmintegration.h"
6
7#include <QtCore/qfile.h>
8#include <QtCore/private/qstdweb_p.h>
9#include <QtCore/private/qeventdispatcher_wasm_p.h>
10#include <QtGui/private/qguiapplication_p.h>
11
12#include <emscripten.h>
13#include <emscripten/val.h>
14#include <emscripten/bind.h>
15
16#include <map>
17#include <array>
18
20
21using namespace emscripten;
22using namespace Qt::StringLiterals;
23
24
25namespace {
26
27class FontData
28{
29public:
30 FontData(val fontData)
31 :m_fontData(fontData) {}
32
33 QString family() const
34 {
35 return QString::fromStdString(m_fontData["family"].as<std::string>());
36 }
37
38 QString fullName() const
39 {
40 return QString::fromStdString(m_fontData["fullName"].as<std::string>());
41 }
42
43 QString postscriptName() const
44 {
45 return QString::fromStdString(m_fontData["postscriptName"].as<std::string>());
46 }
47
48 QString style() const
49 {
50 return QString::fromStdString(m_fontData["style"].as<std::string>());
51 }
52
53 val value() const
54 {
55 return m_fontData;
56 }
57
58private:
59 val m_fontData;
60};
61
62val makeObject(const char *key, const char *value)
63{
64 val obj = val::object();
65 obj.set(key, std::string(value));
66 return obj;
67}
68
69void printError(val err) {
70 qCWarning(lcQpaFonts)
71 << QString::fromStdString(err["name"].as<std::string>())
72 << QString::fromStdString(err["message"].as<std::string>());
74}
75
76void checkFontAccessPermitted(std::function<void(bool)> callback)
77{
78 const val permissions = val::global("navigator")["permissions"];
79 if (permissions.isUndefined()) {
80 callback(false);
81 return;
82 }
83
84 qstdweb::Promise::make(permissions, "query", {
85 .thenFunc = [callback](val status) {
86 callback(status["state"].as<std::string>() == "granted");
87 },
88 }, makeObject("name", "local-fonts"));
89}
90
91void queryLocalFonts(std::function<void(const QList<FontData> &)> callback)
92{
93 emscripten::val window = emscripten::val::global("window");
94 qstdweb::Promise::make(window, "queryLocalFonts", {
95 .thenFunc = [callback](emscripten::val fontArray) {
96 QList<FontData> fonts;
97 const int count = fontArray["length"].as<int>();
98 fonts.reserve(count);
99 for (int i = 0; i < count; ++i)
100 fonts.append(FontData(fontArray.call<emscripten::val>("at", i)));
101 callback(fonts);
102 },
103 .catchFunc = printError
104 });
105}
106
107void readBlob(val blob, std::function<void(const QByteArray &)> callback)
108{
109 qstdweb::Promise::make(blob, "arrayBuffer", {
110 .thenFunc = [callback](emscripten::val fontArrayBuffer) {
112 callback(fontData);
113 },
114 .catchFunc = printError
115 });
116}
117
118void readFont(FontData font, std::function<void(const QByteArray &)> callback)
119{
120 qstdweb::Promise::make(font.value(), "blob", {
121 .thenFunc = [callback](val blob) {
122 readBlob(blob, [callback](const QByteArray &data) {
123 callback(data);
124 });
125 },
126 .catchFunc = printError
127 });
128}
129
130emscripten::val getLocalFontsConfigProperty(const char *name) {
131 emscripten::val qt = val::module_property("qt");
132 if (qt.isUndefined())
133 return emscripten::val();
134 emscripten::val localFonts = qt["localFonts"];
135 if (localFonts.isUndefined())
136 return emscripten::val();
137 return localFonts[name];
138};
139
140bool getLocalFontsBoolConfigPropertyWithDefault(const char *name, bool defaultValue) {
141 emscripten::val prop = getLocalFontsConfigProperty(name);
142 if (prop.isUndefined())
143 return defaultValue;
144 return prop.as<bool>();
145};
146
147QString getLocalFontsStringConfigPropertyWithDefault(const char *name, QString defaultValue) {
148 emscripten::val prop = getLocalFontsConfigProperty(name);
149 if (prop.isUndefined())
150 return defaultValue;
151 return QString::fromStdString(prop.as<std::string>());
152};
153
154QStringList getLocalFontsStringListConfigPropertyWithDefault(const char *name, QStringList defaultValue) {
155 emscripten::val array = getLocalFontsConfigProperty(name);
156 if (array.isUndefined())
157 return defaultValue;
158
160 int size = array["length"].as<int>();
161 for (int i = 0; i < size; ++i) {
162 emscripten::val element = array.call<emscripten::val>("at", i);
163 QString string = QString::fromStdString(element.as<std::string>());
164 if (!string.isEmpty())
165 list.append(string);
166 }
167 return list;
168};
169
170} // namespace
171
174{
175 m_localFontsApiSupported = val::global("window")["queryLocalFonts"].isUndefined() == false;
176 if (m_localFontsApiSupported)
178}
179
181{
182 return static_cast<QWasmFontDatabase *>(QWasmIntegration::get()->fontDatabase());
183}
184
185// Populates the font database with local fonts. Will make the browser ask
186// the user for permission if needed. Does nothing if the Local Font Access API
187// is not supported.
189{
190 // Decide which font families to populate based on user preferences
191 QStringList selectedLocalFontFamilies;
192 bool allFamilies = false;
193
194 switch (m_localFontFamilyLoadSet) {
195 case NoFontFamilies:
196 default:
197 // keep empty selectedLocalFontFamilies
198 break;
199 case DefaultFontFamilies: {
200 const QStringList webSafeFontFamilies =
201 {"Arial", "Verdana", "Tahoma", "Trebuchet", "Times New Roman",
202 "Georgia", "Garamond", "Courier New"};
203 selectedLocalFontFamilies = webSafeFontFamilies;
204 } break;
205 case AllFontFamilies:
206 allFamilies = true;
207 break;
208 }
209
210 selectedLocalFontFamilies += m_extraLocalFontFamilies;
211
212 if (selectedLocalFontFamilies.isEmpty() && !allFamilies) {
214 return;
215 }
216
217 populateLocalFontFamilies(selectedLocalFontFamilies, allFamilies);
218}
219
220namespace {
221 QStringList toStringList(emscripten::val array)
222 {
224 int size = array["length"].as<int>();
225 for (int i = 0; i < size; ++i) {
226 emscripten::val element = array.call<emscripten::val>("at", i);
227 QString string = QString::fromStdString(element.as<std::string>());
228 if (!string.isEmpty())
229 list.append(string);
230 }
231 return list;
232 }
233}
234
236{
237 if (!m_localFontsApiSupported)
238 return;
239 populateLocalFontFamilies(toStringList(families), false);
240}
241
242void QWasmFontDatabase::populateLocalFontFamilies(const QStringList &fontFamilies, bool allFamilies)
243{
244 queryLocalFonts([fontFamilies, allFamilies](const QList<FontData> &fonts) {
246 QList<FontData> filteredFonts;
247 std::copy_if(fonts.begin(), fonts.end(), std::back_inserter(filteredFonts),
248 [fontFamilies, allFamilies](FontData fontData) {
249 return allFamilies || fontFamilies.contains(fontData.family());
250 });
251
252 for (const FontData &font: filteredFonts) {
254 readFont(font, [font](const QByteArray &fontData){
258 });
259 }
261 });
262
263}
264
266{
267 // Load bundled font file from resources.
268 const QString fontFileNames[] = {
269 QStringLiteral(":/fonts/DejaVuSansMono.ttf"),
270 QStringLiteral(":/fonts/DejaVuSans.ttf"),
271 };
272 for (const QString &fontFileName : fontFileNames) {
273 QFile theFont(fontFileName);
274 if (!theFont.open(QIODevice::ReadOnly))
275 break;
276
277 QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
278 }
279
280 // Get config options for controlling local fonts usage
281 m_queryLocalFontsPermission = getLocalFontsBoolConfigPropertyWithDefault("requestPermission", false);
282 QString fontFamilyLoadSet = getLocalFontsStringConfigPropertyWithDefault("familiesCollection", "DefaultFontFamilies");
283 m_extraLocalFontFamilies = getLocalFontsStringListConfigPropertyWithDefault("extraFamilies", QStringList());
284
285 if (fontFamilyLoadSet == "NoFontFamilies") {
286 m_localFontFamilyLoadSet = NoFontFamilies;
287 } else if (fontFamilyLoadSet == "DefaultFontFamilies") {
288 m_localFontFamilyLoadSet = DefaultFontFamilies;
289 } else if (fontFamilyLoadSet == "AllFontFamilies") {
290 m_localFontFamilyLoadSet = AllFontFamilies;
291 } else {
292 m_localFontFamilyLoadSet = NoFontFamilies;
293 qWarning() << "Unknown fontFamilyLoadSet value" << fontFamilyLoadSet;
294 }
295
296 if (!m_localFontsApiSupported)
297 return;
298
299 // Populate the font database with local fonts. Either try unconditianlly
300 // if displyaing a fonts permissions dialog at startup is allowed, or else
301 // only if we already have permission.
302 if (m_queryLocalFontsPermission) {
304 } else {
305 checkFontAccessPermitted([this](bool granted) {
306 if (granted)
308 else
310 });
311 }
312}
313
319
321 QFont::StyleHint styleHint,
322 QChar::Script script) const
323{
324 QStringList fallbacks
325 = QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);
326
327 // Add the DejaVuSans.ttf font (loaded in populateFontDatabase above) as a falback font
328 // to all other fonts (except itself).
329 static const QString wasmFallbackFonts[] = { "DejaVu Sans" };
330 for (auto wasmFallbackFont : wasmFallbackFonts) {
331 if (family != wasmFallbackFont && !fallbacks.contains(wasmFallbackFont))
332 fallbacks.append(wasmFallbackFont);
333 }
334
335 return fallbacks;
336}
337
342
344{
345 return QFont("DejaVu Sans"_L1);
346}
347
348namespace {
349 int g_pendingFonts = 0;
350 bool g_fontStartupTaskCompleted = false;
351}
352
353// Registers font loading as a startup task, which makes Qt delay
354// sending onLoaded event until font loading has completed.
356{
357 g_fontStartupTaskCompleted = false;
359}
360
361// Ends the font loading startup task.
363{
364 if (!g_fontStartupTaskCompleted) {
365 g_fontStartupTaskCompleted = true;
367 }
368}
369
370// Registers that a font file will be loaded.
372{
373 g_pendingFonts += 1;
374}
375
376// Registers that one font file has been loaded, and sends notifactions
377// when all pending font files have been loaded.
379{
380 if (--g_pendingFonts <= 0) {
381 QFontCache::instance()->clear();
382 emit qGuiApp->fontDatabaseChanged();
384 }
385}
386
387// Unconditionally ends local font loading, for instance if there
388// are no fonts to load or if there was an unexpected error.
390{
391 bool hadPandingfonts = g_pendingFonts > 0;
392 if (hadPandingfonts) {
393 // The hadPandingfonts counter might no longer be correct; disable counting
394 // and send notifications unconditionally.
395 g_pendingFonts = 0;
396 QFontCache::instance()->clear();
397 emit qGuiApp->fontDatabaseChanged();
398 }
399
401}
402
403
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore
Definition qfile.h:93
static QFontCache * instance()
Definition qfont.cpp:3336
\reentrant
Definition qfont.h:22
StyleHint
Style hints are used by the \l{QFont}{font matching} algorithm to find an appropriate default family ...
Definition qfont.h:25
QString family() const
Returns the requested font family name.
Definition qfont.cpp:817
Style
This enum describes the different styles of glyphs that are used to display text.
Definition qfont.h:76
QFontEngine * fontEngine(const QFontDef &fontDef, void *handle) override
Returns the font engine that can be used to render the font described by the font definition,...
static QStringList addTTFile(const QByteArray &fontData, const QByteArray &file, QFontDatabasePrivate::ApplicationFont *applicationFont=nullptr)
void releaseHandle(void *handle) override
Releases the specified font handle.
void append(parameter_type t)
Definition qlist.h:458
virtual QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const
Returns a list of alternative fonts for the specified family and style and script using the styleHint...
static void registerFontFamily(const QString &familyName)
Registers a font family with the font database.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
static QString fromStdString(const std::string &s)
Definition qstring.h:1447
static void derefFontFileLoading()
static void endAllFontFileLoading()
void populateFontDatabase() override
This function is called once at startup by Qt's internal font database.
static QWasmFontDatabase * get()
QFont defaultFont() const override
Returns the default system font.
static void refFontFileLoading()
QFontEngine * fontEngine(const QFontDef &fontDef, void *handle) override
Returns the font engine that can be used to render the font described by the font definition,...
static void beginFontDatabaseStartupTask()
void releaseHandle(void *handle) override
Releases the specified font handle.
static void endFontDatabaseStartupTask()
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QChar::Script script) const override
Returns a list of alternative fonts for the specified family and style and script using the styleHint...
void populateLocalFontFamilies(emscripten::val families)
static QWasmIntegration * get()
QByteArray copyToQByteArray() const
Definition qstdweb.cpp:672
Combined button and popup list for selecting options.
void make(emscripten::val target, QString methodName, PromiseCallbacks callbacks, Args... args)
Definition qstdweb_p.h:221
QList< QString > QStringList
Constructs a string list that contains the given string, str.
typedef QByteArray(EGLAPIENTRYP PFNQGSGETDISPLAYSPROC)()
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define qGuiApp
#define qWarning
Definition qlogging.h:166
#define qCWarning(category,...)
GLuint64 GLenum void * handle
GLuint64 key
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLenum GLenum GLsizei count
GLuint name
GLenum GLsizeiptr const void * fontData
GLhandleARB obj
[2]
GLuint GLfloat * val
GLenum array
#define QStringLiteral(str)
#define emit
static QStringList toStringList(const QJsonArray &jsonArray)
QList< int > list
[14]
void printError(const char *msg)
[0]
aWidget window() -> setWindowTitle("New Window Title")
[2]
QJSValue fullName