8#include <QtCore/qfile.h>
9#include <QtCore/qdir.h>
10#include <QtCore/private/qstdweb_p.h>
11#include <QtCore/private/qeventdispatcher_wasm_p.h>
12#include <QtGui/private/qguiapplication_p.h>
14#include <emscripten.h>
15#include <emscripten/val.h>
16#include <emscripten/bind.h>
23using namespace emscripten;
24using namespace Qt::StringLiterals;
32 FontData(val fontData)
33 :m_fontData(fontData) {}
35 QString family()
const
37 return QString::fromStdString(m_fontData[
"family"].as<std::string>());
40 QString fullName()
const
42 return QString::fromStdString(m_fontData[
"fullName"].as<std::string>());
45 QString postscriptName()
const
47 return QString::fromStdString(m_fontData[
"postscriptName"].as<std::string>());
52 return QString::fromStdString(m_fontData[
"style"].as<std::string>());
64val makeObject(
const char *key,
const char *value)
66 val obj = val::object();
67 obj.set(key, std::string(value));
71void printError(val err) {
73 << QString::fromStdString(err[
"name"].as<std::string>())
74 << QString::fromStdString(err[
"message"].as<std::string>());
75 QWasmFontDatabase::endAllFontFileLoading();
78void checkFontAccessPermitted(
std::function<
void(
bool)> callback)
80 const val permissions = val::global(
"navigator")[
"permissions"];
81 if (permissions.isUndefined()) {
86 qstdweb::Promise::make(permissions,
"query", {
87 .thenFunc = [callback](val status) {
88 callback(status[
"state"].as<std::string>() ==
"granted");
90 }, makeObject(
"name",
"local-fonts"));
93void queryLocalFonts(
std::function<
void(
const QList<FontData> &)> callback)
95 emscripten::val window = emscripten::val::global(
"window");
96 qstdweb::Promise::make(window,
"queryLocalFonts", {
97 .thenFunc = [callback](emscripten::val fontArray) {
98 QList<FontData> fonts;
99 const int count = fontArray[
"length"].as<
int>();
100 fonts.reserve(count);
101 for (
int i = 0; i < count; ++i)
102 fonts.append(FontData(fontArray.call<emscripten::val>(
"at", i)));
105 .catchFunc = printError
109void readBlob(val blob,
std::function<
void(
const QByteArray &)> callback)
111 qstdweb::Promise::make(blob,
"arrayBuffer", {
112 .thenFunc = [callback](emscripten::val fontArrayBuffer) {
113 QByteArray fontData = qstdweb::Uint8Array(qstdweb::ArrayBuffer(fontArrayBuffer)).copyToQByteArray();
116 .catchFunc = printError
120void readFont(FontData font,
std::function<
void(
const QByteArray &)> callback)
122 qstdweb::Promise::make(font.value(),
"blob", {
123 .thenFunc = [callback](val blob) {
124 readBlob(blob, [callback](
const QByteArray &data) {
128 .catchFunc = printError
132emscripten::val getLocalFontsConfigProperty(
const char *name) {
133 emscripten::val qt = val::module_property(
"qt");
134 if (qt.isUndefined())
135 return emscripten::val();
136 emscripten::val localFonts = qt[
"localFonts"];
137 if (localFonts.isUndefined())
138 return emscripten::val();
139 return localFonts[name];
142bool getLocalFontsBoolConfigPropertyWithDefault(
const char *name,
bool defaultValue) {
143 emscripten::val prop = getLocalFontsConfigProperty(name);
144 if (prop.isUndefined())
146 return prop.as<
bool>();
149QString getLocalFontsStringConfigPropertyWithDefault(
const char *name, QString defaultValue) {
150 emscripten::val prop = getLocalFontsConfigProperty(name);
151 if (prop.isUndefined())
153 return QString::fromStdString(prop.as<std::string>());
156QStringList getLocalFontsStringListConfigPropertyWithDefault(
const char *name, QStringList defaultValue) {
157 emscripten::val array = getLocalFontsConfigProperty(name);
158 if (array.isUndefined())
162 int size = array[
"length"].as<
int>();
163 for (
int i = 0; i < size; ++i) {
164 emscripten::val element = array.call<emscripten::val>(
"at", i);
165 QString string = QString::fromStdString(element.as<std::string>());
166 if (!string.isEmpty())
174QWasmFontDatabase::QWasmFontDatabase()
175:QFreeTypeFontDatabase()
177 m_localFontsApiSupported = val::global(
"window")[
"queryLocalFonts"].isUndefined() ==
false;
180QWasmFontDatabase *QWasmFontDatabase::get()
188void QWasmFontDatabase::populateLocalfonts()
191 QStringList selectedLocalFontFamilies;
192 bool allFamilies =
false;
194 switch (m_localFontFamilyLoadSet) {
199 case DefaultFontFamilies: {
200 const QStringList webSafeFontFamilies =
201 {
"Arial",
"Verdana",
"Tahoma",
"Trebuchet",
"Times New Roman",
202 "Georgia",
"Garamond",
"Courier New"};
203 selectedLocalFontFamilies = webSafeFontFamilies;
205 case AllFontFamilies:
210 selectedLocalFontFamilies += m_extraLocalFontFamilies;
212 if (selectedLocalFontFamilies.isEmpty() && !allFamilies) {
213 endAllFontFileLoading();
217 populateLocalFontFamilies(selectedLocalFontFamilies, allFamilies);
221 QStringList toStringList(
emscripten::val array)
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())
235void QWasmFontDatabase::populateLocalFontFamilies(
emscripten::val families)
237 if (!m_localFontsApiSupported)
239 populateLocalFontFamilies(toStringList(families),
false);
242void QWasmFontDatabase::populateLocalFontFamilies(
const QStringList &fontFamilies,
bool allFamilies)
244 queryLocalFonts([fontFamilies, allFamilies](
const QList<FontData> &fonts) {
245 refFontFileLoading();
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());
252 for (
const FontData &font: filteredFonts) {
253 refFontFileLoading();
254 readFont(font, [font](
const QByteArray &fontData){
255 QFreeTypeFontDatabase::registerFontFamily(font.family());
256 QFreeTypeFontDatabase::addTTFile(fontData, QByteArray());
257 derefFontFileLoading();
260 derefFontFileLoading();
265void QWasmFontDatabase::populateFontDatabase()
268 const QString fontFileNames[] = {
269 QStringLiteral(
":/fonts/DejaVuSansMono.ttf"),
270 QStringLiteral(
":/fonts/DejaVuSans.ttf"),
272 for (
const QString &fontFileName : fontFileNames) {
273 QFile theFont(fontFileName);
274 if (!theFont.open(QIODevice::ReadOnly))
277 QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
283 const QDir fontDirectory(fontDir());
284 if (fontDirectory.exists()) {
285 static const QString nameFilters[] = { u"*.ttf"_s, u"*.otf"_s, u"*.ttc"_s };
286 const auto entries = fontDirectory.entryInfoList(
287 QStringList::fromReadOnlyData(nameFilters), QDir::Files);
288 qCDebug(lcQpaFonts) <<
"Loading" << entries.count() <<
"font files from" << fontDir();
289 for (
const QFileInfo &entry : entries) {
290 QFreeTypeFontDatabase::addTTFile(QByteArray(),
291 QFile::encodeName(entry.absoluteFilePath()));
294 qCDebug(lcQpaFonts) <<
"Font directory" << fontDir() <<
"does not exist";
298 m_queryLocalFontsPermission = getLocalFontsBoolConfigPropertyWithDefault(
"requestPermission",
false);
299 QString fontFamilyLoadSet = getLocalFontsStringConfigPropertyWithDefault(
"familiesCollection",
"DefaultFontFamilies");
300 m_extraLocalFontFamilies = getLocalFontsStringListConfigPropertyWithDefault(
"extraFamilies", QStringList());
302 if (fontFamilyLoadSet ==
"NoFontFamilies") {
303 m_localFontFamilyLoadSet = NoFontFamilies;
304 }
else if (fontFamilyLoadSet ==
"DefaultFontFamilies") {
305 m_localFontFamilyLoadSet = DefaultFontFamilies;
306 }
else if (fontFamilyLoadSet ==
"AllFontFamilies") {
307 m_localFontFamilyLoadSet = AllFontFamilies;
309 m_localFontFamilyLoadSet = NoFontFamilies;
310 qWarning() <<
"Unknown fontFamilyLoadSet value" << fontFamilyLoadSet;
313 if (!m_localFontsApiSupported)
319 if (m_queryLocalFontsPermission) {
320 populateLocalfonts();
322 checkFontAccessPermitted([
this](
bool granted) {
324 populateLocalfonts();
326 endAllFontFileLoading();
331QFontEngine *QWasmFontDatabase::fontEngine(
const QFontDef &fontDef,
void *handle)
333 QFontEngine *fontEngine = QFreeTypeFontDatabase::fontEngine(fontDef, handle);
337QStringList QWasmFontDatabase::fallbacksForFamily(
const QString &family, QFont::Style style,
338 QFont::StyleHint styleHint,
339 QFontDatabasePrivate::ExtendedScript script)
const
341 QStringList fallbacks
342 = QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);
346 static const QString wasmFallbackFonts[] = {
"DejaVu Sans" };
347 for (
auto wasmFallbackFont : wasmFallbackFonts) {
348 if (family != wasmFallbackFont && !fallbacks.contains(wasmFallbackFont))
349 fallbacks.append(wasmFallbackFont);
355void QWasmFontDatabase::releaseHandle(
void *handle)
357 QFreeTypeFontDatabase::releaseHandle(handle);
360QFont QWasmFontDatabase::defaultFont()
const
362 return QFont(
"DejaVu Sans"_L1);
366 int g_pendingFonts = 0;
370void QWasmFontDatabase::refFontFileLoading()
377void QWasmFontDatabase::derefFontFileLoading()
379 if (--g_pendingFonts <= 0) {
380 QFontCache::instance()->clear();
381 emit qGuiApp->fontDatabaseChanged();
387void QWasmFontDatabase::endAllFontFileLoading()
389 bool hadPandingfonts = g_pendingFonts > 0;
390 if (hadPandingfonts) {
394 QFontCache::instance()->clear();
395 emit qGuiApp->fontDatabaseChanged();
static QWasmIntegration * get()