Detect table structure within a given rectangle.
(textpage, word_rects, rect)
| 162 | |
| 163 | |
| 164 | def make_table_from_bbox(textpage, word_rects, rect): |
| 165 | """Detect table structure within a given rectangle.""" |
| 166 | cells = [] # table cells as (x0,y0,x1,y1) tuples |
| 167 | |
| 168 | # calls fz_find_table_within_bounds |
| 169 | block = get_table_dict_from_rect(textpage, rect) |
| 170 | # No table structure found if not a grid block |
| 171 | if block.get("type") != mupdf.FZ_STEXT_BLOCK_GRID: |
| 172 | return cells |
| 173 | bbox = pymupdf.Rect(block["bbox"]) # resulting table bbox |
| 174 | |
| 175 | # lists of (pos,uncertainty) tuples |
| 176 | xpos = sorted(block["xpos"], key=lambda x: x[0]) |
| 177 | ypos = sorted(block["ypos"], key=lambda y: y[0]) |
| 178 | |
| 179 | # maximum uncertainties in x and y directions |
| 180 | xmaxu, ymaxu = block["max_uncertain"] |
| 181 | |
| 182 | # Modify ypos to remove uncertain positions, and y positions |
| 183 | # that cut through words. |
| 184 | nypos = [] |
| 185 | for y, yunc in ypos: |
| 186 | if yunc > 0: # allow no uncertain y values |
| 187 | continue |
| 188 | if intersects_words_h(bbox, y, word_rects): |
| 189 | continue # allow no y that cuts through words |
| 190 | if nypos and (y - nypos[-1] < 3): |
| 191 | nypos[-1] = y # snap close positions |
| 192 | else: |
| 193 | nypos.append(y) |
| 194 | |
| 195 | # New max y uncertainty: 35% of remaining y positions. |
| 196 | # Omit x positions that intersect too many words, otherwise |
| 197 | # only remove x for the affected cells. |
| 198 | ymaxu = max(0, round((len(nypos) - 2) * 0.35)) |
| 199 | |
| 200 | # Exclude x positions with too high uncertainty |
| 201 | # (we allow more uncertainty in x direction) |
| 202 | nxpos = [x[0] for x in xpos if x[1] <= ymaxu] |
| 203 | if bbox.x1 > nxpos[-1] + 3: |
| 204 | nxpos.append(bbox.x1) # ensure right table border |
| 205 | |
| 206 | # Compose cells from the remaining x and y positions. |
| 207 | for i in range(len(nypos) - 1): |
| 208 | row_box = pymupdf.Rect(bbox.x0, nypos[i], bbox.x1, nypos[i + 1]) |
| 209 | # Sub-select words in this row and sort them by left coordinate |
| 210 | row_words = sorted( |
| 211 | [r for r in word_rects if rect_in_rect(r, row_box)], key=lambda r: r.x0 |
| 212 | ) |
| 213 | # Sub-select x values that do not cut through words |
| 214 | this_xpos = [x for x in nxpos if not any(r.x0 < x < r.x1 for r in row_words)] |
| 215 | for j in range(len(this_xpos) - 1): |
| 216 | cell = pymupdf.Rect(this_xpos[j], nypos[i], this_xpos[j + 1], nypos[i + 1]) |
| 217 | if not cell.is_empty: # valid cell |
| 218 | cells.append(tuple(cell)) |
| 219 | # Add new table to TableFinder tables |
| 220 | return cells |
| 221 |
no test coverage detected
searching dependent graphs…