8#include <QtCore/qfile.h>
9#include <QtCore/private/qstdweb_p.h>
10#include <QtCore/private/qeventdispatcher_wasm_p.h>
11#include <QtGui/private/qguiapplication_p.h>
13#include <emscripten.h>
14#include <emscripten/val.h>
15#include <emscripten/bind.h>
22using namespace emscripten;
23using namespace Qt::StringLiterals;
31 FontData(val fontData)
32 :m_fontData(fontData) {}
34 QString family()
const
36 return QString::fromStdString(m_fontData[
"family"].as<std::string>());
39 QString fullName()
const
41 return QString::fromStdString(m_fontData[
"fullName"].as<std::string>());
44 QString postscriptName()
const
46 return QString::fromStdString(m_fontData[
"postscriptName"].as<std::string>());
51 return QString::fromStdString(m_fontData[
"style"].as<std::string>());
63val makeObject(
const char *key,
const char *value)
65 val obj = val::object();
66 obj.set(key, std::string(value));
70void printError(val err) {
72 << QString::fromStdString(err[
"name"].as<std::string>())
73 << QString::fromStdString(err[
"message"].as<std::string>());
74 QWasmFontDatabase::endAllFontFileLoading();
77void checkFontAccessPermitted(
std::function<
void(
bool)> callback)
79 const val permissions = val::global(
"navigator")[
"permissions"];
80 if (permissions.isUndefined()) {
85 qstdweb::Promise::make(permissions,
"query", {
86 .thenFunc = [callback](val status) {
87 callback(status[
"state"].as<std::string>() ==
"granted");
89 }, makeObject(
"name",
"local-fonts"));
92void queryLocalFonts(
std::function<
void(
const QList<FontData> &)> callback)
94 emscripten::val window = emscripten::val::global(
"window");
95 qstdweb::Promise::make(window,
"queryLocalFonts", {
96 .thenFunc = [callback](emscripten::val fontArray) {
97 QList<FontData> fonts;
98 const int count = fontArray[
"length"].as<
int>();
100 for (
int i = 0; i < count; ++i)
101 fonts.append(FontData(fontArray.call<emscripten::val>(
"at", i)));
104 .catchFunc = printError
108void readBlob(val blob,
std::function<
void(
const QByteArray &)> callback)
110 qstdweb::Promise::make(blob,
"arrayBuffer", {
111 .thenFunc = [callback](emscripten::val fontArrayBuffer) {
112 QByteArray fontData = qstdweb::Uint8Array(qstdweb::ArrayBuffer(fontArrayBuffer)).copyToQByteArray();
115 .catchFunc = printError
119void readFont(FontData font,
std::function<
void(
const QByteArray &)> callback)
121 qstdweb::Promise::make(font.value(),
"blob", {
122 .thenFunc = [callback](val blob) {
123 readBlob(blob, [callback](
const QByteArray &data) {
127 .catchFunc = printError
131emscripten::val getLocalFontsConfigProperty(
const char *name) {
132 emscripten::val qt = val::module_property(
"qt");
133 if (qt.isUndefined())
134 return emscripten::val();
135 emscripten::val localFonts = qt[
"localFonts"];
136 if (localFonts.isUndefined())
137 return emscripten::val();
138 return localFonts[name];
141bool getLocalFontsBoolConfigPropertyWithDefault(
const char *name,
bool defaultValue) {
142 emscripten::val prop = getLocalFontsConfigProperty(name);
143 if (prop.isUndefined())
145 return prop.as<
bool>();
148QString getLocalFontsStringConfigPropertyWithDefault(
const char *name, QString defaultValue) {
149 emscripten::val prop = getLocalFontsConfigProperty(name);
150 if (prop.isUndefined())
152 return QString::fromStdString(prop.as<std::string>());
155QStringList getLocalFontsStringListConfigPropertyWithDefault(
const char *name, QStringList defaultValue) {
156 emscripten::val array = getLocalFontsConfigProperty(name);
157 if (array.isUndefined())
161 int size = array[
"length"].as<
int>();
162 for (
int i = 0; i < size; ++i) {
163 emscripten::val element = array.call<emscripten::val>(
"at", i);
164 QString string = QString::fromStdString(element.as<std::string>());
165 if (!string.isEmpty())
173QWasmFontDatabase::QWasmFontDatabase()
174:QFreeTypeFontDatabase()
176 m_localFontsApiSupported = val::global(
"window")[
"queryLocalFonts"].isUndefined() ==
false;
177 if (m_localFontsApiSupported)
178 beginFontDatabaseStartupTask();
181QWasmFontDatabase *QWasmFontDatabase::get()
189void QWasmFontDatabase::populateLocalfonts()
192 QStringList selectedLocalFontFamilies;
193 bool allFamilies =
false;
195 switch (m_localFontFamilyLoadSet) {
200 case DefaultFontFamilies: {
201 const QStringList webSafeFontFamilies =
202 {
"Arial",
"Verdana",
"Tahoma",
"Trebuchet",
"Times New Roman",
203 "Georgia",
"Garamond",
"Courier New"};
204 selectedLocalFontFamilies = webSafeFontFamilies;
206 case AllFontFamilies:
211 selectedLocalFontFamilies += m_extraLocalFontFamilies;
213 if (selectedLocalFontFamilies.isEmpty() && !allFamilies) {
214 endAllFontFileLoading();
218 populateLocalFontFamilies(selectedLocalFontFamilies, allFamilies);
222 QStringList toStringList(
emscripten::val array)
225 int size = array[
"length"].as<
int>();
226 for (
int i = 0; i < size; ++i) {
227 emscripten::val element = array.call<emscripten::val>(
"at", i);
228 QString string = QString::fromStdString(element.as<std::string>());
229 if (!string.isEmpty())
236void QWasmFontDatabase::populateLocalFontFamilies(
emscripten::val families)
238 if (!m_localFontsApiSupported)
240 populateLocalFontFamilies(toStringList(families),
false);
243void QWasmFontDatabase::populateLocalFontFamilies(
const QStringList &fontFamilies,
bool allFamilies)
245 queryLocalFonts([fontFamilies, allFamilies](
const QList<FontData> &fonts) {
246 refFontFileLoading();
247 QList<FontData> filteredFonts;
248 std::copy_if(fonts.begin(), fonts.end(), std::back_inserter(filteredFonts),
249 [fontFamilies, allFamilies](FontData fontData) {
250 return allFamilies || fontFamilies.contains(fontData.family());
253 for (
const FontData &font: filteredFonts) {
254 refFontFileLoading();
255 readFont(font, [font](
const QByteArray &fontData){
256 QFreeTypeFontDatabase::registerFontFamily(font.family());
257 QFreeTypeFontDatabase::addTTFile(fontData, QByteArray());
258 derefFontFileLoading();
261 derefFontFileLoading();
266void QWasmFontDatabase::populateFontDatabase()
269 const QString fontFileNames[] = {
270 QStringLiteral(
":/fonts/DejaVuSansMono.ttf"),
271 QStringLiteral(
":/fonts/DejaVuSans.ttf"),
273 for (
const QString &fontFileName : fontFileNames) {
274 QFile theFont(fontFileName);
275 if (!theFont.open(QIODevice::ReadOnly))
278 QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
282 m_queryLocalFontsPermission = getLocalFontsBoolConfigPropertyWithDefault(
"requestPermission",
false);
283 QString fontFamilyLoadSet = getLocalFontsStringConfigPropertyWithDefault(
"familiesCollection",
"DefaultFontFamilies");
284 m_extraLocalFontFamilies = getLocalFontsStringListConfigPropertyWithDefault(
"extraFamilies", QStringList());
286 if (fontFamilyLoadSet ==
"NoFontFamilies") {
287 m_localFontFamilyLoadSet = NoFontFamilies;
288 }
else if (fontFamilyLoadSet ==
"DefaultFontFamilies") {
289 m_localFontFamilyLoadSet = DefaultFontFamilies;
290 }
else if (fontFamilyLoadSet ==
"AllFontFamilies") {
291 m_localFontFamilyLoadSet = AllFontFamilies;
293 m_localFontFamilyLoadSet = NoFontFamilies;
294 qWarning() <<
"Unknown fontFamilyLoadSet value" << fontFamilyLoadSet;
297 if (!m_localFontsApiSupported)
303 if (m_queryLocalFontsPermission) {
304 populateLocalfonts();
306 checkFontAccessPermitted([
this](
bool granted) {
308 populateLocalfonts();
310 endAllFontFileLoading();
315QFontEngine *QWasmFontDatabase::fontEngine(
const QFontDef &fontDef,
void *handle)
317 QFontEngine *fontEngine = QFreeTypeFontDatabase::fontEngine(fontDef, handle);
321QStringList QWasmFontDatabase::fallbacksForFamily(
const QString &family, QFont::Style style,
322 QFont::StyleHint styleHint,
323 QFontDatabasePrivate::ExtendedScript script)
const
325 QStringList fallbacks
326 = QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);
330 static const QString wasmFallbackFonts[] = {
"DejaVu Sans" };
331 for (
auto wasmFallbackFont : wasmFallbackFonts) {
332 if (family != wasmFallbackFont && !fallbacks.contains(wasmFallbackFont))
333 fallbacks.append(wasmFallbackFont);
339void QWasmFontDatabase::releaseHandle(
void *handle)
341 QFreeTypeFontDatabase::releaseHandle(handle);
344QFont QWasmFontDatabase::defaultFont()
const
346 return QFont(
"DejaVu Sans"_L1);
350 int g_pendingFonts = 0;
351 bool g_fontStartupTaskCompleted =
false;
356void QWasmFontDatabase::beginFontDatabaseStartupTask()
358 g_fontStartupTaskCompleted =
false;
359 QEventDispatcherWasm::registerStartupTask();
363void QWasmFontDatabase::endFontDatabaseStartupTask()
365 if (!g_fontStartupTaskCompleted) {
366 g_fontStartupTaskCompleted =
true;
367 QEventDispatcherWasm::completeStarupTask();
372void QWasmFontDatabase::refFontFileLoading()
379void QWasmFontDatabase::derefFontFileLoading()
381 if (--g_pendingFonts <= 0) {
382 QFontCache::instance()->clear();
383 emit qGuiApp->fontDatabaseChanged();
384 endFontDatabaseStartupTask();
390void QWasmFontDatabase::endAllFontFileLoading()
392 bool hadPandingfonts = g_pendingFonts > 0;
393 if (hadPandingfonts) {
397 QFontCache::instance()->clear();
398 emit qGuiApp->fontDatabaseChanged();
401 endFontDatabaseStartupTask();
static QWasmIntegration * get()