diff options
Diffstat (limited to 'src/plugins/platforms/wasm/qwasmfontdatabase.cpp')
-rw-r--r-- | src/plugins/platforms/wasm/qwasmfontdatabase.cpp | 328 |
1 files changed, 262 insertions, 66 deletions
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp index c0833a65ca..3f3dc10f71 100644 --- a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp +++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp @@ -6,6 +6,7 @@ #include <QtCore/qfile.h> #include <QtCore/private/qstdweb_p.h> +#include <QtCore/private/qeventdispatcher_wasm_p.h> #include <QtGui/private/qguiapplication_p.h> #include <emscripten.h> @@ -23,10 +24,40 @@ using namespace Qt::StringLiterals; namespace { -bool isLocalFontsAPISupported() +class FontData { - return val::global("window")["queryLocalFonts"].isUndefined() == false; -} +public: + FontData(val fontData) + :m_fontData(fontData) {} + + QString family() const + { + return QString::fromStdString(m_fontData["family"].as<std::string>()); + } + + QString fullName() const + { + return QString::fromStdString(m_fontData["fullName"].as<std::string>()); + } + + QString postscriptName() const + { + return QString::fromStdString(m_fontData["postscriptName"].as<std::string>()); + } + + QString style() const + { + return QString::fromStdString(m_fontData["style"].as<std::string>()); + } + + val value() const + { + return m_fontData; + } + +private: + val m_fontData; +}; val makeObject(const char *key, const char *value) { @@ -35,52 +66,38 @@ val makeObject(const char *key, const char *value) return obj; } -std::multimap<QString, emscripten::val> makeFontFamilyMap(const QList<val> &fonts) -{ - std::multimap<QString, emscripten::val> fontFamilies; - for (auto font : fonts) { - QString family = QString::fromStdString(font["family"].as<std::string>()); - fontFamilies.insert(std::make_pair(family, font)); - } - return fontFamilies; -} - void printError(val err) { qCWarning(lcQpaFonts) << QString::fromStdString(err["name"].as<std::string>()) << QString::fromStdString(err["message"].as<std::string>()); + QWasmFontDatabase::endAllFontFileLoading(); } -std::array<const char *, 8> webSafeFontFamilies() -{ - return {"Arial", "Verdana", "Tahoma", "Trebuchet", "Times New Roman", - "Georgia", "Garamond", "Courier New"}; -} - -void checkFontAccessPermitted(std::function<void()> callback) +void checkFontAccessPermitted(std::function<void(bool)> callback) { const val permissions = val::global("navigator")["permissions"]; - if (permissions.isUndefined()) + if (permissions.isUndefined()) { + callback(false); return; + } qstdweb::Promise::make(permissions, "query", { .thenFunc = [callback](val status) { - if (status["state"].as<std::string>() == "granted") - callback(); - } + callback(status["state"].as<std::string>() == "granted"); + }, }, makeObject("name", "local-fonts")); } -void queryLocalFonts(std::function<void(const QList<val> &)> callback) +void queryLocalFonts(std::function<void(const QList<FontData> &)> callback) { emscripten::val window = emscripten::val::global("window"); qstdweb::Promise::make(window, "queryLocalFonts", { .thenFunc = [callback](emscripten::val fontArray) { - QList<val> fonts; + QList<FontData> fonts; const int count = fontArray["length"].as<int>(); fonts.reserve(count); for (int i = 0; i < count; ++i) - fonts.append(fontArray.call<emscripten::val>("at", i)); + fonts.append(FontData(fontArray.call<emscripten::val>("at", i))); callback(fonts); }, .catchFunc = printError @@ -98,9 +115,9 @@ void readBlob(val blob, std::function<void(const QByteArray &)> callback) }); } -void readFont(val font, std::function<void(const QByteArray &)> callback) +void readFont(FontData font, std::function<void(const QByteArray &)> callback) { - qstdweb::Promise::make(font, "blob", { + qstdweb::Promise::make(font.value(), "blob", { .thenFunc = [callback](val blob) { readBlob(blob, [callback](const QByteArray &data) { callback(data); @@ -110,49 +127,146 @@ void readFont(val font, std::function<void(const QByteArray &)> callback) }); } +emscripten::val getLocalFontsConfigProperty(const char *name) { + emscripten::val qt = val::module_property("qt"); + if (qt.isUndefined()) + return emscripten::val(); + emscripten::val localFonts = qt["localFonts"]; + if (localFonts.isUndefined()) + return emscripten::val(); + return localFonts[name]; +}; + +bool getLocalFontsBoolConfigPropertyWithDefault(const char *name, bool defaultValue) { + emscripten::val prop = getLocalFontsConfigProperty(name); + if (prop.isUndefined()) + return defaultValue; + return prop.as<bool>(); +}; + +QString getLocalFontsStringConfigPropertyWithDefault(const char *name, QString defaultValue) { + emscripten::val prop = getLocalFontsConfigProperty(name); + if (prop.isUndefined()) + return defaultValue; + return QString::fromStdString(prop.as<std::string>()); +}; + +QStringList getLocalFontsStringListConfigPropertyWithDefault(const char *name, QStringList defaultValue) { + emscripten::val array = getLocalFontsConfigProperty(name); + if (array.isUndefined()) + return defaultValue; + + QStringList list; + int size = array["length"].as<int>(); + for (int i = 0; i < size; ++i) { + emscripten::val element = array.call<emscripten::val>("at", i); + QString string = QString::fromStdString(element.as<std::string>()); + if (!string.isEmpty()) + list.append(string); + } + return list; +}; + } // namespace +QWasmFontDatabase::QWasmFontDatabase() +:QFreeTypeFontDatabase() +{ + m_localFontsApiSupported = val::global("window")["queryLocalFonts"].isUndefined() == false; + if (m_localFontsApiSupported) + beginFontDatabaseStartupTask(); +} + +QWasmFontDatabase *QWasmFontDatabase::get() +{ + return static_cast<QWasmFontDatabase *>(QWasmIntegration::get()->fontDatabase()); +} + +// Populates the font database with local fonts. Will make the browser ask +// the user for permission if needed. Does nothing if the Local Font Access API +// is not supported. void QWasmFontDatabase::populateLocalfonts() { - if (!isLocalFontsAPISupported()) + // Decide which font families to populate based on user preferences + QStringList selectedLocalFontFamilies; + bool allFamilies = false; + + switch (m_localFontFamilyLoadSet) { + case NoFontFamilies: + default: + // keep empty selectedLocalFontFamilies + break; + case DefaultFontFamilies: { + const QStringList webSafeFontFamilies = + {"Arial", "Verdana", "Tahoma", "Trebuchet", "Times New Roman", + "Georgia", "Garamond", "Courier New"}; + selectedLocalFontFamilies = webSafeFontFamilies; + } break; + case AllFontFamilies: + allFamilies = true; + break; + } + + selectedLocalFontFamilies += m_extraLocalFontFamilies; + + if (selectedLocalFontFamilies.isEmpty() && !allFamilies) { + endAllFontFileLoading(); return; + } - // Run the font population code if local font access has been - // permitted. This does not request permission, since we are currently - // starting up and should not display a permission request dialog at - // this point. - checkFontAccessPermitted([](){ - queryLocalFonts([](const QList<val> &fonts){ - auto fontFamilies = makeFontFamilyMap(fonts); - // Populate some font families. We can't populate _all_ fonts as in-memory fonts, - // since that would require several gigabytes of memory. Instead, populate - // a subset of the available fonts. - for (const QString &family: webSafeFontFamilies()) { - auto fontsRange = fontFamilies.equal_range(family); - if (fontsRange.first != fontsRange.second) - QFreeTypeFontDatabase::registerFontFamily(family); - - for (auto it = fontsRange.first; it != fontsRange.second; ++it) { - const val font = it->second; - readFont(font, [](const QByteArray &fontData){ - QFreeTypeFontDatabase::addTTFile(fontData, QByteArray()); - QWasmFontDatabase::notifyFontsChanged(); - }); - } - } + populateLocalFontFamilies(selectedLocalFontFamilies, allFamilies); +} + +namespace { + QStringList toStringList(emscripten::val array) + { + QStringList list; + int size = array["length"].as<int>(); + for (int i = 0; i < size; ++i) { + emscripten::val element = array.call<emscripten::val>("at", i); + QString string = QString::fromStdString(element.as<std::string>()); + if (!string.isEmpty()) + list.append(string); + } + return list; + } +} + +void QWasmFontDatabase::populateLocalFontFamilies(emscripten::val families) +{ + if (!m_localFontsApiSupported) + return; + populateLocalFontFamilies(toStringList(families), false); +} + +void QWasmFontDatabase::populateLocalFontFamilies(const QStringList &fontFamilies, bool allFamilies) +{ + queryLocalFonts([fontFamilies, allFamilies](const QList<FontData> &fonts) { + refFontFileLoading(); + QList<FontData> filteredFonts; + std::copy_if(fonts.begin(), fonts.end(), std::back_inserter(filteredFonts), + [fontFamilies, allFamilies](FontData fontData) { + return allFamilies || fontFamilies.contains(fontData.family()); }); + + for (const FontData &font: filteredFonts) { + refFontFileLoading(); + readFont(font, [font](const QByteArray &fontData){ + QFreeTypeFontDatabase::registerFontFamily(font.family()); + QFreeTypeFontDatabase::addTTFile(fontData, QByteArray()); + derefFontFileLoading(); + }); + } + derefFontFileLoading(); }); + } void QWasmFontDatabase::populateFontDatabase() { - // Load font file from resources. Currently - // all fonts needs to be bundled with the nexe - // as Qt resources. - + // Load bundled font file from resources. const QString fontFileNames[] = { QStringLiteral(":/fonts/DejaVuSansMono.ttf"), - QStringLiteral(":/fonts/Vera.ttf"), QStringLiteral(":/fonts/DejaVuSans.ttf"), }; for (const QString &fontFileName : fontFileNames) { @@ -163,12 +277,44 @@ void QWasmFontDatabase::populateFontDatabase() QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1()); } - populateLocalfonts(); + // Get config options for controlling local fonts usage + m_queryLocalFontsPermission = getLocalFontsBoolConfigPropertyWithDefault("requestPermission", false); + QString fontFamilyLoadSet = getLocalFontsStringConfigPropertyWithDefault("familiesCollection", "DefaultFontFamilies"); + m_extraLocalFontFamilies = getLocalFontsStringListConfigPropertyWithDefault("extraFamilies", QStringList()); + + if (fontFamilyLoadSet == "NoFontFamilies") { + m_localFontFamilyLoadSet = NoFontFamilies; + } else if (fontFamilyLoadSet == "DefaultFontFamilies") { + m_localFontFamilyLoadSet = DefaultFontFamilies; + } else if (fontFamilyLoadSet == "AllFontFamilies") { + m_localFontFamilyLoadSet = AllFontFamilies; + } else { + m_localFontFamilyLoadSet = NoFontFamilies; + qWarning() << "Unknown fontFamilyLoadSet value" << fontFamilyLoadSet; + } + + if (!m_localFontsApiSupported) + return; + + // Populate the font database with local fonts. Either try unconditianlly + // if displyaing a fonts permissions dialog at startup is allowed, or else + // only if we already have permission. + if (m_queryLocalFontsPermission) { + populateLocalfonts(); + } else { + checkFontAccessPermitted([this](bool granted) { + if (granted) + populateLocalfonts(); + else + endAllFontFileLoading(); + }); + } } QFontEngine *QWasmFontDatabase::fontEngine(const QFontDef &fontDef, void *handle) { - return QFreeTypeFontDatabase::fontEngine(fontDef, handle); + QFontEngine *fontEngine = QFreeTypeFontDatabase::fontEngine(fontDef, handle); + return fontEngine; } QStringList QWasmFontDatabase::fallbacksForFamily(const QString &family, QFont::Style style, @@ -178,9 +324,9 @@ QStringList QWasmFontDatabase::fallbacksForFamily(const QString &family, QFont:: QStringList fallbacks = QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script); - // Add the vera.ttf and DejaVuSans.ttf fonts (loaded in populateFontDatabase above) as falback fonts + // Add the DejaVuSans.ttf font (loaded in populateFontDatabase above) as a falback font // to all other fonts (except itself). - static const QString wasmFallbackFonts[] = { "Bitstream Vera Sans", "DejaVu Sans" }; + static const QString wasmFallbackFonts[] = { "DejaVu Sans" }; for (auto wasmFallbackFont : wasmFallbackFonts) { if (family != wasmFallbackFont && !fallbacks.contains(wasmFallbackFont)) fallbacks.append(wasmFallbackFont); @@ -196,13 +342,63 @@ void QWasmFontDatabase::releaseHandle(void *handle) QFont QWasmFontDatabase::defaultFont() const { - return QFont("Bitstream Vera Sans"_L1); + return QFont("DejaVu Sans"_L1); +} + +namespace { + int g_pendingFonts = 0; + bool g_fontStartupTaskCompleted = false; +} + +// Registers font loading as a startup task, which makes Qt delay +// sending onLoaded event until font loading has completed. +void QWasmFontDatabase::beginFontDatabaseStartupTask() +{ + g_fontStartupTaskCompleted = false; + QEventDispatcherWasm::registerStartupTask(); } -void QWasmFontDatabase::notifyFontsChanged() +// Ends the font loading startup task. +void QWasmFontDatabase::endFontDatabaseStartupTask() { - QFontCache::instance()->clear(); - emit qGuiApp->fontDatabaseChanged(); + if (!g_fontStartupTaskCompleted) { + g_fontStartupTaskCompleted = true; + QEventDispatcherWasm::completeStarupTask(); + } } +// Registers that a font file will be loaded. +void QWasmFontDatabase::refFontFileLoading() +{ + g_pendingFonts += 1; +} + +// Registers that one font file has been loaded, and sends notifactions +// when all pending font files have been loaded. +void QWasmFontDatabase::derefFontFileLoading() +{ + if (--g_pendingFonts <= 0) { + QFontCache::instance()->clear(); + emit qGuiApp->fontDatabaseChanged(); + endFontDatabaseStartupTask(); + } +} + +// Unconditionally ends local font loading, for instance if there +// are no fonts to load or if there was an unexpected error. +void QWasmFontDatabase::endAllFontFileLoading() +{ + bool hadPandingfonts = g_pendingFonts > 0; + if (hadPandingfonts) { + // The hadPandingfonts counter might no longer be correct; disable counting + // and send notifications unconditionally. + g_pendingFonts = 0; + QFontCache::instance()->clear(); + emit qGuiApp->fontDatabaseChanged(); + } + + endFontDatabaseStartupTask(); +} + + QT_END_NAMESPACE |