From 066d81568d189c245c9867e052ccc2f6787c7ebe Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 20 Dec 2016 10:31:38 +0000 Subject: [PATCH] Remote-canvas loads fonts from the host. --- utils/fgqcanvas/CMakeLists.txt | 2 + utils/fgqcanvas/fgcanvastext.cpp | 24 ++++- utils/fgqcanvas/fgcanvastext.h | 2 + utils/fgqcanvas/fgqcanvasfontcache.cpp | 122 +++++++++++++++++++++++++ utils/fgqcanvas/fgqcanvasfontcache.h | 39 ++++++++ utils/fgqcanvas/main.cpp | 8 ++ 6 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 utils/fgqcanvas/fgqcanvasfontcache.cpp create mode 100644 utils/fgqcanvas/fgqcanvasfontcache.h diff --git a/utils/fgqcanvas/CMakeLists.txt b/utils/fgqcanvas/CMakeLists.txt index 3538ab1b5..e2438eb3e 100644 --- a/utils/fgqcanvas/CMakeLists.txt +++ b/utils/fgqcanvas/CMakeLists.txt @@ -30,6 +30,8 @@ set(SOURCES fgcanvaswidget.h canvastreemodel.cpp canvastreemodel.h + fgqcanvasfontcache.cpp + fgqcanvasfontcache.h ) qt5_wrap_ui(uic_sources temporarywidget.ui) diff --git a/utils/fgqcanvas/fgcanvastext.cpp b/utils/fgqcanvas/fgcanvastext.cpp index 74566ec85..e7ed7b85e 100644 --- a/utils/fgqcanvas/fgcanvastext.cpp +++ b/utils/fgqcanvas/fgcanvastext.cpp @@ -5,11 +5,16 @@ #include "fgcanvaspaintcontext.h" #include "localprop.h" +#include "fgqcanvasfontcache.h" FGCanvasText::FGCanvasText(FGCanvasGroup* pr, LocalProp* prop) : FGCanvasElement(pr, prop), _metrics(QFont()) { + // this signal fires infrequently enough, it's simpler just to have + // all texts watch it. + connect(FGQCanvasFontCache::instance(), &FGQCanvasFontCache::fontLoaded, + this, &FGCanvasText::onFontLoaded); } void FGCanvasText::doPaint(FGCanvasPaintContext *context) const @@ -131,10 +136,25 @@ void FGCanvasText::markFontDirty() _fontDirty = true; } +void FGCanvasText::onFontLoaded(QByteArray name) +{ + QByteArray fontName = getCascadedStyle("font", QString()).toByteArray(); + if (name != fontName) { + return; // not our font + } + + markFontDirty(); +} + void FGCanvasText::rebuildFont() const { - QString fontName = getCascadedStyle("font", QString()).toString(); - QFont f(fontName); + QByteArray fontName = getCascadedStyle("font", QString()).toByteArray(); + bool ok; + QFont f = FGQCanvasFontCache::instance()->fontForName(fontName, &ok); + if (!ok) { + // wait for the correct font + } + f.setPixelSize(getCascadedStyle("character-size", 16).toInt()); _font = f; _metrics = QFontMetricsF(_font); diff --git a/utils/fgqcanvas/fgcanvastext.h b/utils/fgqcanvas/fgcanvastext.h index 5f33ff4e6..4df31a638 100644 --- a/utils/fgqcanvas/fgcanvastext.h +++ b/utils/fgqcanvas/fgcanvastext.h @@ -24,6 +24,8 @@ private: void setDrawMode(QVariant var); void markFontDirty(); + + void onFontLoaded(QByteArray name); private: void rebuildFont() const; void rebuildAlignment(QVariant var) const; diff --git a/utils/fgqcanvas/fgqcanvasfontcache.cpp b/utils/fgqcanvas/fgqcanvasfontcache.cpp new file mode 100644 index 000000000..c120ec019 --- /dev/null +++ b/utils/fgqcanvas/fgqcanvasfontcache.cpp @@ -0,0 +1,122 @@ +#include "fgqcanvasfontcache.h" + +#include +#include +#include +#include +#include +#include +#include + +FGQCanvasFontCache::FGQCanvasFontCache(QNetworkAccessManager* nam, QObject *parent) + : QObject(parent) + , m_downloader(nam) +{ +} + +QFont FGQCanvasFontCache::fontForName(QByteArray name, bool* ok) +{ + if (m_cache.contains(name)) { + if (ok) { + *ok = true; + } + return m_cache.value(name); // easy! + } + + lookupFile(name); + if (m_cache.contains(name)) { + if (ok) { + *ok = true; + } + return m_cache.value(name); + } + + if (ok) { + *ok = false; + } + + return QFont(); // default font +} + +static FGQCanvasFontCache* s_instance = nullptr; + +void FGQCanvasFontCache::initialise(QNetworkAccessManager *nam) +{ + Q_ASSERT(s_instance == nullptr); + s_instance = new FGQCanvasFontCache(nam); +} + +FGQCanvasFontCache *FGQCanvasFontCache::instance() +{ + + return s_instance; +} + +void FGQCanvasFontCache::onFontDownloadFinished() +{ + QByteArray fontPath = sender()->property("font").toByteArray(); + qDebug() << "finished download of " << fontPath; + + QDir cacheDir(QStandardPaths::writableLocation(QStandardPaths::CacheLocation)); + QString absPath = cacheDir.absoluteFilePath(fontPath); + + QFileInfo finfo(fontPath); + cacheDir.mkpath(finfo.dir().path()); + + QFile f(absPath); + if (!f.open(QIODevice::WriteOnly)) { + qWarning() << "failed to open cache file" << f.fileName(); + } + + QNetworkReply* reply = qobject_cast(sender()); + Q_ASSERT(m_transfers.contains(reply)); + + f.write(reply->readAll()); + f.close(); + + m_transfers.removeOne(reply); + + // call ourselves again now it's cached; + lookupFile(fontPath); +} + +void FGQCanvasFontCache::onFontDownloadError(QNetworkReply::NetworkError) +{ + QNetworkReply* reply = qobject_cast(sender()); + qWarning() << "font download failed:" << reply->errorString(); +} + +void FGQCanvasFontCache::lookupFile(QByteArray name) +{ + QString path = QStandardPaths::locate(QStandardPaths::CacheLocation, name); + if (path.isEmpty()) { + QUrl url = QUrl("http://localhost:8080/Fonts/" + name); + + Q_FOREACH (QNetworkReply* transfer, m_transfers) { + if (transfer->url() == url) { + return; // transfer already active + } + } + + qDebug() << "reqeusting font" << url; + QNetworkReply* reply = m_downloader->get(QNetworkRequest(url)); + reply->setProperty("font", name); + + connect(reply, &QNetworkReply::finished, this, &FGQCanvasFontCache::onFontDownloadFinished); + // connect(reply, &QNetworkReply::error, this, &FGQCanvasFontCache::onFontDownloadError); + + m_transfers.append(reply); + } else { + qDebug() << "found font" << name << "at path" << path; + + int fontFamilyId = QFontDatabase::addApplicationFont(path); + QStringList families = QFontDatabase::applicationFontFamilies(fontFamilyId); + qDebug() << "families are:" << families; + + // compute a QFont and cache + QFont font(families.front()); + m_cache.insert(name, font); + } +} + + diff --git a/utils/fgqcanvas/fgqcanvasfontcache.h b/utils/fgqcanvas/fgqcanvasfontcache.h new file mode 100644 index 000000000..c8fd39a60 --- /dev/null +++ b/utils/fgqcanvas/fgqcanvasfontcache.h @@ -0,0 +1,39 @@ +#ifndef FGQCANVASFONTCACHE_H +#define FGQCANVASFONTCACHE_H + +#include +#include +#include +#include +#include + +class QNetworkAccessManager; + +class FGQCanvasFontCache : public QObject +{ + Q_OBJECT +public: + explicit FGQCanvasFontCache(QNetworkAccessManager* nam, QObject *parent = 0); + + QFont fontForName(QByteArray name, bool* ok = nullptr); + + static void initialise(QNetworkAccessManager* nam); + static FGQCanvasFontCache* instance(); + +signals: + void fontLoaded(QByteArray name); + +private slots: + void onFontDownloadFinished(); + void onFontDownloadError(QNetworkReply::NetworkError); + +private: + QNetworkAccessManager* m_downloader; + QHash m_cache; + + void lookupFile(QByteArray name); + + QList m_transfers; +}; + +#endif // FGQCANVASFONTCACHE_H diff --git a/utils/fgqcanvas/main.cpp b/utils/fgqcanvas/main.cpp index 00499dc37..c8e5e52bc 100644 --- a/utils/fgqcanvas/main.cpp +++ b/utils/fgqcanvas/main.cpp @@ -1,5 +1,9 @@ #include "temporarywidget.h" + #include +#include + +#include "fgqcanvasfontcache.h" int main(int argc, char *argv[]) { @@ -9,6 +13,10 @@ int main(int argc, char *argv[]) a.setOrganizationDomain("flightgear.org"); a.setOrganizationName("FlightGear"); + QNetworkAccessManager* downloader = new QNetworkAccessManager; + + FGQCanvasFontCache::initialise(downloader); + TemporaryWidget w; w.show();