Skip to content

Commit bcc002a

Browse files
authored
Merge pull request #18 from igapyon/tiga0319vaa
表検出の過剰な一体化を抑制し、`table` fixture を追加する
2 parents ff9b68e + 87980d1 commit bcc002a

26 files changed

Lines changed: 612 additions & 6 deletions

docs/TODO.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
- fixture 用 Excel ブックを追加する
66
- `tests/fixtures/formula/formula-spill-sample01.xlsx`
7+
- `tests/fixtures/merge/merge-multiline-sample01.xlsx`
8+
- 結合セル内の改行付きテキストを確認する fixture
9+
- 追加時は fixture だけでなく Markdown 正規化ポリシー変更もセットで見直す
10+
- 現状は `markdown-normalize.ts``sheet-markdown.ts` で改行を空白化し、`xlsx2md-sheet-markdown.test.js` もその前提
711
- formula 次段タスク
812
- `scripts/observe-xlsx2md-formulas.mjs` による観測を継続し、AST evaluator 側へ寄せる関数群を整理する
913
- 優先順は `cached value -> AST evaluator -> 既存 resolver -> fallback_formula` で固定
@@ -19,6 +23,7 @@
1923
- 表セル、narrative、見出し、箇条書きで共通方針を持つ
2024
- 少なくとも `改行 / | / \`` を安全に扱う
2125
- 必要に応じて行頭の Markdown 記号 (`#`, `-`, `*`, `>`) も整理する
26+
- 結合セル内の改行を `<br>` として許容するか、別の表現にするかを決める
2227

2328
## 未対応事項
2429

src/xlsx2md/js/table-detector.js

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,13 @@
6262
function getBoundsArea(bounds) {
6363
return Math.max(1, (bounds.endRow - bounds.startRow + 1) * (bounds.endCol - bounds.startCol + 1));
6464
}
65+
function getCombinedCandidateArea(candidates) {
66+
return candidates.reduce((sum, candidate) => sum + getBoundsArea(candidate), 0);
67+
}
6568
function pruneRedundantCandidates(candidates) {
6669
return candidates.filter((candidate, candidateIndex) => {
6770
const candidateArea = getBoundsArea(candidate);
68-
return !candidates.some((other, otherIndex) => {
71+
const hasSingleDominatingContainedCandidate = candidates.some((other, otherIndex) => {
6972
if (candidateIndex === otherIndex)
7073
return false;
7174
if (!isWithinBounds(candidate, other))
@@ -75,6 +78,20 @@
7578
return false;
7679
return candidateArea > otherArea;
7780
});
81+
if (hasSingleDominatingContainedCandidate) {
82+
return false;
83+
}
84+
const containedCandidates = candidates.filter((other, otherIndex) => {
85+
if (candidateIndex === otherIndex)
86+
return false;
87+
if (!isWithinBounds(candidate, other))
88+
return false;
89+
return getBoundsArea(other) < candidateArea;
90+
});
91+
if (containedCandidates.length >= 2 && getCombinedCandidateArea(containedCandidates) >= candidateArea * 0.6) {
92+
return false;
93+
}
94+
return true;
7895
});
7996
}
8097
function detectTableCandidates(sheet, buildCellMap, scoreWeights = DEFAULT_TABLE_SCORE_WEIGHTS) {
@@ -179,7 +196,9 @@
179196
const containingBorderCandidates = candidates.filter((candidate) => isWithinBounds(candidate, bounds));
180197
const fallbackArea = getBoundsArea(bounds);
181198
const shadowedByBorderCandidate = containingBorderCandidates.some((candidate) => (getBoundsArea(candidate) >= fallbackArea * 0.4));
182-
if (shadowedByBorderCandidate) {
199+
const shadowedByMultipleBorderCandidates = containingBorderCandidates.length >= 2
200+
&& getCombinedCandidateArea(containingBorderCandidates) >= fallbackArea * 0.6;
201+
if (shadowedByBorderCandidate || shadowedByMultipleBorderCandidates) {
183202
continue;
184203
}
185204
maybePushCandidate(component);

src/xlsx2md/ts/table-detector.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,34 @@
139139
return Math.max(1, (bounds.endRow - bounds.startRow + 1) * (bounds.endCol - bounds.startCol + 1));
140140
}
141141

142+
function getCombinedCandidateArea(
143+
candidates: Array<{ startRow: number; startCol: number; endRow: number; endCol: number }>
144+
): number {
145+
return candidates.reduce((sum, candidate) => sum + getBoundsArea(candidate), 0);
146+
}
147+
142148
function pruneRedundantCandidates(candidates: TableCandidate[]): TableCandidate[] {
143149
return candidates.filter((candidate, candidateIndex) => {
144150
const candidateArea = getBoundsArea(candidate);
145-
return !candidates.some((other, otherIndex) => {
151+
const hasSingleDominatingContainedCandidate = candidates.some((other, otherIndex) => {
146152
if (candidateIndex === otherIndex) return false;
147153
if (!isWithinBounds(candidate, other)) return false;
148154
const otherArea = getBoundsArea(other);
149155
if (otherArea < candidateArea * 0.4) return false;
150156
return candidateArea > otherArea;
151157
});
158+
if (hasSingleDominatingContainedCandidate) {
159+
return false;
160+
}
161+
const containedCandidates = candidates.filter((other, otherIndex) => {
162+
if (candidateIndex === otherIndex) return false;
163+
if (!isWithinBounds(candidate, other)) return false;
164+
return getBoundsArea(other) < candidateArea;
165+
});
166+
if (containedCandidates.length >= 2 && getCombinedCandidateArea(containedCandidates) >= candidateArea * 0.6) {
167+
return false;
168+
}
169+
return true;
152170
});
153171
}
154172

@@ -269,7 +287,9 @@
269287
const shadowedByBorderCandidate = containingBorderCandidates.some((candidate) => (
270288
getBoundsArea(candidate) >= fallbackArea * 0.4
271289
));
272-
if (shadowedByBorderCandidate) {
290+
const shadowedByMultipleBorderCandidates = containingBorderCandidates.length >= 2
291+
&& getCombinedCandidateArea(containingBorderCandidates) >= fallbackArea * 0.6;
292+
if (shadowedByBorderCandidate || shadowedByMultipleBorderCandidates) {
273293
continue;
274294
}
275295
maybePushCandidate(component);

tests/fixtures/README.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,14 @@
1919
| `xlsx2md-basic-sample01.xlsx` | 総合サンプル | `xlsx2md-spec.md` 6, 7, 10, 13 | 表と地の文の崩れ、基本 Markdown 差分 |
2020
| `display/display-format-sample01.xlsx` | 表示形式 | `xlsx2md-spec.md` 12 | `display / raw / both` の見え方差分 |
2121
| `merge/merge-pattern-sample01.xlsx` | 結合セル | `xlsx2md-spec.md` 13 | `[MERGED←] / [MERGED↑]` の崩れ |
22+
| `table/table-basic-sample01.xlsx` | 隣接表(縦) | `xlsx2md-spec.md` 7, 8 | 縦に密接した独立表の誤結合 |
23+
| `table/table-basic-sample02.xlsx` | 隣接表(横) | `xlsx2md-spec.md` 7, 8 | 横に密接した独立表の誤結合 |
24+
| `table/table-basic-sample03.xlsx` | 隣接表(縦横) | `xlsx2md-spec.md` 7, 8 | 4表密集時の過剰な一体検出 |
25+
| `table/table-basic-sample11.xlsx` | 方眼紙表(単体) | `xlsx2md-spec.md` 7, 8, 13 | merge 多用の方眼紙風表の取りこぼし |
26+
| `table/table-basic-sample12.xlsx` | 方眼紙表(縦) | `xlsx2md-spec.md` 7, 8, 13 | merge 多用の方眼紙風 2 表の誤結合 |
27+
| `table/table-basic-sample13.xlsx` | 方眼紙表(縦横) | `xlsx2md-spec.md` 7, 8, 13 | merge 多用の方眼紙風 4 表の誤結合 |
28+
| `table/table-basic-sample14.xlsx` | 方眼紙表(結合漏れ) | `xlsx2md-spec.md` 7, 8, 13 | merge 多用表で一部だけ結合漏れがある場合の崩れ |
29+
| `table/table-basic-sample15.xlsx` | 方眼紙表(縦結合混在) | `xlsx2md-spec.md` 7, 8, 13 | merge 多用表で縦結合が混じる場合の崩れ |
2230

2331
### ルート直下
2432

@@ -44,6 +52,49 @@
4452
- 対応章: `xlsx2md-spec.md` 13
4553
- 主に確認する症状: `[MERGED←] / [MERGED↑]` の崩れ
4654

55+
### `table/`
56+
57+
- `table-basic-sample01.xlsx`
58+
- 独立した表が縦に密接しているケース
59+
- 見出し行や注記行を挟まずに上下へ並ぶ 2 表を確認する
60+
- 対応章: `xlsx2md-spec.md` 7, 8
61+
- 主に確認する症状: 縦に密接した独立表の誤結合
62+
- `table-basic-sample02.xlsx`
63+
- 独立した表が横に密接しているケース
64+
- 表の間に補助列の文字セルがあっても別表として扱えるか確認する
65+
- 対応章: `xlsx2md-spec.md` 7, 8
66+
- 主に確認する症状: 横に密接した独立表の誤結合、補助列の narrative 混入
67+
- `table-basic-sample03.xlsx`
68+
- 独立した表が縦横に密接して 4 表並ぶケース
69+
- 2x2 配置の全体を 1 つの大きな表として誤検出しないか確認する
70+
- 対応章: `xlsx2md-spec.md` 7, 8
71+
- 主に確認する症状: 4表密集時の過剰な一体検出
72+
- `table-basic-sample11.xlsx`
73+
- 方眼紙風に merge を多用した単表ケース
74+
- 見た目上は広い方眼紙でも 1 つの表として抽出できるか確認する
75+
- 対応章: `xlsx2md-spec.md` 7, 8, 13
76+
- 主に確認する症状: merge 多用の方眼紙風表の取りこぼし
77+
- `table-basic-sample12.xlsx`
78+
- 方眼紙風に merge を多用した表が縦に 2 つ並ぶケース
79+
- 説明セルを挟んでも上下の表を別表として扱えるか確認する
80+
- 対応章: `xlsx2md-spec.md` 7, 8, 13
81+
- 主に確認する症状: merge 多用の方眼紙風 2 表の誤結合
82+
- `table-basic-sample13.xlsx`
83+
- 方眼紙風に merge を多用した表が縦横に 4 つ並ぶケース
84+
- 2x2 配置でも各表を独立して検出できるか確認する
85+
- 対応章: `xlsx2md-spec.md` 7, 8, 13
86+
- 主に確認する症状: merge 多用の方眼紙風 4 表の誤結合
87+
- `table-basic-sample14.xlsx`
88+
- 方眼紙風に merge を多用した単表で、一部に結合漏れセルがあるケース
89+
- 多少の merge 崩れがあっても表全体を 1 表として扱えるか確認する
90+
- 対応章: `xlsx2md-spec.md` 7, 8, 13
91+
- 主に確認する症状: merge 多用表で一部だけ結合漏れがある場合の崩れ
92+
- `table-basic-sample15.xlsx`
93+
- 方眼紙風に merge を多用した単表で、備考列に縦結合が混ざるケース
94+
- `MERGED↑` を含む表でも Markdown 表として壊れないか確認する
95+
- 対応章: `xlsx2md-spec.md` 7, 8, 13
96+
- 主に確認する症状: merge 多用表で縦結合が混じる場合の崩れ
97+
4798
## 作成予定 fixture
4899

49100
### `formula/formula-basic-sample01.xlsx`

tests/fixtures/table/.gitkeep

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
128 KB
Loading
10.3 KB
Binary file not shown.
105 KB
Loading
10.3 KB
Binary file not shown.
180 KB
Loading

0 commit comments

Comments
 (0)