mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-07 20:10:14 +08:00
91 lines
2.9 KiB
Go
91 lines
2.9 KiB
Go
package table
|
|
|
|
import (
|
|
"strings"
|
|
|
|
pdf "ragflow/internal/deepdoc/parser/pdf/type"
|
|
)
|
|
|
|
// calSpans computes colspan and rowspan for spanning cells in the grid.
|
|
// Returns spanInfo (row,col → colspan,rowspan) and covered (cells hidden by spans).
|
|
// Matches Python's __cal_spans (table_structure_recognizer.py:535).
|
|
func CalSpans(rows [][]pdf.TSRCell) (map[[2]int][2]int, map[[2]int]bool) {
|
|
spanInfo := make(map[[2]int][2]int)
|
|
covered := make(map[[2]int]bool)
|
|
if len(rows) == 0 || len(rows[0]) == 0 { return spanInfo, covered }
|
|
|
|
// Compute column center positions.
|
|
nCols := len(rows[0])
|
|
colLeft := make([]float64, nCols)
|
|
colRight := make([]float64, nCols)
|
|
for j := 0; j < nCols; j++ {
|
|
colLeft[j] = 1e9
|
|
colRight[j] = -1e9
|
|
}
|
|
nRows := len(rows)
|
|
rowTop := make([]float64, nRows)
|
|
rowBott := make([]float64, nRows)
|
|
for i := 0; i < nRows; i++ {
|
|
rowTop[i] = 1e9
|
|
rowBott[i] = -1e9
|
|
}
|
|
|
|
for i, row := range rows {
|
|
for j, cell := range row {
|
|
if j >= nCols { continue }
|
|
// Exclude spanning cells from column/row boundary calculations.
|
|
// Use label-based detection (O(1), no dependency on column midpoints).
|
|
if strings.Contains(cell.Label, "spanning") { continue }
|
|
if cell.X0 < colLeft[j] { colLeft[j] = cell.X0 }
|
|
if cell.X1 > colRight[j] { colRight[j] = cell.X1 }
|
|
if cell.Y0 < rowTop[i] { rowTop[i] = cell.Y0 }
|
|
if cell.Y1 > rowBott[i] { rowBott[i] = cell.Y1 }
|
|
}
|
|
}
|
|
|
|
// For each spanning cell, compute how many cols/rows it covers.
|
|
for i, row := range rows {
|
|
for j, cell := range row {
|
|
if j >= nCols || covered[[2]int{i,j}] { continue }
|
|
// Skip cells without position data (they can't span).
|
|
if cell.X0 == 0 && cell.X1 == 0 && cell.Y0 == 0 && cell.Y1 == 0 { continue }
|
|
cs, rs := 1, 1
|
|
// Count columns whose center is inside this cell's X range.
|
|
for k := j+1; k < nCols; k++ {
|
|
// Skip columns with no non-spanning cells (initial values unchanged).
|
|
if colLeft[k] == 1e9 && colRight[k] == -1e9 { continue }
|
|
colCenter := (colLeft[k] + colRight[k]) / 2
|
|
if colCenter >= cell.X0 && colCenter <= cell.X1 { cs++ }
|
|
}
|
|
// Count rows whose center is inside this cell's Y range.
|
|
for k := i+1; k < nRows; k++ {
|
|
// Skip rows with no non-spanning cells.
|
|
if rowTop[k] == 1e9 && rowBott[k] == -1e9 { continue }
|
|
rowCenter := (rowTop[k] + rowBott[k]) / 2
|
|
if rowCenter >= cell.Y0 && rowCenter <= cell.Y1 { rs++ }
|
|
}
|
|
if cs > 1 || rs > 1 {
|
|
spanInfo[[2]int{i,j}] = [2]int{cs, rs}
|
|
// Mark covered cells.
|
|
for ri := i; ri < i+rs && ri < nRows; ri++ {
|
|
for cj := j; cj < j+cs && cj < nCols; cj++ {
|
|
if ri != i || cj != j {
|
|
covered[[2]int{ri, cj}] = true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return spanInfo, covered
|
|
}
|
|
|
|
// flattenGrid flattens a 2D grid into a 1D slice for fillCellTextFromBoxes.
|
|
func FlattenGrid(grid [][]pdf.TSRCell) []pdf.TSRCell {
|
|
n := 0
|
|
for _, row := range grid { n += len(row) }
|
|
flat := make([]pdf.TSRCell, 0, n)
|
|
for _, row := range grid { flat = append(flat, row...) }
|
|
return flat
|
|
}
|