Revision control

Copy as Markdown

// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/private/qcore_ohos_p.h>
#include <QtCore/qfileinfo.h>
#include "qohosplatformfontdatabase_p.h"
#include "qohosqpaenums.h"
#include <QtCore/private/qnapi_p.h>
#include <fontconfig/fontconfig.h>
using namespace std::string_literals;
QT_BEGIN_NAMESPACE
// Defined in gui/text/qfontdatabase.cpp
Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
namespace {
using SystemFontType = QtOhosQpa::enums::ohos::graphics::text::SystemFontType;
bool ohosNoUiChildMode = false;
QStringList getAllFontPaths()
{
if (ohosNoUiChildMode)
return {};
auto fontsPaths = QOhosJsThreadGateway::evalWithPromise<std::vector<std::string>>(
[](QOhosJsState &jsState, QOhosTaskPromise<std::vector<std::string>> evalPromise) {
auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
jsState.evalToPromiseOrRejectOnThrow(
"@ohos.graphics.text.getSystemFontFullNamesByType(*)",
{jsState.mapOhosEnumToJs(SystemFontType::ALL)})
.onThen(
[thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &cbInfo) mutable {
auto fontsNamesArray = cbInfo.getFirstArg<QNapi::Array>(Q_FUNC_INFO);
if (fontsNamesArray.Length() == 0) {
thenPromise({});
return;
}
auto fontsNames = QNapi::getArrayElements<std::vector<std::string>, QNapi::String>(fontsNamesArray);
auto sharedThenPromise = QtOhos::moveToSharedPtr(std::move(thenPromise).makeChained(Q_FUNC_INFO));
auto pathsCollector = std::make_shared<QOhosConsumer<std::string>>(
[sharedThenPromise, pushSize = fontsNames.size(), result = std::vector<std::string>()](auto element) mutable {
result.push_back(std::move(element));
if (result.size() == pushSize)
(*sharedThenPromise)(std::move(result));
});
for (const auto &fontName : fontsNames) {
cbInfo.jsState().evalToPromiseOrRejectOnThrow(
"@ohos.graphics.text.getFontDescriptorByFullName(*)",
{fontName, cbInfo.jsState().mapOhosEnumToJs(SystemFontType::ALL)})
.onThen(
[pathsCollector](const QOhosCallbackInfo &cbInfo) {
auto fontDescriptor = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
auto optFontPath = QNapi::getOptionalPropOrEmpty<QNapi::String>(fontDescriptor, "path");
(*pathsCollector)(!optFontPath.IsEmpty() ? optFontPath.Utf8Value() : ""s);
})
.onCatch(
[pathsCollector, fontName](const QOhosCallbackInfo &cbInfo) {
QtOhos::logJsCallbackError(
cbInfo, ("getFontDescriptorByFullName("s + fontName + ") failed"s).c_str());
(*pathsCollector)(""s);
});
}
})
.onCatch(
[catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
QtOhos::logJsCallbackError(cbInfo, "getSystemFontFullNamesByType() failed");
catchPromise({});
});
},
Q_FUNC_INFO);
QStringList result;
std::transform(
fontsPaths.begin(), fontsPaths.end(),
std::back_inserter(result), QString::fromStdString);
result.removeAll(QString());
result.sort();
return result;
}
std::string getDefaultFontFamily(QOhosJsState &jsState)
{
if (ohosNoUiChildMode)
return "";
std::string defaultFontFamily;
auto genericFonts = jsState.eval<QNapi::Array>("@ohos.font.getUIFontConfig().generic");
if (genericFonts.Length() > 0) {
auto firstFont = QNapi::checkedCast<QNapi::Object>(genericFonts.Get(0U));
defaultFontFamily = firstFont.get<QNapi::String>("family");
} else {
qFatal("Failed to get system default font family name."
" The reason is: empty `@ohos.font.getUIFontConfig().generic` array.");
}
return defaultFontFamily;
}
void registerSystemFonts()
{
QStringList fontPaths = getAllFontPaths();
fontPaths.removeDuplicates();
QSet<QString> uniqueFontDirs;
for (const QString &fontPath : fontPaths)
uniqueFontDirs.insert(QFileInfo(fontPath).absolutePath());
FcConfig *config = FcConfigGetCurrent();
if (config != nullptr) {
for (const QString &dir : uniqueFontDirs)
FcConfigAppFontAddDir(config, reinterpret_cast<const FcChar8 *>(dir.toUtf8().constData()));
FcConfigBuildFonts(config);
} else {
qOhosPrintfError("Failed to get fontconfig current configuration.");
std::abort();
}
}
}
void QOhosPlatformFontDatabase::setOhosNoUiChildMode()
{
ohosNoUiChildMode = true;
}
void QOhosPlatformFontDatabase::populateFontDatabase()
{
FcInit();
registerSystemFonts();
QFontconfigDatabase::populateFontDatabase();
}
QStringList QOhosPlatformFontDatabase::fallbacksForFamily(const QString &family,
QFont::Style style,
QFont::StyleHint styleHint,
QFontDatabasePrivate::ExtendedScript script) const
{
QStringList result;
const QFontDatabase::WritingSystem ws = qt_writing_system_for_script(script);
const bool defaultFontSupportsScript =
ws == QFontDatabase::Any ||
QFontDatabase::writingSystems(defaultFont().family()).contains(ws);
if (defaultFontSupportsScript)
result.append(defaultFont().family());
if (styleHint == QFont::Monospace || styleHint == QFont::Courier)
result.append(QString::fromUtf8(qgetenv("Droid Sans Mono;Droid Sans;Noto Sans")).split(QLatin1Char(';')));
else if (styleHint == QFont::Serif)
result.append(QString::fromUtf8(qgetenv("Noto Serif")).split(QLatin1Char(';')));
else
result.append(QString::fromUtf8(qgetenv("Roboto;Droid Sans")).split(QLatin1Char(';')));
result.append(QFontconfigDatabase::fallbacksForFamily(family, style, styleHint, script));
return result;
}
QFont QOhosPlatformFontDatabase::defaultFont() const
{
static const char * const preferredDefaultFontFamily = "HarmonyOS Sans SC";
static const QString defaultFontFamily =
QFontDatabase::families().contains(QLatin1String(preferredDefaultFontFamily))
? QString::fromStdString(preferredDefaultFontFamily)
: QOhosJsThreadGateway::eval(
[&](auto &jsState) {
return QString::fromStdString(getDefaultFontFamily(jsState));
},
Q_FUNC_INFO);
return QFont(defaultFontFamily);
}
QT_END_NAMESPACE