summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Sørvig <[email protected]>2023-03-24 15:39:08 +0100
committerQt Cherry-pick Bot <[email protected]>2023-04-25 23:39:07 +0000
commitb98ee1d340f53dc6a2bd3f88e6f13d19e86a26db (patch)
tree3c003931a52b48c0b3e7a10dcbb7a4efcf418790
parent81caae1ddc212747dbe4e891a568cefd86f6355d (diff)
wasm: rework local font support
Populate a subset of the font families at startup if the local fonts access API is supported, and the access permission has been given. Since this code runs at app startup there is no opportunity to request font access. That should be done in response to user action, for example by having a "load local fonts" button in the application. Change-Id: Ib6826deeec06ee3def0e793dd1462977710462be Reviewed-by: Mikołaj Boc <[email protected]> Reviewed-by: Aleksandr Reviakin <[email protected]> (cherry picked from commit 15dab565d071455ef08d6bf4ad4980f726df1cfa) Reviewed-by: Qt Cherry-pick Bot <[email protected]>
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.cpp218
-rw-r--r--src/plugins/platforms/wasm/qwasmfontdatabase.h3
2 files changed, 127 insertions, 94 deletions
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
index 5fdaae8f84a..c0833a65ca2 100644
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.cpp
@@ -12,126 +12,158 @@
#include <emscripten/val.h>
#include <emscripten/bind.h>
+#include <map>
+#include <array>
+
QT_BEGIN_NAMESPACE
using namespace emscripten;
using namespace Qt::StringLiterals;
-void QWasmFontDatabase::populateFontDatabase()
+
+namespace {
+
+bool isLocalFontsAPISupported()
{
- // Load font file from resources. Currently
- // all fonts needs to be bundled with the nexe
- // as Qt resources.
+ return val::global("window")["queryLocalFonts"].isUndefined() == false;
+}
- const QString fontFileNames[] = {
- QStringLiteral(":/fonts/DejaVuSansMono.ttf"),
- QStringLiteral(":/fonts/Vera.ttf"),
- QStringLiteral(":/fonts/DejaVuSans.ttf"),
- };
- for (const QString &fontFileName : fontFileNames) {
- QFile theFont(fontFileName);
- if (!theFont.open(QIODevice::ReadOnly))
- break;
+val makeObject(const char *key, const char *value)
+{
+ val obj = val::object();
+ obj.set(key, std::string(value));
+ return obj;
+}
- QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
+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;
+}
- // check if local-fonts API is available in the browser
- val window = val::global("window");
- val fonts = window["queryLocalFonts"];
+void printError(val err) {
+ qCWarning(lcQpaFonts)
+ << QString::fromStdString(err["name"].as<std::string>())
+ << QString::fromStdString(err["message"].as<std::string>());
+}
- if (fonts.isUndefined())
- return;
+std::array<const char *, 8> webSafeFontFamilies()
+{
+ return {"Arial", "Verdana", "Tahoma", "Trebuchet", "Times New Roman",
+ "Georgia", "Garamond", "Courier New"};
+}
- val permissions = val::global("navigator")["permissions"];
- if (permissions["request"].isUndefined())
+void checkFontAccessPermitted(std::function<void()> callback)
+{
+ const val permissions = val::global("navigator")["permissions"];
+ if (permissions.isUndefined())
return;
- val requestLocalFontsPermission = val::object();
- requestLocalFontsPermission.set("name", std::string("local-fonts"));
-
- qstdweb::PromiseCallbacks permissionRequestCallbacks {
- .thenFunc = [window](val status) {
- qCDebug(lcQpaFonts) << "onFontPermissionSuccess:"
- << QString::fromStdString(status["state"].as<std::string>());
-
- // query all available local fonts and call registerFontFamily for each of them
- qstdweb::Promise::make(window, "queryLocalFonts", {
- .thenFunc = [](val status) {
- const int count = status["length"].as<int>();
- for (int i = 0; i < count; ++i) {
- val font = status.call<val>("at", i);
- const std::string family = font["family"].as<std::string>();
- QFreeTypeFontDatabase::registerFontFamily(QString::fromStdString(family));
- }
- QWasmFontDatabase::notifyFontsChanged();
- },
- .catchFunc = [](val) {
- qCWarning(lcQpaFonts)
- << "Error while trying to query local-fonts API";
- }
- });
- },
- .catchFunc = [](val error) {
- qCWarning(lcQpaFonts)
- << "Error while requesting local-fonts API permission: "
- << QString::fromStdString(error["name"].as<std::string>());
+ qstdweb::Promise::make(permissions, "query", {
+ .thenFunc = [callback](val status) {
+ if (status["state"].as<std::string>() == "granted")
+ callback();
}
- };
-
- // request local fonts permission (currently supported only by Chrome 103+)
- qstdweb::Promise::make(permissions, "request", std::move(permissionRequestCallbacks), std::move(requestLocalFontsPermission));
+ }, makeObject("name", "local-fonts"));
}
-void QWasmFontDatabase::populateFamily(const QString &familyName)
+void queryLocalFonts(std::function<void(const QList<val> &)> callback)
{
- val window = val::global("window");
-
- auto queryFontsArgument = val::array(std::vector<val>({ val(familyName.toStdString()) }));
- val queryFont = val::object();
- queryFont.set("postscriptNames", std::move(queryFontsArgument));
-
- qstdweb::PromiseCallbacks localFontsQueryCallback {
- .thenFunc = [](val status) {
- val font = status.call<val>("at", 0);
-
- if (font.isUndefined())
- return;
-
- qstdweb::PromiseCallbacks blobQueryCallback {
- .thenFunc = [](val status) {
- qCDebug(lcQpaFonts) << "onBlobQuerySuccess";
+ emscripten::val window = emscripten::val::global("window");
+ qstdweb::Promise::make(window, "queryLocalFonts", {
+ .thenFunc = [callback](emscripten::val fontArray) {
+ QList<val> 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));
+ callback(fonts);
+ },
+ .catchFunc = printError
+ });
+}
- qstdweb::PromiseCallbacks arrayBufferCallback {
- .thenFunc = [](val status) {
- qCDebug(lcQpaFonts) << "onArrayBuffer" ;
+void readBlob(val blob, std::function<void(const QByteArray &)> callback)
+{
+ qstdweb::Promise::make(blob, "arrayBuffer", {
+ .thenFunc = [callback](emscripten::val fontArrayBuffer) {
+ QByteArray fontData = qstdweb::Uint8Array(qstdweb::ArrayBuffer(fontArrayBuffer)).copyToQByteArray();
+ callback(fontData);
+ },
+ .catchFunc = printError
+ });
+}
- QByteArray fontByteArray = QByteArray::fromEcmaUint8Array(status);
+void readFont(val font, std::function<void(const QByteArray &)> callback)
+{
+ qstdweb::Promise::make(font, "blob", {
+ .thenFunc = [callback](val blob) {
+ readBlob(blob, [callback](const QByteArray &data) {
+ callback(data);
+ });
+ },
+ .catchFunc = printError
+ });
+}
- QFreeTypeFontDatabase::addTTFile(fontByteArray, QByteArray());
+} // namespace
- QWasmFontDatabase::notifyFontsChanged();
- },
- .catchFunc = [](val) {
- qCWarning(lcQpaFonts) << "onArrayBufferError";
- }
- };
+void QWasmFontDatabase::populateLocalfonts()
+{
+ if (!isLocalFontsAPISupported())
+ return;
- qstdweb::Promise::make(status, "arrayBuffer", std::move(arrayBufferCallback));
- },
- .catchFunc = [](val) {
- qCWarning(lcQpaFonts) << "onBlobQueryError";
+ // 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();
+ });
}
- };
+ }
+ });
+ });
+}
- qstdweb::Promise::make(font, "blob", std::move(blobQueryCallback));
- },
- .catchFunc = [](val) {
- qCWarning(lcQpaFonts) << "onLocalFontsQueryError";
- }
+void QWasmFontDatabase::populateFontDatabase()
+{
+ // Load font file from resources. Currently
+ // all fonts needs to be bundled with the nexe
+ // as Qt resources.
+
+ const QString fontFileNames[] = {
+ QStringLiteral(":/fonts/DejaVuSansMono.ttf"),
+ QStringLiteral(":/fonts/Vera.ttf"),
+ QStringLiteral(":/fonts/DejaVuSans.ttf"),
};
+ for (const QString &fontFileName : fontFileNames) {
+ QFile theFont(fontFileName);
+ if (!theFont.open(QIODevice::ReadOnly))
+ break;
+
+ QFreeTypeFontDatabase::addTTFile(theFont.readAll(), fontFileName.toLatin1());
+ }
- qstdweb::Promise::make(window, "queryLocalFonts", std::move(localFontsQueryCallback), std::move(queryFont));
+ populateLocalfonts();
}
QFontEngine *QWasmFontDatabase::fontEngine(const QFontDef &fontDef, void *handle)
diff --git a/src/plugins/platforms/wasm/qwasmfontdatabase.h b/src/plugins/platforms/wasm/qwasmfontdatabase.h
index 22c550f2445..8a2936cb1d7 100644
--- a/src/plugins/platforms/wasm/qwasmfontdatabase.h
+++ b/src/plugins/platforms/wasm/qwasmfontdatabase.h
@@ -12,7 +12,6 @@ class QWasmFontDatabase : public QFreeTypeFontDatabase
{
public:
void populateFontDatabase() override;
- void populateFamily(const QString &familyName) override;
QFontEngine *fontEngine(const QFontDef &fontDef, void *handle) override;
QStringList fallbacksForFamily(const QString &family, QFont::Style style,
QFont::StyleHint styleHint,
@@ -20,6 +19,8 @@ public:
void releaseHandle(void *handle) override;
QFont defaultFont() const override;
+ void populateLocalfonts();
+
static void notifyFontsChanged();
};
QT_END_NAMESPACE