diff options
-rw-r--r-- | src/plugins/platforms/wasm/qwasmfontdatabase.cpp | 278 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmfontdatabase.h | 24 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.cpp | 11 | ||||
-rw-r--r-- | src/plugins/platforms/wasm/qwasmintegration.h | 3 |
4 files changed, 257 insertions, 59 deletions
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp index 83eee06ac0..1e166fa6ff 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> @@ -58,11 +59,6 @@ private: val m_fontData; }; -bool isLocalFontsAPISupported() -{ - return val::global("window")["queryLocalFonts"].isUndefined() == false; -} - val makeObject(const char *key, const char *value) { val obj = val::object(); @@ -70,37 +66,25 @@ val makeObject(const char *key, const char *value) return obj; } -std::multimap<QString, FontData> makeFontFamilyMap(const QList<FontData> &fonts) -{ - std::multimap<QString, FontData> fontFamilies; - for (auto font : fonts) - fontFamilies.insert(std::make_pair(font.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")); } @@ -143,46 +127,144 @@ void readFont(FontData 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<FontData> &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 FontData &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"), @@ -196,12 +278,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, @@ -232,10 +346,60 @@ QFont QWasmFontDatabase::defaultFont() const return QFont("Bitstream Vera Sans"_L1); } -void QWasmFontDatabase::notifyFontsChanged() +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(); +} + +// Ends the font loading startup task. +void QWasmFontDatabase::endFontDatabaseStartupTask() +{ + 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() { - QFontCache::instance()->clear(); - emit qGuiApp->fontDatabaseChanged(); + 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 diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h index 8a2936cb1d..a1c8f1ff48 100644 --- a/src/plugins/platforms/wasm/qwasmfontdatabase.h +++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h @@ -6,11 +6,16 @@ #include <QtGui/private/qfreetypefontdatabase_p.h> +#include <emscripten/val.h> + QT_BEGIN_NAMESPACE class QWasmFontDatabase : public QFreeTypeFontDatabase { public: + QWasmFontDatabase(); + static QWasmFontDatabase *get(); + void populateFontDatabase() override; QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override; QStringList fallbacksForFamily(const QString &family, QFont::Style style, @@ -20,8 +25,25 @@ public: QFont defaultFont() const override; void populateLocalfonts(); + void populateLocalFontFamilies(emscripten::val families); + void populateLocalFontFamilies(const QStringList &famliies, bool allFamilies); + + static void beginFontDatabaseStartupTask(); + static void endFontDatabaseStartupTask(); + static void refFontFileLoading(); + static void derefFontFileLoading(); + static void endAllFontFileLoading(); - static void notifyFontsChanged(); +private: + bool m_localFontsApiSupported = false; + bool m_queryLocalFontsPermission = false; + enum FontFamilyLoadSet { + NoFontFamilies, + DefaultFontFamilies, + AllFontFamilies, + }; + FontFamilyLoadSet m_localFontFamilyLoadSet; + QStringList m_extraLocalFontFamilies; }; QT_END_NAMESPACE #endif diff --git a/src/plugins/platforms/wasm/qwasmintegration.cpp b/src/plugins/platforms/wasm/qwasmintegration.cpp index 778c769ae1..2fbb33eb2d 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.cpp +++ b/src/plugins/platforms/wasm/qwasmintegration.cpp @@ -66,6 +66,11 @@ static void resizeAllScreens(emscripten::val event) QWasmIntegration::get()->resizeAllScreens(); } +static void loadLocalFontFamilies(emscripten::val event) +{ + QWasmIntegration::get()->loadLocalFontFamilies(event); +} + EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) { function("qtSetContainerElements", &setContainerElements); @@ -74,6 +79,7 @@ EMSCRIPTEN_BINDINGS(qtQWasmIntegraton) function("qtResizeContainerElement", &resizeContainerElement); function("qtUpdateDpi", &qtUpdateDpi); function("qtResizeAllScreens", &resizeAllScreens); + function("qtLoadLocalFontFamilies", &loadLocalFontFamilies); } QWasmIntegration *QWasmIntegration::s_instance; @@ -395,6 +401,11 @@ void QWasmIntegration::resizeAllScreens() elementAndScreen.wasmScreen->updateQScreenAndCanvasRenderSize(); } +void QWasmIntegration::loadLocalFontFamilies(emscripten::val families) +{ + m_fontDb->populateLocalFontFamilies(families); +} + quint64 QWasmIntegration::getTimestamp() { return emscripten_performance_now(); diff --git a/src/plugins/platforms/wasm/qwasmintegration.h b/src/plugins/platforms/wasm/qwasmintegration.h index 8dfa065d9c..b966487760 100644 --- a/src/plugins/platforms/wasm/qwasmintegration.h +++ b/src/plugins/platforms/wasm/qwasmintegration.h @@ -74,8 +74,9 @@ public: void addContainerElement(emscripten::val elementArray); void removeContainerElement(emscripten::val elementArray); void resizeScreen(const emscripten::val &canvas); - void resizeAllScreens(); void updateDpi(); + void resizeAllScreens(); + void loadLocalFontFamilies(emscripten::val families); void removeBackingStore(QWindow* window); static quint64 getTimestamp(); |