添加中文字体
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
"""
|
||||
将项目 fonts/ 目录下的中文字体安装到 Docker QGIS 容器。
|
||||
|
||||
QGIS 官方 Docker 镜像不包含中文字体,模板中的 SimHei/SimSun/YaHei 字体会显示为方块。
|
||||
本脚本将 fonts/ 目录下的字体文件复制到容器内并刷新字体缓存。
|
||||
|
||||
用法:
|
||||
python app/script/install_fonts_to_container.py [--container qgis-server] [--dry-run]
|
||||
|
||||
前置条件:
|
||||
- Docker 容器已启动(docker start qgis-server)
|
||||
- 项目根目录下 fonts/ 目录包含所需的 .ttf/.ttc 字体文件
|
||||
"""
|
||||
import argparse
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent
|
||||
FONTS_DIR = PROJECT_ROOT / "fonts"
|
||||
|
||||
# 容器内字体目录
|
||||
CONTAINER_FONT_DIR = "/usr/share/fonts/truetype/winfonts"
|
||||
|
||||
|
||||
def _run(cmd, timeout=10, **kwargs):
|
||||
"""subprocess.run 封装,统一 UTF-8 编码"""
|
||||
return subprocess.run(
|
||||
cmd, capture_output=True, timeout=timeout,
|
||||
encoding="utf-8", errors="replace", **kwargs,
|
||||
)
|
||||
|
||||
|
||||
def _load_config():
|
||||
"""从 settings.toml 读取配置"""
|
||||
try:
|
||||
from config import settings
|
||||
return {
|
||||
"container": getattr(settings, "QGIS_DOCKER_CONTAINER", "qgis-server"),
|
||||
}
|
||||
except ImportError:
|
||||
pass
|
||||
|
||||
cfg = {"container": "qgis-server"}
|
||||
toml_path = PROJECT_ROOT / "settings.toml"
|
||||
if toml_path.exists():
|
||||
for line in toml_path.read_text(encoding="utf-8").splitlines():
|
||||
line = line.strip()
|
||||
if line.startswith("QGIS_DOCKER_CONTAINER") and "=" in line:
|
||||
cfg["container"] = line.split("=", 1)[1].strip().strip('"').strip("'")
|
||||
return cfg
|
||||
|
||||
|
||||
def _check_container(container: str):
|
||||
"""检查容器是否运行"""
|
||||
result = _run(["docker", "inspect", "--format={{.State.Running}}", container], timeout=5)
|
||||
if (result.stdout or "").strip() != "true":
|
||||
raise RuntimeError(f"容器 {container} 未运行,请先 docker start {container}")
|
||||
|
||||
|
||||
def install_fonts(container: str = None, dry_run: bool = False):
|
||||
"""将 fonts/ 目录下的字体安装到容器"""
|
||||
cfg = _load_config()
|
||||
if container is None:
|
||||
container = cfg["container"]
|
||||
|
||||
_check_container(container)
|
||||
|
||||
# 扫描字体文件
|
||||
if not FONTS_DIR.is_dir():
|
||||
print(f"字体目录不存在: {FONTS_DIR}")
|
||||
print(f"请先在项目根目录下创建 fonts/ 目录,并放入 .ttf/.ttc 字体文件。")
|
||||
print(f"Windows 字体路径: C:\\Windows\\Fonts\\")
|
||||
print(f" simhei.ttf — 黑体(模板默认字体)")
|
||||
print(f" simsun.ttc — 宋体")
|
||||
print(f" msyh.ttc — 微软雅黑")
|
||||
print(f" msyhbd.ttc — 微软雅黑粗体")
|
||||
sys.exit(1)
|
||||
|
||||
font_files = [f for f in FONTS_DIR.iterdir()
|
||||
if f.is_file() and f.suffix.lower() in (".ttf", ".ttc", ".otf")]
|
||||
|
||||
if not font_files:
|
||||
print(f"字体目录为空或无字体文件: {FONTS_DIR}")
|
||||
print(f"请放入 .ttf/.ttc/.otf 字体文件后重试。")
|
||||
sys.exit(1)
|
||||
|
||||
print(f"=== 安装中文字体到 Docker 容器 {container} ===\n")
|
||||
print(f" 字体目录: {FONTS_DIR}")
|
||||
print(f" 字体文件: {len(font_files)} 个")
|
||||
for f in sorted(font_files):
|
||||
size_kb = f.stat().st_size / 1024
|
||||
print(f" - {f.name} ({size_kb:.0f} KB)")
|
||||
print(f" 容器目标: {container}:{CONTAINER_FONT_DIR}")
|
||||
print()
|
||||
|
||||
if dry_run:
|
||||
print(" [dry-run] 跳过安装")
|
||||
return
|
||||
|
||||
# 1. 在容器内创建字体目录
|
||||
t0 = time.time()
|
||||
_run(["docker", "exec", container, "mkdir", "-p", CONTAINER_FONT_DIR])
|
||||
|
||||
# 2. 逐个复制字体文件到容器
|
||||
print(" [1/3] 复制字体文件...")
|
||||
for f in sorted(font_files):
|
||||
result = _run(
|
||||
["docker", "cp", str(f), f"{container}:{CONTAINER_FONT_DIR}/{f.name}"],
|
||||
timeout=30,
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f" FAIL {f.name}: {result.stderr.strip()}")
|
||||
else:
|
||||
print(f" OK {f.name}")
|
||||
|
||||
# 3. 刷新字体缓存
|
||||
print("\n [2/3] 刷新字体缓存...")
|
||||
result = _run(["docker", "exec", container, "fc-cache", "-fv"], timeout=30)
|
||||
if result.returncode != 0:
|
||||
print(f" WARN fc-cache 输出: {result.stderr.strip()}")
|
||||
else:
|
||||
print(" OK")
|
||||
|
||||
# 4. 验证字体
|
||||
print("\n [3/3] 验证字体...")
|
||||
verify_script = (
|
||||
"from PyQt5.QtGui import QFontDatabase; "
|
||||
"db = QFontDatabase(); "
|
||||
"zh = [f for f in db.families() if any(k in f for k in "
|
||||
"['SimHei','YaHei','SimSun','WenQuanYi','Noto Sans CJK'])]; "
|
||||
"print('中文字体:', zh if zh else '未安装!')"
|
||||
)
|
||||
result = _run(
|
||||
["docker", "exec", container, "python3", "-c", verify_script],
|
||||
timeout=10,
|
||||
)
|
||||
print(f" {(result.stdout or '').strip()}")
|
||||
|
||||
elapsed = time.time() - t0
|
||||
print(f"\n=== 完成,耗时 {elapsed:.1f}s ===")
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description="将中文字体安装到 Docker QGIS 容器")
|
||||
parser.add_argument("--container", default=None, help="Docker 容器名称")
|
||||
parser.add_argument("--dry-run", action="store_true", help="仅显示信息,不实际安装")
|
||||
args = parser.parse_args()
|
||||
|
||||
try:
|
||||
install_fonts(container=args.container, dry_run=args.dry_run)
|
||||
except Exception as e:
|
||||
print(f"错误: {e}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user