Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosplatformfontdatabase.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QDir>
5
7#include <QtCore/private/qnapi_p.h>
8#include <fontconfig/fontconfig.h>
9#include <qohosplugincore.h>
10
11using namespace std::string_literals;
12
14
15// Defined in gui/text/qfontdatabase.cpp
16Q_GUI_EXPORT QFontDatabase::WritingSystem qt_writing_system_for_script(int script);
17
18namespace {
19
20enum class JsSystemFontType {
21 ALL = 1 << 0,
22 GENERIC = 1 << 1,
23 STYLISH = 1 << 2,
24 INSTALLED = 1 << 3,
25};
26
27bool ohosNoUiChildMode = false;
28
29QStringList getSystemFontPaths(QtOhos::JsState &jsState)
30{
31 if (ohosNoUiChildMode)
32 return {};
33
34 auto fontModule = jsState.eval<QNapi::Object>("@ohos.font");
35
36 auto systemFontPaths = QNapi::getArrayElements<QStringList, QNapi::String>(
37 fontModule.call<QNapi::Array>("getSystemFontList"),
38 [&](QNapi::String fontName) {
39 auto fontInfo = fontModule.call("getFontByName", {fontName});
40 return fontInfo.IsObject()
41 ? QString::fromStdString(
42 QNapi::checkedCast<QNapi::Object>(fontInfo).get<QNapi::String>("path"))
43 : QString();
44 });
45
46 systemFontPaths.removeAll(QString());
47
48 return systemFontPaths;
49}
50
51QStringList getUIFontPaths(QtOhos::JsState &jsState)
52{
53 if (ohosNoUiChildMode)
54 return {};
55
56 auto uiFontDirs = QNapi::getArrayElements<QStringList, QNapi::String>(
57 jsState.eval<QNapi::Array>("@ohos.font.getUIFontConfig().fontDir"),
58 &QString::fromStdString);
59
60 QStringList nameFilters;
61 nameFilters << QLatin1String("*.ttf")
62 << QLatin1String("*.otf")
63 << QLatin1String("*.ttc");
64
65 QStringList result;
66 for (const auto &fontDir : uiFontDirs) {
67 for (const QFileInfo &fileInfo : QDir(fontDir).entryInfoList(nameFilters, QDir::Files))
68 result.append(fileInfo.absoluteFilePath());
69 }
70
71 return result;
72}
73
74QStringList getInstalledFontPaths()
75{
76 if (ohosNoUiChildMode)
77 return {};
78
79 auto fontsPaths = QtOhos::evalInJsThreadWithPromise<std::vector<std::string>>(
80 [](QtOhos::JsState &jsState, QOhosTaskPromise<std::vector<std::string>> evalPromise) {
81 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
82 jsState.evalToPromiseOrRejectOnThrow(
83 "@ohos.graphics.text.getSystemFontFullNamesByType(*)",
84 {static_cast<int>(JsSystemFontType::INSTALLED)})
85 .onThen(
86 [thenPromise = std::move(thenCatchPromises.first)](const QtOhos::CallbackInfo &cbInfo) mutable {
87 auto fontsNamesArray = cbInfo.getFirstArg<QNapi::Array>(Q_FUNC_INFO);
88
89 if (fontsNamesArray.Length() == 0) {
90 thenPromise({});
91 return;
92 }
93
94 auto fontsNames = QNapi::getArrayElements<std::vector<std::string>, QNapi::String>(fontsNamesArray);
95 auto sharedThenPromise = QtOhos::moveToSharedPtr(std::move(thenPromise).makeChained(Q_FUNC_INFO));
96 auto pathsCollector = std::make_shared<QOhosConsumer<std::string>>(
97 [sharedThenPromise, pushSize = fontsNames.size(), result = std::vector<std::string>()](auto element) mutable {
98 result.push_back(std::move(element));
99 if (result.size() == pushSize)
100 (*sharedThenPromise)(std::move(result));
101 });
102
103 for (const auto &fontName : fontsNames) {
104 cbInfo.jsState().evalToPromiseOrRejectOnThrow(
105 "@ohos.graphics.text.getFontDescriptorByFullName(*)",
106 {fontName, static_cast<int>(JsSystemFontType::INSTALLED)})
107 .onThen(
108 [pathsCollector](const QtOhos::CallbackInfo &cbInfo) {
109 auto fontDescriptor = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
110 auto optFontPath = QNapi::getOptionalPropOrEmpty<QNapi::String>(fontDescriptor, "path");
111 (*pathsCollector)(!optFontPath.IsEmpty() ? optFontPath.Utf8Value() : ""s);
112 })
113 .onCatch(
114 [pathsCollector, fontName](const QtOhos::CallbackInfo &cbInfo) {
115 QtOhos::logJsCallbackError(
116 cbInfo, ("getFontDescriptorByFullName("s + fontName + ") failed"s).c_str());
117 (*pathsCollector)(""s);
118 });
119 }
120 })
121 .onCatch(
122 [catchPromise = std::move(thenCatchPromises.second)](const QtOhos::CallbackInfo &cbInfo) {
123 QtOhos::logJsCallbackError(cbInfo, "getSystemFontFullNamesByType() failed");
124 catchPromise({});
125 });
126 },
127 Q_FUNC_INFO);
128
129 QStringList result;
130 std::transform(
131 fontsPaths.begin(), fontsPaths.end(),
132 std::back_inserter(result), QString::fromStdString);
133 result.removeAll(QString());
134 result.sort();
135
136 return result;
137}
138
139std::string getDefaultFontFamily(QtOhos::JsState &jsState)
140{
141 if (ohosNoUiChildMode)
142 return "";
143
144 std::string defaultFontFamily;
145 auto genericFonts = jsState.eval<QNapi::Array>("@ohos.font.getUIFontConfig().generic");
146 if (genericFonts.Length() > 0) {
147 auto firstFont = QNapi::checkedCast<QNapi::Object>(genericFonts.Get(0U));
148 defaultFontFamily = firstFont.get<QNapi::String>("family");
149 } else {
150 qFatal("Failed to get system default font family name."
151 " The reason is: empty `@ohos.font.getUIFontConfig().generic` array.");
152 }
153
154 return defaultFontFamily;
155}
156
157void registerSystemFonts()
158{
159 QStringList fontPaths;
161 [&](auto &jsState) {
162 fontPaths.append(getSystemFontPaths(jsState));
163 fontPaths.append(getUIFontPaths(jsState));
164 },
165 Q_FUNC_INFO);
166 fontPaths.append(getInstalledFontPaths());
167 fontPaths.removeDuplicates();
168
169 QSet<QString> uniqueFontDirs;
170 for (const QString &fontPath : fontPaths)
171 uniqueFontDirs.insert(QFileInfo(fontPath).absolutePath());
172
173 FcConfig *config = FcConfigGetCurrent();
174 if (config != nullptr) {
175 for (const QString &dir : uniqueFontDirs)
176 FcConfigAppFontAddDir(config, reinterpret_cast<const FcChar8 *>(dir.toUtf8().constData()));
177 FcConfigBuildFonts(config);
178 } else {
179 qOhosPrintfError("Failed to get fontconfig current configuration.");
180 std::abort();
181 }
182}
183
184}
185
187{
188 ohosNoUiChildMode = true;
189}
190
192{
193 FcInit();
194
195 registerSystemFonts();
196
197 QFontconfigDatabase::populateFontDatabase();
198}
199
201 QFont::Style style,
202 QFont::StyleHint styleHint,
203 QFontDatabasePrivate::ExtendedScript script) const
204{
205 QStringList result;
206
207 const QFontDatabase::WritingSystem ws = qt_writing_system_for_script(script);
208 const bool defaultFontSupportsScript =
209 ws == QFontDatabase::Any ||
210 QFontDatabase::writingSystems(defaultFont().family()).contains(ws);
211
212 if (defaultFontSupportsScript)
213 result.append(defaultFont().family());
214
215 if (styleHint == QFont::Monospace || styleHint == QFont::Courier)
216 result.append(QString::fromUtf8(qgetenv("Droid Sans Mono;Droid Sans;Noto Sans")).split(QLatin1Char(';')));
217 else if (styleHint == QFont::Serif)
218 result.append(QString::fromUtf8(qgetenv("Noto Serif")).split(QLatin1Char(';')));
219 else
220 result.append(QString::fromUtf8(qgetenv("Roboto;Droid Sans")).split(QLatin1Char(';')));
221 result.append(QFontconfigDatabase::fallbacksForFamily(family, style, styleHint, script));
222
223 return result;
224}
225
227{
228 static const char * const preferredDefaultFontFamily = "HarmonyOS Sans SC";
229 static const QString defaultFontFamily =
230 QFontDatabase::families().contains(QLatin1String(preferredDefaultFontFamily))
231 ? QString::fromStdString(preferredDefaultFontFamily)
232 : QtOhos::evalInJsThread(
233 [&](auto &jsState) {
234 return QString::fromStdString(getDefaultFontFamily(jsState));
235 },
236 Q_FUNC_INFO);
237 return QFont(defaultFontFamily);
238}
239
240QT_END_NAMESPACE
QStringList fallbacksForFamily(const QString &family, QFont::Style style, QFont::StyleHint styleHint, QFontDatabasePrivate::ExtendedScript script) const override
Returns a list of alternative fonts for the specified family and style and script using the styleHint...
QFont defaultFont() const override
Returns the default system font.
void populateFontDatabase() override
This function is called once at startup by Qt's internal font database.
void runInJsThreadAndWait(const std::function< void(JsState &)> &task, std::string callerContextName={})
QDebug Q_GUI_EXPORT & operator<<(QDebug &s, const QVectorPath &path)