HarmonyOS鸿蒙PC开源FeatherPad文本编辑器移植:开源工具FeatherPad到鸿蒙PC实践总结
本文结合本仓库 FeatherPadNext 的实际源码、工程结构,系统总结开源轻量级多标签纯文本编辑器 FeatherPad 从 Linux 桌面到鸿蒙 PC(2in1)的完整移植过程。
移植成功后的效果:
一、FeatherPad 与 FeatherPadNext 在鸿蒙上的差异
FeatherPad 是 Pedram Pourang (tsujan) 开发的 Qt 桌面多标签纯文本编辑器(原始仓库:https://github.com/tsujan/FeatherPad),上游以 Linux 为主但 Qt 代码跨平台(Windows / macOS / BSD 均可编译)。本文参考的 v1.6.4 源码基于 Windows 编译版本。FeatherPadNext 是其在鸿蒙 PC 上的移植版本。
与 MeldNext 的根本差异:MeldNext 用 ArkTS 完全重写 UI 层;FeatherPadNext 走的是"Qt 引擎直接嵌入 ArkUI 容器"的路线——保留 95% Qt C++ 源码,用鸿蒙 XComponent + QAbility 作为运行宿主,仅通过
FP_OHOS宏裁剪桌面专有代码,用 N-API 桥接对接系统服务(字体安装、打印)。
| 维度 | 原生 FeatherPad(Windows / Linux 桌面) | FeatherPadNext(鸿蒙 PC) |
|---|---|---|
| 应用形态 | 独立 Qt 桌面应用(Win32 或 X11/Wayland 进程) | HAP 应用包(QAbility 生命周期) |
| UI 宿主 | Win32 窗口(Windows)/ X11 / Wayland(Linux) | ArkUI XComponent 嵌入 Qt 窗口(libqohos.so 平台插件) |
| 编程语言 | C++ (Qt 5) | C++ (Qt 5.12.12 for OHOS) + ArkTS(壳层) |
| 文本引擎 | QPlainTextEdit + QSyntaxHighlighter | 复用原版 Qt 引擎(无重写) |
| 拼写检查 | Hunspell(系统包) | Hunspell 1.7.2 交叉编译 + resfile 加载 |
| 打印 | Qt PrintSupport(Windows 走 WinSpool / Linux 走 CUPS) | Qt PrintSupport + ohosprintsupport + Print Kit |
| 剪贴板 | QClipboard(系统剪贴板) | 应用内静态剪贴板(READ_PASTEBOARD 是 system_basic 权限) |
| 单实例 | D-Bus 跨进程协调(Linux 主路径,Windows 退化为本地) | 始终主实例(无 D-Bus) |
| SVG 图标 | QSvgRenderer 头文件 + 主题色替换 | libqsvg.so 插件 + QImage 逐像素着色 |
| 保存提权 | pkexec / Polkit(Linux 路径)/ UAC(Windows 路径) | 自动引导"另存为"(沙箱限制) |
| Help 文档 | DATADIR/featherpad/help(Linux)/ 应用目录(Windows) |
:/help Qt 资源内嵌只读标签页 |
| 字体加载 | 系统字体安装 / Fontconfig | N-API 桥接 → 下载微软雅黑 → 唤起系统字体安装器 |
| 日志 | qDebug() | OHOS hilog,LOG_TAG=FeatherPad |
| 构建系统 | qmake / CMake + 系统 Qt | Hvigor + CMake + 内置 Qt 5.12.12 OHOS SDK |
| 包名 / 主 Ability | featherpad / 无 |
com.develop.opensource.featherpadnext / QAbility |
| 设备 / SDK | Windows / Linux 桌面 / 系统 Qt | 2in1(鸿蒙 PC / 平板)/ HarmonyOS 6.0.1(21),兼容 5.0.5(17) |
二、技术架构与技术原理
2.1 总体架构

2.2 核心技术原理
QAbility + XComponent 容器化:用 ArkUI 的 XComponent 控件作为 Qt 窗口的承载容器,XComponent 暴露 surfaceId,Qt 通过 libqohos.so 平台插件把 surfaceId 包装成 QWindow,Qt 的 EGL/OpenGL ES 把内容画到这个 surface 上。
FP_OHOS 宏守护桌面代码:FP_OHOS=1 在 CMake 全局定义,桌面专有代码用 #ifdef FP_OHOS 双向守护(D-Bus 单实例、pkexec/UAC 提权、SpellDialog 弹窗、SpellDialog 建议词、剪贴板双缓冲、Help 路径、SVG 渲染、X11/Win32 头文件等 6+ 处)。
N-API 双向桥接:
libfontbridge.so(必须独立成库):含 Qt 依赖,独立编译后让 ArkTS 只 import 本库,避免 ArkTS import 时 dlopen 破坏 Qt 运行时加载时序printbridge(内嵌于 libfeatherpad.so):无 Qt 依赖,可直接编进主库- N-API 线程安全:C++ 侧(特别是 Qt 事件循环线程)调用 ArkTS 函数必须用
napi_threadsafe_function派发到 JS 线程,否则直接调用会 cppcrash
Hunspell resfile 加载:Hunspell 需要真实文件路径,rawfile 不能用 QFile 直接打开,必须用 resfile(运行时路径 /data/storage/el1/bundle/entry/resources/resfile/dictionaries/),所以词典文件在工程里存在两份。
Qt 主题与系统调色板对抗:OHOS 默认调色板会把 Untitled 编辑区画成"黑底蓝字",main.cpp::applyOhosUiFixes() 强制 Fusion 样式 + 覆盖 QPalette 12 个 role + 全量 QSS,在 QApplication 构造后立即调用。
三、底层依赖开源库介绍
| # | 库 | 版本 | 在 FeatherPadNext 中的角色 | 移植方式 | 复用率 |
|---|---|---|---|---|---|
| 1 | Qt 5.12.12 for OHOS | 5.12.12 | 整个 libfeatherpad.so 的运行时 | 内置到 thirdparty/qt-5.12.12-ohos-sdk/,CMake find_package |
100% |
| 2 | libqohos.so | 与 Qt 同 | Qt 在 OHOS 的平台后端(XComponent + Ability 生命周期) | 内置于 entry/libs/arm64-v8a/ |
100% |
| 3 | libohosprintsupport.so | 与 Qt 同 | 把 QPrinter 桥接到 BasicServicesKit.print |
内置于 entry/libs/arm64-v8a/printsupport/ |
100% |
| 4 | libqsvg / libqsvgicon | 与 Qt 同 | SVG 图像/图标插件(避开 QSvgRenderer 头依赖) | 内置于 Qt SDK plugins/ | 100% |
| 5 | Hunspell | 1.7.2 | spellChecker.cpp 调用 hunspell.hxx |
交叉编译(hunspell_build.sh),预编译产物内置 |
~100% |
| 6 | en_US Hunspell 词典 | 1.7.2 | 英文拼写词库 | fp.qrc 嵌入 Qt 资源 + resfile/dictionaries/ 双份 |
100% |
| 7 | FeatherPad 21 语言高亮器 | 1.6.4 | cmake/css/fountain/html/java/json/lua/markdown/pascal/patterns/perl-regex/regex/rest/ruby/rust/sh/tcl/toml/xml/yaml + url | 源码内嵌 highlighter/,0 改动 |
100% |
| 8 | libQt5DBus | 5.12.12 | 桌面端单实例依赖;鸿蒙端代码裁剪后仅链接不调用 | Qt SDK 自带 | 100% |
四、移植流程
4.1 总体移植流程

各阶段简述:
- 第1阶段·环境准备:DevEco Studio 6.x + 内置 Qt 5.12.12 SDK(已打包到
thirdparty/)+ OHOS Native SDK - 第2阶段·Hunspell 交叉编译(可选):
./hunspell_build.sh→libhunspell-1.7.{a,so},预编译产物已内置到仓库,首次构建可跳过 - 第3阶段·CMake 从源码编译:
FEATHERPAD_BUILD_FROM_SOURCE=ON触发CMakeLists.featherpad-from-source.cmake,add_library(featherpad SHARED ...50个源文件)+add_library(fontbridge SHARED font_install_napi_bridge) - 第4阶段·DevEco 打包:拷贝所有
.so到entry/libs/arm64-v8a/+ Qt 插件 + 配置build-profile.json5(externalNativeOptions)+module.json5(deviceTypes/permissions)+ 拷贝词典到resfile/ - 第5阶段·签名 + 安装:DevEco 自动签名 → 生成 HAP →
hdc install/ Run
4.2 关键代码
4.2.1 关键 CMakeLists(from-source 模式核心片段)
# entry/src/main/cpp/CMakeLists.featherpad-from-source.cmake
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
# Qt SDK 查找:环境变量 > 内置 thirdparty/
foreach(_c "${CMAKE_CURRENT_SOURCE_DIR}/../../../../thirdparty/qt-5.12.12-ohos-sdk"
"${CMAKE_CURRENT_SOURCE_DIR}/../../../../../qt-5.12.12-ohos-sdk")
get_filename_component(_c_abs "${_c}" ABSOLUTE)
if(EXISTS "${_c_abs}/lib/cmake/Qt5/Qt5Config.cmake")
set(QT_PREFIX "${_c_abs}"); break()
endif()
endforeach()
list(APPEND CMAKE_PREFIX_PATH "${QT_PREFIX}")
list(APPEND CMAKE_FIND_ROOT_PATH "${QT_PREFIX}")
# 50 个源文件 + 5 个 .ui + fp.qrc 编译进 libfeatherpad.so
add_library(featherpad SHARED ${featherpad_SRCS} ${featherpad_MOC_HDRS} ${featherpad_RES})
target_compile_definitions(featherpad PRIVATE FP_NO_SVG FP_OHOS=1) # 关键宏
find_package(Qt5 REQUIRED COMPONENTS Core Gui Widgets PrintSupport)
target_link_libraries(featherpad PRIVATE Qt5::Core Qt5::Gui Qt5::Widgets Qt5::PrintSupport libhilog_ndk.z.so)
# 字体安装桥接:必须独立成库
add_library(fontbridge SHARED font_install_napi_bridge.cpp)
target_link_libraries(fontbridge PRIVATE libace_napi.z.so libhilog_ndk.z.so)
target_link_libraries(featherpad PRIVATE fontbridge)
4.2.2 QAbility 启动链
// QAbilityStage.ets —— 把 ArkUI 上下文 + Qt 库名塞给 libqohos.so
qpa.setupQtApplication({
appContext: this.context.getApplicationContext(),
appName: 'libfeatherpad.so', // 与 CMake add_library(featherpad ...) 一致
resourceDir: this.context.resourceDir,
_unusedQChildProcess: new QChildProcess(),
});
// QAbility.ets —— 生命周期全部转发给 qpa
qpa.handleAbilityOnCreate(this, want, launchParam);
qpa.handleAbilityOnWindowStageCreate(this, windowStage); // 此处同时调 FontInstallService.init(ctx)
// XComponent 必须用 libraryname: 'qohos' 绑定 libqohos.so 平台插件
XComponent({ type: XComponentType.NODE, id: xComponentId, libraryname: 'qohos' })
.width('100%').height('100%')
4.2.3 N-API 字体安装桥接(独立成库 + tsfn 派发)

// FontInstallService.ets —— ArkTS 侧
static init(ctx) { fontbridge.initFontBridge(FontInstallService); } // 把 installMsyh 静态方法注册给 C++
static async installMsyh() {
// 1) 下载 msyh.ttf 到沙箱(已有则跳过)
const req = http.createHttp();
const resp = await req.request(MSYH_URL, { expectDataType: http.HttpDataType.ARRAY_BUFFER });
fs.writeSync(fs.openSync(destPath, fs.OpenMode.CREATE | fs.OpenMode.READ_WRITE).fd, resp.result as ArrayBuffer);
// 2) 唤起系统字体安装器
await ctx.startAbility({
bundleName: 'com.huawei.hmos.fontinstaller',
abilityName: 'DoubleClickInstallAbility',
action: 'ohos.want.action.viewData',
uri: fileUri.getUriFromPath(destPath),
});
}
// font_install_napi_bridge.cpp —— C++ 侧(tsfn 派发到 JS 线程)
extern "C" bool OhosLoadMsyhFont() { // Qt 按钮点击线程调用
return napi_call_threadsafe_function(g_tsfn, nullptr, napi_tsfn_nonblocking) == napi_ok;
}
static napi_value InitFontBridge(napi_env env, napi_callback_info info) {
napi_value installFunc;
napi_get_named_property(env, argv[0], "installMsyh", &installFunc);
napi_value resourceName;
napi_create_string_utf8(env, "fontInstall", NAPI_AUTO_LENGTH, &resourceName);
napi_create_threadsafe_function(env, installFunc, nullptr, resourceName,
0, 1, nullptr, nullptr, nullptr, CallInstallMsyhOnJsThread, &g_tsfn);
napi_ref_threadsafe_function(env, g_tsfn); // 防止 GC
return undefined;
}
4.2.4 N-API 打印桥接(内嵌 + napi_ref 缓存)
// PrintService.ets
static init() { printbridge.initPrintBridge(); } // 一次性注册 PrintService 类
static async printHtml(html, jobName) {
await print.print([html], {
jobName, pageSize: { id: 'ISO_A4', name: 'A4' },
margin: { top: 20, bottom: 20, left: 20, right: 20 },
orientation: print.PrintDocumentOrientation.PORTRAIT,
colorMode: print.PrintColorMode.MONOCHROME,
duplexMode: print.PrintDuplexMode.ONE_SIDED
});
}
// ohos_print_adapter.cpp(Qt 侧)—— 一行桥接到 C++
bool OHOSPrintAdapter::callNativePrint(const QString &html, const QString &job) {
return CallPrintService(html.toUtf8().constData(), job.toUtf8().constData());
}
// print_napi_bridge.cpp(内嵌于 libfeatherpad.so)—— 存 napi_ref + napi_call_function
extern "C" bool CallPrintService(const char* html, const char* job) {
napi_value svc, fn;
napi_get_reference_value(g_env, g_printServiceRef, &svc);
napi_get_named_property(g_env, svc, "printHtml", &fn);
napi_value args[2];
napi_create_string_utf8(g_env, html, NAPI_AUTO_LENGTH, &args[0]);
napi_create_string_utf8(g_env, job, NAPI_AUTO_LENGTH, &args[1]);
napi_call_function(g_env, svc, fn, 2, args, nullptr);
return true;
}
4.2.5 SVG 双路径渲染(关键差异点)
// svgicons.cpp::symbolicIconEngine::paint()
#ifndef FP_NO_SVG
// 桌面端:QSvgRenderer 解析 + 字符串替换 #000
QSvgRenderer renderer;
renderer.load(bytes.replace("#000", col.name().toLatin1()));
renderer.render(&p, QRect(0, 0, rect.width(), rect.height()));
#else
// 鸿蒙端:QImage 读 SVG(依赖 libqsvg.so 插件)+ 逐像素着色
QImage img(fileName); // libqsvg.so 解析 SVG
img = img.scaled(rect.width(), rect.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
for (int y = 0; y < img.height(); ++y)
for (int x = 0; x < img.width(); ++x) {
QColor c = img.pixelColor(x, y);
if (c.alpha() > 0)
img.setPixelColor(x, y, QColor(col.red(), col.green(), col.blue(), c.alpha()));
}
p.drawImage(0, 0, img);
#endif
4.2.6 桌面代码裁剪实例
// fpwin.cpp:3554 —— saveFile 失败时
#ifdef FP_OHOS
showWarningBar("Please use Save As to save to a writable location.", 15);
#else
fileProcess->start("pkexec", QStringList() << "--disable-internal-agent" << "cp" << fname << target);
#endif
// textedit.cpp —— 双缓冲剪贴板
#ifdef FP_OHOS
static QString internalClipboard_;
#endif
void TextEdit::copy() {
QApplication::clipboard()->setText(text);
#ifdef FP_OHOS
internalClipboard_ = text; // 同步写一份应用内静态变量,绕开 READ_PASTEBOARD system_basic 权限
#endif
}
void TextEdit::paste() {
#ifdef FP_OHOS
if (!internalClipboard_.isEmpty()) { insertPlainText(internalClipboard_); return; }
#endif
QPlainTextEdit::paste();
}
4.3 关键配置文件
| 文件 | 关键作用 |
|---|---|
build-profile.json5(根) |
targetSdkVersion=6.0.1(21),compatibleSdkVersion=5.0.5(17),runtimeOS=HarmonyOS,arkTSVersion=1.1 |
entry/build-profile.json5 |
externalNativeOptions.path=./src/main/cpp/CMakeLists.txt,arguments=-DFEATHERPAD_BUILD_FROM_SOURCE=ON,abiFilters=["arm64-v8a"] |
entry/src/main/module.json5 |
deviceTypes=["2in1"],requestPermissions=[ohos.permission.PRINT, ohos.permission.INTERNET],mainElement=QAbility |
entry/src/main/cpp/CMakeLists.txt |
顶层 CMake 入口,from-source 开关(OFF 时用预编译 libfeatherpad.so) |
entry/src/main/cpp/CMakeLists.featherpad-from-source.cmake |
from-source 编译配置(Qt 查找、Hunspell 路径、宏定义、库链接) |
entry/src/main/ets/qability/QAbility.ets |
UIAbility 生命周期,调用 libqohos.so + FontInstallService.init() |
entry/src/main/ets/qabilitystage/QAbilityStage.ets |
AbilityStage,调用 qpa.setupQtApplication() |
entry/src/main/ets/common/QtAppConstants.ets |
APP_LIBRARY_NAME = 'libfeatherpad.so'(与 CMake 产物名一致) |
entry/src/main/ets/pages/MainWindowNativeNode.ets |
XComponent 容器,libraryname: 'qohos' |
entry/src/main/cpp/data/fp.qrc |
Qt 资源清单(help、icons/*.svg、dictionaries) |
entry/libs/arm64-v8a/libqohos.so |
Qt OHOS 平台插件(HAP 打包必需) |
entry/libs/arm64-v8a/lib{fontbridge,ohosprintsupport,qsvg,qsvgicon}.so |
各类 Qt 插件 |
entry/libs/arm64-v8a/libhunspell-1.7.{a,so} |
预编译 Hunspell |
entry/libs/arm64-v8a/libQt5*.so |
Qt 5 运行时 7 个核心库(Core/Gui/Widgets/PrintSupport/Svg/DBus/Concurrent) |
thirdparty/qt-5.12.12-ohos-sdk/ |
内置 Qt SDK(含已修复硬编码路径的 cmake 配置) |
entry/src/main/resources/resfile/dictionaries/ |
运行时可被 QFile 打开的 Hunspell 词典 |
4.4 注意事项
-
Qt SDK 路径硬编码已修复:旧版
thirdparty/qt-5.12.12-ohos-sdk/lib/cmake/Qt5Gui/Qt5GuiConfigExtras.cmake写死D:/SDK/23/native/...,新版本按OHOS_SDK_NATIVE环境变量 → 自动检测 DevEco 默认路径 → 兜底报错的顺序查找 -
Hunspell 词典双份拷贝:必须在
resfile/dictionaries/留一份(运行时路径),rawfile/dictionaries/可选(用于 Qt 资源访问) -
N-API 桥接库的"独立 vs 内嵌":
- libfontbridge.so → 必须独立(含 Qt 依赖的库会让 ArkTS 提前 dlopen,破坏 Qt 运行时加载时序)
- printbridge → 可以内嵌于 libfeatherpad.so(无 Qt 依赖)
-
N-API 线程安全:C++ 侧调用 ArkTS 函数(特别是 Qt 事件循环线程)必须用
napi_threadsafe_function派发到 JS 线程,否则直接调用 NAPI 会 cppcrash -
剪贴板方案:copy/cut 时同时写
QApplication::clipboard()(系统剪贴板)和internalClipboard_(应用内静态),形成双缓冲 -
OHOS 调色板对抗:必须在
QApplication构造后立即调applyOhosUiFixes(),否则 Untitled 编辑区会显示成黑底蓝字 -
打包 .so 完整性:HAP 打包时所有运行时 .so 必须放
entry/libs/<abi>/,包括 Qt 插件(platforms/、printsupport/、imageformats/、iconengines/)。缺一个就启动失败 -
目标设备:本工程
deviceTypes: ["2in1"],仅支持鸿蒙 PC / 平板,不支持手机 -
首次编译时长:~50 个 C++ 源文件 + 5 个 .ui + 1 个 .qrc,3-8 分钟。后续为秒级增量
-
C++ 改动后无效果:2in1 target 的 CMake 增量检测偶有失效。
Build → Clean Project或删entry/build/、entry/.cxx/、.hvigor/后重编
五、移植总结
5.1 移植模式
| 模式 | 适用场景 | 代表项目 | 复杂度 |
|---|---|---|---|
| Qt 应用嵌入 ArkUI 容器(FeatherPadNext) | 原项目本身就是 Qt 应用,希望保留 Qt 引擎层 + 极小代价上鸿蒙 | FeatherPad、KDE 应用 | ★★☆ |
| ArkTS UI 重写 + C++ 引擎(MeldNext) | 原项目是 Python/GTK/其他 UI,需要完全重写 UI 层 | MeldNext 等 | ★★★ |
| HNP 命令行工具 | 纯命令行工具,无 UI | Git、SVN、diffutils | ★☆☆ |
FeatherPadNext 走的是"最小代价"路线——保留 95% 的 Qt C++ 源码,只做四类工作:
FP_OHOS宏裁剪桌面专有代码(D-Bus、pkexec/UAC、X11/Win32、SpellDialog)- N-API 桥接系统服务(字体、打印)
- Qt SDK 路径可移植化(让仓库换电脑也能编)
- .so 全量打包进 HAP
5.2 关键技术决策
- 不重写 UI 层:直接复用 Qt Widgets + QSyntaxHighlighter,开发量最小、风险最低、原作者升级时合并最容易
FP_OHOS宏守护桌面代码:而非 fork 上游仓库,让所有 OHOS 适配与上游代码并存可读- Qt 主题适配下沉到 main.cpp:
applyOhosUiFixes()一次性解决调色板 + QSS 问题,不污染业务代码 - N-API 桥接库按需独立:libfontbridge 必须独立(Qt 依赖加载时序),libprintbridge 可内嵌(无 Qt 依赖)
- Hunspell 走 resfile 而非 rawfile:满足
QFile::open路径要求,同时保留 rawfile 备份 - Qt SDK 内置到仓库:让工程自带
thirdparty/qt-5.12.12-ohos-sdk/,构建时无需额外下载 Qt - saveFile 失败时引导 Save As:而非走 pkexec 提权,符合鸿蒙沙箱安全模型
5.3 效果
| 指标 | 说明 |
|---|---|
| 代码复用率 | Qt 引擎层 ~95% 未修改,高亮器 100% 复用 |
| 新写代码量 | ArkTS 壳层 ~10 个文件 / 300 行;N-API 桥接 ~250 行(font + print);CMake 改造 ~150 行 |
| 功能完整性 | 多标签编辑、21 语言语法高亮、查找替换、跳转行号、偏好设置、会话恢复、侧边栏、拼写检查、打印、字体下载安装、Help 文档 均正常运行 |
| 架构 / SDK | arm64-v8a / HarmonyOS 6.0.1(21),兼容 5.0.5(17) |
| 设备形态 | 2in1(鸿蒙 PC / 平板) |
| 首编译时长 | 3-8 分钟(50 个 C++ 文件);增量秒级 |
| HAP 体积 | ~30 MB(含 Qt 运行时) |
5.4 适用建议
什么项目适合走"Qt 嵌入 ArkUI 容器"路线?
- 原项目是 Qt 5/6 C++ 应用,体量较大(>50 个源文件)
- 重写 UI 工作量不亚于移植本身
- 团队对 Qt 熟悉,对 ArkTS 还在学习
- 鸿蒙端功能以"展示+编辑"为主,不需要深度集成 ArkUI 控件
什么项目建议走"ArkTS UI 重写 + C++ 引擎"路线?
- 原项目 UI 是 Python/GTK/Electron 等,重写 ArkTS 反而更轻
- 需要深度使用 ArkUI 控件(如 Navigation、List、Grid 组件)
- 原项目 C++ 引擎层逻辑独立(如 MeldNext 的 diff 引擎、文件加载器)
5.5 参考资源
- 原始 FeatherPad 仓库:https://github.com/tsujan/FeatherPad
- FeatherPadNext 鸿蒙开源地址:https://gitcode.com/OpenHarmonyPCDeveloper/ohos_FeatherPadNext
- Qt for OpenHarmony 官方模板:https://gitcode.com/openharmony-sig/qt
- OHOS Native SDK:https://developer.huawei.com/consumer/cn/deveco-studio/
- OpenHarmony 三方库移植指导:https://gitcode.com/openharmony-sig/tpc_c_cplusplus
- Hunspell 拼写检查引擎:https://github.com/hunspell/hunspell
版权声明:本文为原创技术总结,遵循 CC 4.0 BY-SA 版权协议。FeatherPad 上游代码遵循 GPL-3.0+ 协议。
更多推荐
所有评论(0)