演示通过cmake将Qt6项目编译为WebAssembly,并加载自定义中文字体

首先在官网注册并下载安装Qt。安装时需要勾选WebAssembly选项。截止目前本文所采用的Qt 6.2版本中,WebAssembly还是作为技术预览版出现,不太适合用于生产环境。

直接上代码

// main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QFontDatabase>

int main(int argc, char *argv[]) {
  QGuiApplication app(argc, argv);

  // 设置全局字体,用于解决Qt for WebAssembly中文显示异常问题
  // fileName 参数不用带qrc前缀
  int fontId = QFontDatabase::addApplicationFont(":/qtwasm/qrc/ZhiMangXing-Regular.ttf");
  QStringList fontFamilies = QFontDatabase::applicationFontFamilies(fontId);
  qDebug()<<"fontfamilies:"<<fontFamilies;
  if (fontFamilies.size() > 0)
  {
    QFont font;
    auto fontFamilie = fontFamilies[0];
    font.setFamily(fontFamilie);//设置全局字体
    app.setFont(font);
  }

  QQmlApplicationEngine engine;
  const QUrl url(QStringLiteral("qrc:/qtwasm/qrc/Main.qml"));

  engine.load(url);

  if (engine.rootObjects().isEmpty())
    return -1;

  return app.exec();
}

通过QFontDatabase类加载自定义字体。其实fileName不需要加qrc前缀,不然加载不出来。

QML文件内容如下:

// Main.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

Window {
    visible: true
    width: 800
    height: 600
    title: qsTr("Hello World")

    App{

    }
}

// App.qml
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ColumnLayout {
    Layout.fillHeight: true
    Layout.fillWidth: true

    RowLayout {
        height: 40
        Text {
            text: "Hello中文😯"
            font.pointSize: 20
        }
    }
    RowLayout {
        Layout.preferredHeight: 80
        TextEdit {
            text: "Please Input 请输入内容"
            selectByMouse: true
            font.pointSize: 20
        }
    }
}

qml内容比较简单,只是演示目的 CMakeLists.txt文件内容如下:

cmake_minimum_required(VERSION 3.15)

project(qt-wasm VERSION 0.1 LANGUAGES CXX)

set(CMAKE_VERBOSE_MAKEFILE ON)
set(CMAKE_INCLUDE_CURRENT_DIR ON)

set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Qt6 COMPONENTS Core Gui Quick REQUIRED)

set(PROJECT_SOURCES
        main.cpp)

qt_add_executable(qt-wasm
        MANUAL_FINALIZATION
        ${PROJECT_SOURCES})

target_link_libraries(qt-wasm
        PRIVATE Qt6::Core Qt6::Gui Qt6::Quick)

# 当编译WebAssembly时修改链接参数,否则会报初始内存太小错误
if(EMSCRIPTEN)
    target_link_options(qt-wasm PRIVATE -s TOTAL_MEMORY=32MB)
endif()

qt_import_qml_plugins(qt-wasm)
qt_finalize_executable(qt-wasm)

qt_add_qml_module(qt-wasm
        URI qtwasm
        VERSION 1.0
        QML_FILES
        qrc/Main.qml qrc/App.qml
        RESOURCES
        qrc/ZhiMangXing-Regular.ttf)

其中qt_add_qml_module为qt 6.2新增的cmake api 在编译wasm时遇到了初始内存大小超出限制的错误,经过搜索需要加上TOTAL_MEMORY链接参数。在以上cmake文件内容中,通过判断是WebAssembly平台即加上该参数。 中文字体可以在网上下载或本机系统目录中拷贝。

在项目目录执行以下命令构建wasm文件:

mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=~/Qt/6.2.0/macos/lib/cmake -DCMAKE_TOOLCHAIN_FILE:PATH=~/Qt/6.2.0/wasm_32/lib/cmake/Qt6/qt.toolchain.cmake ..
make

需要把qt的文件目录,和wasm工具链的路径参数传递给cmake命令。 注意需要提前安装Emscripten编译环境,参考官方链接安装:Download and install — Emscripten 3.1.4-git (dev) documentation 执行make构建完成后,会在build目录生成以下文件:

qt-wasm.html
qt-wasm.js
qt-wasm.wasm
qtloader.js

其中qt-wasm是我们在CMakeLists.txt文件中设置的可执行文件名称 如果想看构建完wasm文件之后的运行效果,可以在build目录启动一个简单的http服务器,在浏览器访问相应端口即可。 这里采用node的http-server来运行:

npm i -g http-server # 安装工具,需要node环境 http-sever # 下build目录执行该命令

执行完http-server命令后,即可在浏览器中访问127.0.0.1:8080来查看编译后的效果。 qt通过网页中的canvas来绘制qml文件中描述的内容。

目前可以显示中文了,但是对中文输入法的支持很不好,无法正常输入。同时生成的wasm文件大约有20M,即使gzip压缩后也有7M左右,是不太适合直接应用到网站项目中的。希望后续会有所改善。