123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404 | // Copyright (C) 2018 The Qt Company Ltd.// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only#include"qwasmfontdatabase.h"#include"qwasmintegration.h"#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>#include <emscripten/val.h>#include <emscripten/bind.h>#include <map>#include <array> QT_BEGIN_NAMESPACE using namespace emscripten;using namespaceQt::StringLiterals;namespace{class FontData {public:FontData(val fontData):m_fontData(fontData) {} QString family()const{returnQString::fromStdString(m_fontData["family"].as<std::string>());} QString fullName()const{returnQString::fromStdString(m_fontData["fullName"].as<std::string>());} QString postscriptName()const{returnQString::fromStdString(m_fontData["postscriptName"].as<std::string>());} QString style()const{returnQString::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){ val obj =val::object(); obj.set(key,std::string(value));return obj;}voidprintError(val err) {qCWarning(lcQpaFonts)<<QString::fromStdString(err["name"].as<std::string>())<<QString::fromStdString(err["message"].as<std::string>());QWasmFontDatabase::endAllFontFileLoading();}voidcheckFontAccessPermitted(std::function<void(bool)> callback){const val permissions =val::global("navigator")["permissions"];if(permissions.isUndefined()) {callback(false);return;}qstdweb::Promise::make(permissions,"query", {.thenFunc = [callback](val status) {callback(status["state"].as<std::string>() =="granted");},},makeObject("name","local-fonts"));}voidqueryLocalFonts(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<FontData> fonts;const int count = fontArray["length"].as<int>(); fonts.reserve(count);for(int i =0; i < count; ++i) fonts.append(FontData(fontArray.call<emscripten::val>("at", i)));callback(fonts);},.catchFunc = printError });}voidreadBlob(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 });}voidreadFont(FontData font,std::function<void(const QByteArray &)> callback){qstdweb::Promise::make(font.value(),"blob", {.thenFunc = [callback](val blob) {readBlob(blob, [callback](const QByteArray &data) {callback(data);});},.catchFunc = printError });}emscripten::val getLocalFontsConfigProperty(const char*name) {emscripten::val qt =val::module_property("qt");if(qt.isUndefined())returnemscripten::val();emscripten::val localFonts = qt["localFonts"];if(localFonts.isUndefined())returnemscripten::val();return localFonts[name];};boolgetLocalFontsBoolConfigPropertyWithDefault(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;returnQString::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;};}// namespaceQWasmFontDatabase::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.voidQWasmFontDatabase::populateLocalfonts(){// Decide which font families to populate based on user preferences QStringList selectedLocalFontFamilies;bool allFamilies =false;switch(m_localFontFamilyLoadSet) {case NoFontFamilies:default:// keep empty selectedLocalFontFamiliesbreak;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;}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;}}voidQWasmFontDatabase::populateLocalFontFamilies(emscripten::val families){if(!m_localFontsApiSupported)return;populateLocalFontFamilies(toStringList(families),false);}voidQWasmFontDatabase::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();});}voidQWasmFontDatabase::populateFontDatabase(){// Load bundled font file from resources.const QString fontFileNames[] = {QStringLiteral(":/fonts/DejaVuSansMono.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());}// 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();elseendAllFontFileLoading();});}} QFontEngine *QWasmFontDatabase::fontEngine(const QFontDef &fontDef,void*handle){ QFontEngine *fontEngine =QFreeTypeFontDatabase::fontEngine(fontDef, handle);return fontEngine;} QStringList QWasmFontDatabase::fallbacksForFamily(const QString &family,QFont::Style style,QFont::StyleHint styleHint,QFontDatabasePrivate::ExtendedScript script)const{ QStringList fallbacks =QFreeTypeFontDatabase::fallbacksForFamily(family, style, styleHint, script);// Add the DejaVuSans.ttf font (loaded in populateFontDatabase above) as a falback font// to all other fonts (except itself).static const QString wasmFallbackFonts[] = {"DejaVu Sans"};for(auto wasmFallbackFont : wasmFallbackFonts) {if(family != wasmFallbackFont && !fallbacks.contains(wasmFallbackFont)) fallbacks.append(wasmFallbackFont);}return fallbacks;}voidQWasmFontDatabase::releaseHandle(void*handle){QFreeTypeFontDatabase::releaseHandle(handle);} QFont QWasmFontDatabase::defaultFont()const{returnQFont("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.voidQWasmFontDatabase::beginFontDatabaseStartupTask(){ g_fontStartupTaskCompleted =false;QEventDispatcherWasm::registerStartupTask();}// Ends the font loading startup task.voidQWasmFontDatabase::endFontDatabaseStartupTask(){if(!g_fontStartupTaskCompleted) { g_fontStartupTaskCompleted =true;QEventDispatcherWasm::completeStarupTask();}}// Registers that a font file will be loaded.voidQWasmFontDatabase::refFontFileLoading(){ g_pendingFonts +=1;}// Registers that one font file has been loaded, and sends notifactions// when all pending font files have been loaded.voidQWasmFontDatabase::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.voidQWasmFontDatabase::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
|