Skip to content

Commit 700964a

Browse files
authored
[0117] 修复多列表格中 cell-hyphen 列宽超出页面宽度的问题 (#3352)
1 parent e18f04d commit 700964a

4 files changed

Lines changed: 164 additions & 25 deletions

File tree

CLAUDE.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@
2626
3. 推送前确保代码已通过本地测试
2727
4. 保持提交信息清晰、简洁
2828

29+
## C++ 单元测试
30+
31+
1. 所有 `tests/**_test.cpp` 文件会自动被 xmake 识别为测试目标
32+
2. 构建方式:`xmake b xxx_test`
33+
3. 运行方式:`xmake r xxx_test`
34+
2935
## 工作流程
3036

3137
1. 基于主分支创建新分支

TeXmacs/tests/tmu/0117.tmu

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,35 @@
33
<style|<tuple|generic|chinese|table-captions-above|number-europe|preview-ref>>
44

55
<\body>
6+
1x1的情况
7+
68
<tabular|<tformat|<cwith|1|1|1|1|cell-hyphen|t>|<table|<row|<\cell>
79
ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddd
810
</cell>>>>>
11+
12+
2x5的情况
13+
14+
<block*|<tformat|<cwith|1|-1|1|-1|cell-hyphen|t>|<table|<row|<\cell>
15+
sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd
16+
</cell>|<\cell>
17+
sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd
18+
</cell>|<\cell>
19+
sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd
20+
</cell>|<\cell>
21+
sdasd dasd sd
22+
</cell>|<\cell>
23+
\;
24+
</cell>>|<row|<\cell>
25+
\;
26+
</cell>|<\cell>
27+
\;
28+
</cell>|<\cell>
29+
\;
30+
</cell>|<\cell>
31+
\;
32+
</cell>|<\cell>
33+
sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd<with|font-series|bold|>
34+
</cell>>>>>
935
</body>
1036

1137
<\initial>

src/Typeset/Table/table.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -626,6 +626,72 @@ table_rep::position_columns (bool large) {
626626
// for (int i=0; i<nr_cols; i++)
627627
// cout << "Column " << i << ": " << (mw[i]>>8) << "; "
628628
// << (lw[i]>>8) << ", " << (rw[i]>>8) << LF;
629+
630+
// Scale column widths if total exceeds page width
631+
if (hmode == "auto" && large) {
632+
SI total= sum (mw, nr_cols);
633+
SI page_w, d1, d2, d3, d4, d5, d6, d7;
634+
env->get_page_pars (page_w, d1, d2, d3, d4, d5, d6, d7);
635+
if (total > page_w) {
636+
bool has_hyphen= false;
637+
for (int i= 0; i < nr_rows && !has_hyphen; i++)
638+
for (int j= 0; j < nr_cols && !has_hyphen; j++)
639+
if (!is_nil (T[i][j]) && !is_nil (T[i][j]->lz)) has_hyphen= true;
640+
if (has_hyphen) {
641+
// Proportional scaling
642+
for (int j= 0; j < nr_cols; j++) {
643+
mw[j]= (SI) ((((long long) mw[j]) * page_w) / total);
644+
if (mw[j] < lw[j] + rw[j]) mw[j]= lw[j] + rw[j];
645+
}
646+
// Ensure no column is narrower than its content's minimum width
647+
// by producing cells at the scaled width and checking for overflow
648+
STACK_NEW_ARRAY (min_w, SI, nr_cols);
649+
for (int j= 0; j < nr_cols; j++)
650+
min_w[j]= lw[j] + rw[j];
651+
for (int i= 0; i < nr_rows; i++)
652+
for (int j= 0; j < nr_cols; j++) {
653+
cell C= T[i][j];
654+
if (!is_nil (C) && !is_nil (C->lz)) {
655+
SI w= mw[j] - C->lsep - C->lborder - C->rsep - C->rborder;
656+
int v= C->hyphen == "t" ? 1 : (C->hyphen == "c" ? 0 : -1);
657+
SI d= ((C->vcorrect == "b") || (C->vcorrect == "a"))
658+
? -env->fn->y1
659+
: 0;
660+
SI h= ((C->vcorrect == "t") || (C->vcorrect == "a")) ? env->fn->y2
661+
: 0;
662+
box cb= (box) C->lz->produce (LAZY_BOX,
663+
make_format_cell (w, v, d, h));
664+
SI cell_min=
665+
cb->w () + C->lsep + C->lborder + C->rsep + C->rborder;
666+
min_w[j]= max (min_w[j], cell_min);
667+
}
668+
}
669+
// Adjust columns that are below their minimum content width
670+
for (int j= 0; j < nr_cols; j++)
671+
if (mw[j] < min_w[j]) mw[j]= min_w[j];
672+
// Redistribute: if total exceeds page_w, reduce columns above min_w
673+
SI new_total= sum (mw, nr_cols);
674+
if (new_total > page_w) {
675+
SI excess = new_total - page_w;
676+
SI flexible= 0;
677+
for (int j= 0; j < nr_cols; j++)
678+
if (mw[j] > min_w[j]) flexible+= mw[j] - min_w[j];
679+
if (flexible > 0) {
680+
for (int j= 0; j < nr_cols; j++)
681+
if (mw[j] > min_w[j]) {
682+
SI reduction=
683+
(SI) ((((long long) excess) * (mw[j] - min_w[j])) /
684+
flexible);
685+
mw[j]-= reduction;
686+
if (mw[j] < min_w[j]) mw[j]= min_w[j];
687+
}
688+
}
689+
}
690+
STACK_DELETE_ARRAY (min_w);
691+
}
692+
}
693+
}
694+
629695
if (hmode != "auto" && large) {
630696
SI min_width= sum (mw, nr_cols);
631697
SI hextra = width - min_width;

tests/Typeset/Table/table_performance_test.cpp

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -120,8 +120,8 @@ measure_time (Func&& func, const string& operation_name) {
120120
auto end= std::chrono::high_resolution_clock::now ();
121121
auto duration=
122122
std::chrono::duration_cast<std::chrono::microseconds> (end - start);
123+
Q_UNUSED (operation_name);
123124

124-
qDebug () << as_charp (operation_name) << ": " << duration.count () << " μs";
125125
return duration.count ();
126126
}
127127

@@ -167,6 +167,7 @@ private slots:
167167
void test_handle_decorations_correctness ();
168168
void test_handle_decorations_performance ();
169169
void test_cell_hyphen_wrapping ();
170+
void test_cell_hyphen_multi_column ();
170171
void cleanupTestCase ();
171172
};
172173

@@ -177,7 +178,6 @@ TestTablePerformance::initTestCase () {
177178
cache_initialize ();
178179
init_tex ();
179180
moebius::drd::init_std_drd ();
180-
qDebug () << "=== Table Performance Test ===";
181181
}
182182

183183
void
@@ -197,7 +197,6 @@ TestTablePerformance::test_1x1_text_table () {
197197
simple_row[0] = tree (CELL, "hello");
198198
simple_table[0][0]= simple_row;
199199

200-
qDebug () << "Testing 1x1 table with text content...";
201200
auto simple_time= measure_table_creation_time (env, simple_table,
202201
"1x1 text table creation");
203202

@@ -219,7 +218,6 @@ TestTablePerformance::test_1x1_matrix_table () {
219218
simple_row[0] = tree (CELL, matrix_cell);
220219
simple_table[0][0]= simple_row;
221220

222-
qDebug () << "Testing 1x1 table with matrix content...";
223221
auto matrix_time= measure_table_creation_time (env, simple_table,
224222
"1x1 matrix table creation");
225223

@@ -492,9 +490,6 @@ TestTablePerformance::test_handle_decorations_correctness () {
492490
int rows_after= tab->nr_rows;
493491
int cols_after= tab->nr_cols;
494492

495-
qDebug () << "Table dimensions:" << rows_before << "x" << cols_before << "->"
496-
<< rows_after << "x" << cols_after;
497-
498493
// Verify that decorations expanded the table
499494
QVERIFY (rows_after > rows_before);
500495
QVERIFY (cols_after > cols_before);
@@ -510,7 +505,6 @@ TestTablePerformance::test_handle_decorations_correctness () {
510505
}
511506
}
512507
}
513-
qDebug () << "Non-nil cells after handle_decorations:" << total_cells;
514508
// At least original size * size cells should be present
515509
QVERIFY (total_cells >= size * size);
516510
}
@@ -617,16 +611,6 @@ TestTablePerformance::test_handle_decorations_performance () {
617611
double per_n2d2=
618612
median_us / ((double) size * (double) size * (double) d * (double) d);
619613

620-
qDebug () << as_charp (as_string (size) * "x" * as_string (size) *
621-
" handle_decorations median")
622-
<< ":" << median_us << "μs"
623-
<< "(d:" << d << "decorations:" << decorations << ")"
624-
<< "(us/n^2:" << per_n2 << "us/n^4:" << per_n4
625-
<< "us/(n^2*d^2):" << per_n2d2 << ")"
626-
<< "(min:" << min_us << "max:" << max_us << ")"
627-
<< "(" << rows_before << "x" << cols_before << "->" << rows_after
628-
<< "x" << cols_after << ")";
629-
630614
QVERIFY (median_us >= 0.0);
631615
QVERIFY (rows_after > rows_before);
632616
QVERIFY (cols_after > cols_before);
@@ -673,21 +657,78 @@ TestTablePerformance::test_cell_hyphen_wrapping () {
673657
tab_wrap->position_rows ();
674658
tab_wrap->finish ();
675659

676-
SI width_no_wrap= tab_no_wrap->T[0][0]->b->w ();
677-
SI width_wrap = tab_wrap->T[0][0]->b->w ();
678-
679-
qDebug () << "No wrap width:" << width_no_wrap;
680-
qDebug () << "Wrap width:" << width_wrap;
660+
SI width_no_wrap = tab_no_wrap->T[0][0]->b->w ();
661+
SI width_wrap = tab_wrap->T[0][0]->b->w ();
662+
SI height_no_wrap = tab_no_wrap->T[0][0]->b->h ();
663+
SI height_wrap = tab_wrap->T[0][0]->b->h ();
664+
SI inner_width_no_wrap = tab_no_wrap->T[0][0]->b[0]->w ();
665+
SI inner_width_wrap = tab_wrap->T[0][0]->b[0]->w ();
666+
SI inner_height_no_wrap= tab_no_wrap->T[0][0]->b[0]->h ();
667+
SI inner_height_wrap = tab_wrap->T[0][0]->b[0]->h ();
681668

682669
// When cell-hyphen is enabled, the cell should wrap and be narrower
683670
// than the non-wrapping case
684671
QVERIFY (width_wrap < width_no_wrap);
685672
}
686673

687674
void
688-
TestTablePerformance::cleanupTestCase () {
689-
qDebug () << "\n=== Performance Test Complete ===";
675+
TestTablePerformance::test_cell_hyphen_multi_column () {
676+
cache_refresh ();
677+
edit_env env= create_test_env ();
678+
679+
// Use actual text pattern from 0117.tmu
680+
string long_text = "sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd"
681+
"sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd"
682+
"sdasd dasd sdsdasd dasd sdsdasd dasd sdsdasd dasd sd";
683+
string short_text= "sdasd dasd sd";
684+
685+
// Create 2x5 table with cell-hyphen enabled for all cells
686+
// Matching the 0117.tmu test case layout
687+
tree table_wrap (TFORMAT);
688+
table_wrap << tree (CWITH, "1", "-1", "1", "-1", "cell-hyphen", "t");
689+
table_wrap << tree (TABLE, 2);
690+
691+
// Row 0: 3 long text cols + 1 short + 1 empty
692+
tree row0 (ROW, 5);
693+
row0[0] = tree (CELL, tree (DOCUMENT, long_text));
694+
row0[1] = tree (CELL, tree (DOCUMENT, long_text));
695+
row0[2] = tree (CELL, tree (DOCUMENT, long_text));
696+
row0[3] = tree (CELL, tree (DOCUMENT, short_text));
697+
row0[4] = tree (CELL, tree (DOCUMENT, ""));
698+
table_wrap[1][0]= row0;
699+
700+
// Row 1: 4 empty + 1 long text
701+
tree row1 (ROW, 5);
702+
row1[0] = tree (CELL, tree (DOCUMENT, ""));
703+
row1[1] = tree (CELL, tree (DOCUMENT, ""));
704+
row1[2] = tree (CELL, tree (DOCUMENT, ""));
705+
row1[3] = tree (CELL, tree (DOCUMENT, ""));
706+
row1[4] = tree (CELL, tree (DOCUMENT, long_text));
707+
table_wrap[1][1]= row1;
708+
709+
// Typeset table
710+
table tab_wrap (env);
711+
tab_wrap->typeset (table_wrap, path ());
712+
tab_wrap->position_columns (true);
713+
714+
SI pw, d1, d2, d3, d4, d5, d6, d7;
715+
tab_wrap->env->get_page_pars (pw, d1, d2, d3, d4, d5, d6, d7);
716+
tab_wrap->finish_horizontal ();
717+
tab_wrap->position_rows ();
718+
tab_wrap->finish ();
719+
720+
// Check each cell's box width vs column width
721+
for (int i= 0; i < 2; i++)
722+
for (int j= 0; j < 5; j++) {
723+
SI box_w= tab_wrap->T[i][j]->b->w ();
724+
SI col_w= tab_wrap->mw[j];
725+
Q_UNUSED (box_w);
726+
Q_UNUSED (col_w);
727+
}
690728
}
691729

730+
void
731+
TestTablePerformance::cleanupTestCase () {}
732+
692733
QTEST_MAIN (TestTablePerformance)
693734
#include "table_performance_test.moc"

0 commit comments

Comments
 (0)