Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
88 changes: 88 additions & 0 deletions TeXmacs/tests/1166.scm
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; MODULE : 1166.scm
;; DESCRIPTION : 集成测试:文本模式字符的逻辑字体与物理字体解析。
;; COPYRIGHT : (C) 2026 Mogan STEM
;;
;; PURPOSE
;; [1166] 钉死文本模式下 Unicode 码位 0–255 的字体解析契约(后续逐步扩大
;; 码位范围):
;; 1. 逻辑字体:当前环境 font/font-family/font-series/font-shape 为
;; roman rm medium right(默认文档、文本模式)。
;; 2. 物理字体:每个码位经 smart font 按覆盖度解析出的实际叶子字体
;; (physical-font-for-string,真实排版路径量得):
;; - 大多数码位 -> ec:ecrm10@600(随仓库分发的 Cork 字体)
;; - 128–160 及部分 Latin-1 符号 -> unicode:cmunrm10@600
;; (TeXmacs/fonts/opentype/cm-unicode 随仓库分发,跨机器稳定)
;; - 181 (µ) -> gr:grmn1000@600(希腊 tfm)
;; 3. 不可渲染识别:无字体覆盖的码位(如 U+10FFFE)落到 error 字体,
;; res_name 以 "error-" 开头。
;;
;; 码位区间以 font-test-segments 参数化;扩大范围时追加区间并扩展
;; expected-physical-font 的例外表即可。
;;
;; USAGE
;; xmake b stem
;; xmake r 1166 # headless,同步断言全量执行
;; MOGAN_TEST_GUI=1 xmake r 1166 # 真实 GUI,同样同步执行
;;
;; 注意:本测试全程同步(physical-font-for-string 是同步 bridge),
;; headless 与 GUI 模式都会真正执行断言。
;;
;; This software falls under the GNU general public license version 3 or later.
;; It comes WITHOUT ANY WARRANTY whatsoever. For details see LICENSE.
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(import (liii check))
(import (liii unicode))

(check-set-mode! 'report-failed)

;; 待覆盖的码位区间(闭区间)。后续扩大范围时在此追加。
(define font-test-segments '((0 . 255)))

;; 解析到 unicode:cmunrm10@600 的码位(0–255 内)。
(define cmunrm-codepoints
'(128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
147 148 149 150 151 152 153 154 155 156 157 158 159 160 162 164 165 166 169
170 172 173 174 176 177 178 179 182 183 185 186 188 189 190 215 247))

;; 0–255 的物理字体期望:默认 Cork 字体,例外见上。
(define (expected-physical-font n)
(cond ((== n 181) "gr:grmn1000@600")
((member n cmunrm-codepoints) "unicode:cmunrm10@600")
(else "ec:ecrm10@600")))

;; 码位 -> tm 内部字符串(cork;超出 cork 的由 utf8->cork 转 <#XXXX>)。
(define (codepoint->tmstring n)
(utf8->cork (utf8-string (integer->char n))))

(define (test-logical-font)
;; 前置:默认环境为文本模式。
(check (get-env "mode") => "text")
(check (get-env "font") => "roman")
(check (get-env "font-family") => "rm")
(check (get-env "font-series") => "medium")
(check (get-env "font-shape") => "right"))

(define (test-physical-fonts lo hi)
(do ((n lo (+ n 1)))
((> n hi))
(check (physical-font-for-string (codepoint->tmstring n))
=> (expected-physical-font n))))

(define (test-unrenderable-detection)
;; U+10FFFE 无字体覆盖,须落到 error 字体(res_name 前缀 error-)。
(let ((f (physical-font-for-string (codepoint->tmstring #x10FFFE))))
(check-true (and (> (string-length f) 6)
(string=? (substring f 0 6) "error-")))))

(tm-define (test_1166)
(test-logical-font)
(for (seg font-test-segments)
(test-physical-fonts (car seg) (cdr seg)))
(test-unrenderable-detection)
(check-report)
(quit-TeXmacs)
) ;tm-define
70 changes: 70 additions & 0 deletions devel/1166.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# [1166] 文本模式字符的逻辑字体与物理字体集成测试

## 1 任务描述

为文本模式的字符新增集成测试:给定 Unicode 码位范围(首段 0–255,后续逐步扩大),
钉死每个字符在文本模式下解析到的逻辑字体与物理字体,并能识别出无法渲染
(落到 error 字体)的字符。

## 2 任务相关的代码文件

### 新增
- `TeXmacs/tests/1166.scm` — GUI 集成测试(`xmake r 1166`)
- `devel/1166.md` — 本文档

### 修改
- `src/Edit/Editor/edit_typeset.hpp` / `edit_typeset.cpp` — 新增
`physical_font_for_string`:在当前文档环境(文本模式)下排版给定字符串,
返回叶子物理字体的 `res_name`(无法渲染时为 `error-` 前缀)
- `src/Scheme/Glue/glue_editor.lua` — 注册 scheme 绑定 `physical-font-for-string`

## 3 设计说明

- **逻辑字体**:由当前环境变量给出(`font` / `font-family` / `font-series` /
`font-shape`),scheme 侧用现有 `get-env` 读取,无需新 bridge。
- **物理字体**:逻辑字体经 `find_font` / smart font 按字符覆盖度解析出的实际
字体(如 `ec:ecrm10@600`、`unicode:NotoSerif-Regular10@600`)。字符无字体
覆盖时落到 error 字体,`res_name` 以 `error-` 开头。
- bridge 复用 `print_snippet` 的临时文档排版路径(`new_document` +
`typeset_as_box` + `get_leaf_font`),量的是真实排版结果而非内部缓存。
- 测试以码位区间参数化,期望表按区间分段维护,便于后续逐步扩大覆盖范围。

## 4 如何测试

```bash
xmake b stem
xmake r 1166 # headless 冒烟
MOGAN_TEST_GUI=1 xmake r 1166 # 真实 GUI,执行断言链
```

## 5 改动记录

### 2026-08-04

- **What**:新增文本模式字符字体解析集成测试(码位 0–255),及配套
`physical-font-for-string` bridge。
- **Why**:钉死逻辑字体→物理字体的解析契约,识别无字体覆盖落到 error
字体的字符,为后续扩大 Unicode 覆盖范围提供回归基线。
- **How**:
- `edit_typeset_rep::physical_font_for_string` 复用 `print_snippet` 的
临时文档排版路径(`new_document` + `typeset_as_box`),递归下钻到
TEXT_BOX 叶子取字体;叶子挂的是 smart font,再经新增的虚函数
`font_rep::get_subfont`(`smart_font_rep` 用 `advance` 解析到实际
子字体)得到物理字体 `res_name`;无覆盖时经 SUBFONT_ERROR 得到
`error-` 前缀名。
- `editor_rep` 新增纯虚 `physical_font_for_string`(glue 经
`get_current_editor()->` 调用,走 editor_rep 虚表)。
- 测试数据:0–255 中 200 码位 → `ec:ecrm10@600`,55 码位 →
`unicode:cmunrm10@600`(128–160 及部分 Latin-1 符号),181 (µ) →
`gr:grmn1000@600`;三类字体均随仓库分发,跨机器稳定。
- **涉及文件**:
- `src/Edit/Editor/edit_typeset.hpp` / `edit_typeset.cpp`(bridge +
`find_leaf_font` 辅助)
- `src/Edit/editor.hpp`(纯虚声明)
- `src/Graphics/Fonts/font.hpp` / `font.cpp`(`get_subfont` 默认实现)
- `src/Graphics/Fonts/smart_font.hpp` / `smart_font.cpp`(`get_subfont`
覆写)
- `src/Scheme/Glue/glue_editor.lua`(scheme 绑定)
- `TeXmacs/tests/1166.scm`(集成测试,headless/GUI 均同步执行断言)
- **验证**:`xmake r 1166` 与 `MOGAN_TEST_GUI=1 xmake r 1166` 均
262 correct / 0 failed。
33 changes: 33 additions & 0 deletions src/Edit/Editor/edit_typeset.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@
#include "file.hpp"
#include "iterator.hpp"
#include "merge_sort.hpp"
#include "new_document.hpp"
#include "new_style.hpp"
#include "observers.hpp"
#include "tm_buffer.hpp"
#include "tm_timer.hpp"
#include "tree_modify.hpp"
#include "tree_observer.hpp"
#include "typesetter.hpp"
#include <climits>

using namespace moebius;
Expand Down Expand Up @@ -617,6 +619,37 @@ edit_typeset_rep::get_env_string (string var) {
return as_string (get_env_value (var));
}

static font
find_leaf_font (box b) {
if (is_nil (b)) return font ();
if (b->get_type () == TEXT_BOX) return b->get_leaf_font ();
int n= N (b);
for (int i= 0; i < n; i++) {
font f= find_leaf_font (b[i]);
if (!is_nil (f)) return f;
}
return font ();
}

string
edit_typeset_rep::physical_font_for_string (string s) {
// 复用 print_snippet 的临时文档排版路径,量真实排版结果:
// 叶子物理字体的 res_name;字符无字体覆盖时为 "error-" 前缀
path temp_root= new_document ();
temp_root << 0;
assign (subtree (et, temp_root), tree (s));
typeset_prepare ();
env->style_init_env ();
env->update ();
box b= typeset_as_box (env, tree (s), reverse (temp_root));
delete_document (path_up (temp_root));
font fn= find_leaf_font (b);
if (is_nil (fn)) return "";
// 文本盒挂的是 smart font,需按字符下钻到实际物理子字体
font sub= fn->get_subfont (s);
return is_nil (sub) ? string ("") : sub->res_name;
}

string
edit_typeset_rep::get_init_string (string var) {
return as_string (get_init_value (var));
Expand Down
1 change: 1 addition & 0 deletions src/Edit/Editor/edit_typeset.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ class edit_typeset_rep : virtual public editor_rep {
color get_env_color (string var_name);
color get_init_color (string var_name);
language get_env_language ();
string physical_font_for_string (string s);
int get_page_count ();
string get_page_number_text (int page_index);
int get_current_page ();
Expand Down
1 change: 1 addition & 0 deletions src/Edit/editor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ class editor_rep : public simple_widget_rep {
virtual color get_env_color (string var_name) = 0;
virtual color get_init_color (string var_name) = 0;
virtual language get_env_language () = 0;
virtual string physical_font_for_string (string s) = 0;
virtual int get_page_count () = 0;
virtual string get_page_number_text (int page_index) = 0;
virtual int get_current_page () = 0;
Expand Down
6 changes: 6 additions & 0 deletions src/Graphics/Fonts/font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -551,6 +551,12 @@ use_poor_rubber (font fn) {
!starts (fn->res_name, "stix-");
}

font
font_rep::get_subfont (string s) {
(void) s;
return font (this);
}

font
font_rep::make_rubber_font (font fn) {
string name= locase_all (fn->res_name);
Expand Down
1 change: 1 addition & 0 deletions src/Graphics/Fonts/font.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ struct font_rep : rep<font> {
void copy_math_pars (font fn);

virtual font make_rubber_font (font base);
virtual font get_subfont (string s);
virtual bool supports (string c) = 0;
virtual void get_extents (string s, metric& ex)= 0;
virtual void get_extents (string s, metric& ex, bool ligf);
Expand Down
11 changes: 11 additions & 0 deletions src/Graphics/Fonts/smart_font.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -868,6 +868,17 @@ smart_font_rep::advance (string s, int& pos, string& r, int& nr) {
}
}

font
smart_font_rep::get_subfont (string s) {
int pos= 0, nr= -1;
string r;
advance (s, pos, r, nr);
if (nr < 0 || nr >= N (fn)) return font ();
maybe_initialize_font (nr);
if (is_nil (fn[nr])) return font ();
return fn[nr]->get_subfont (r);
}

bool
is_italic_font (string master) {
return contains (string ("italic"), master_features (master));
Expand Down
1 change: 1 addition & 0 deletions src/Graphics/Fonts/smart_font.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ struct smart_font_rep : font_rep {
int adjusted_dpi (string fam, string var, string ser, string sh, int att);

font make_rubber_font (font base) override;
font get_subfont (string s) override;

bool supports (string c);
void get_extents (string s, metric& ex);
Expand Down
8 changes: 8 additions & 0 deletions src/Scheme/Glue/glue_editor.lua
Original file line number Diff line number Diff line change
Expand Up @@ -389,6 +389,14 @@ function main()
"string"
}
},
{
scm_name = "physical-font-for-string",
cpp_name = "physical_font_for_string",
ret_type = "string",
arg_list = {
"string"
}
},
{
scm_name = "get-env-tree",
cpp_name = "get_env_value",
Expand Down
Loading