drawing_tools.table package#

Submodules#

drawing_tools.table.candidate_preparation module#

Candidate preparation for table detection (Phase 1): which edges/texts to propose.

The TableCandidatePreparer turns a drawing view into candidate sets of edges paired with the view’s text entities, ready for table generation. It collects edge sources (CompositeEntities, background-view geometries with the grid reference excluded), extracts the H/V line segments and peels densely-connected clusters with the iterative connectivity filter.

class drawing_tools.table.candidate_preparation.CandidatePreparationConfig(line_segment_tolerance: float = 0.5, connectivity_filter: ConnectivityFilterConfig = <factory>, max_filter_passes: int = 5)#

Bases: object

Configuration for candidate edge preparation (Phase 1).

line_segment_tolerance: float = 0.5#
connectivity_filter: ConnectivityFilterConfig#
max_filter_passes: int = 5#
__init__(line_segment_tolerance: float = 0.5, connectivity_filter: ConnectivityFilterConfig = <factory>, max_filter_passes: int = 5) → None#
class drawing_tools.table.candidate_preparation.TableCandidatePreparer(sheet_width: float, sheet_height: float, grid_reference_extractor: ReferenceFrameExtractor | None, config: CandidatePreparationConfig | None = None)#

Bases: object

Prepares candidate edge/text sets for table detection in a view (Phase 1).

Owns the ConnectivityDensityFilter and the grid-reference exclusion for the background view. The entry point is create_view_candidate_sets(); each candidate set it returns goes through the same pipeline: grid reference exclusion followed by iterative connectivity density filtering.

__init__(sheet_width: float, sheet_height: float, grid_reference_extractor: ReferenceFrameExtractor | None, config: CandidatePreparationConfig | None = None)#

Initialize the preparer.

Parameters:
  • sheet_width – Width of the sheet, for the connectivity filter thresholds.

  • sheet_height – Height of the sheet, for the connectivity filter thresholds.

  • grid_reference_extractor – The sheet’s reference frame extractor, used to exclude the grid reference geometries from background-view candidates. May only be None when no view is ever processed with is_background=True.

  • config – Configuration for candidate edge preparation.

create_view_candidate_sets(view: View, is_background: bool = False, _view_data: ViewDetectionData | None = None) → list[tuple[list[Edge], list[Entity], str]]#

Create candidate sets of edges and annotations for table detection in a view.

For background views, the grid reference is identified via the sheet extractor’s reference_frame_data.grid_reference_geometries and excluded from both direct geometries and composite entity geometries.

Every candidate set is paired with the leaf Text entities of the whole view, collected only once the view is known to produce candidates: unpacking every annotation tree is not free, and most views yield no candidate at all.

Each candidate set goes through the same pipeline: grid reference exclusion followed by connectivity density filtering.

Parameters:
  • view – The view to process

  • is_background – True if this is the background view; requires a non-None grid_reference_extractor

  • _view_data – Optional data container for storing intermediate results

Returns:

Filtered candidate sets.

drawing_tools.table.core module#

Table detection algorithm for detect tables in technical drawings.

This module provides methods to detect and extract table structures from technical drawings.

class drawing_tools.table.core.TableLine(edge: Edge | None = None, start: Point2D | None = None, end: Point2D | None = None, tolerance: float = 0.5, span_ranges: list[tuple[float, float]] | None = None, name: str = '')#

Bases: DessiaObject

Represents a line segment for table detection, either from an Edge or virtual.

This class wraps line segments that are used in table structure detection, providing convenient properties for analyzing table grid patterns. Lines can be either extracted from Edge objects or created as virtual lines to complete table boundaries.

__init__(edge: Edge | None = None, start: Point2D | None = None, end: Point2D | None = None, tolerance: float = 0.5, span_ranges: list[tuple[float, float]] | None = None, name: str = '')#

Initialize either from an Edge or from explicit coordinates.

Parameters:
  • edge – Optional Edge object to extract line from

  • start – Start point (required if edge is None)

  • end – End point (required if edge is None)

  • tolerance – Tolerance for line classification (vertical/horizontal detection)

  • span_ranges – Optional custom span ranges (for merged lines with gaps)

Raises:

ValueError – If edge is invalid or if start/end are missing when edge is None

connections: set[TableLine]#
connection_count: int#
add_connection(other: TableLine) → None#

Add a connection to another line.

classmethod from_edge(edge: Edge, tolerance: float = 0.001) → TableLine#

Create TableLine from an Edge object.

Parameters:
  • edge – Edge containing a line segment

  • tolerance – Tolerance for line classification

Returns:

TableLine instance

classmethod virtual(start: Point2D, end: Point2D, tolerance: float = 0.001) → TableLine#

Create a virtual TableLine without an associated Edge.

Virtual lines are used to complete table boundaries when detecting table structures that may have incomplete borders.

Parameters:
  • start – Start point of the line

  • end – End point of the line

  • tolerance – Tolerance for line classification

Returns:

TableLine instance marked as virtual

property start: Point2D#

Get the start point of the line segment.

property end: Point2D#

Get the end point of the line segment.

property is_vertical: bool#

Check if the line is vertical within tolerance.

property is_horizontal: bool#

Check if the line is horizontal within tolerance.

property line_coordinate: float#

Get the coordinate value where this line is positioned.

For vertical lines, returns the x-coordinate. For horizontal lines, returns the y-coordinate.

Returns:

The average coordinate along the line’s perpendicular axis

property length: float#

Get the length of the line segment.

property is_virtual: bool#

Check if this is a virtual line (no associated Edge).

Virtual lines are created to complete table boundaries and don’t correspond to actual geometry in the drawing.

Returns:

True if this line has no associated Edge object

property span_ranges: list[tuple[float, float]]#

Get the ranges where this line spans (read-only).

merge_with(other: TableLine) → TableLine#

Merge this line with another parallel line.

Parameters:

other – Another TableLine that should be parallel to this one

Returns:

New TableLine representing the merged result

Raises:

ValueError – If lines are not parallel or compatible

property has_gaps: bool#

Check if this line has gaps (multiple span ranges).

property gap_positions: list[tuple[float, float]]#

Get the positions of gaps in this line.

Returns:

List of (start, end) positions where gaps exist

property total_coverage: float#

Calculate the total length covered by all spans.

property coverage_ratio: float#

Calculate the ratio of covered length to total possible length.

Useful for determining if a line is mostly continuous or has significant gaps.

is_on_range(coord: float, tolerance: float = 0.0) → bool#

Check if a coordinate falls on any span of this line.

Parameters:
  • coord – y-coordinate for vertical lines, x-coordinate for horizontal

  • tolerance – Tolerance to extend span boundaries (handles floating-point imprecision)

Returns:

True if the coordinate is within any span range

find_connected_component(processed: set[int]) → list[TableLine]#

Find a connected component using DFS on TableLine connections data structure.

class drawing_tools.table.core.TableCell(row_id: int, col_id: int, start: Point2D, end: Point2D, row_range: tuple[int, int], col_range: tuple[int, int], content: list[Entity] | None = None, name: str = '')#

Bases: DessiaObject

Represents a logical cell in a detected table with content.

TableCell represents the logical/semantic cells of a table, which may span multiple grid positions when merged. Each TableCell contains the actual content (text, annotations) and represents how the table appears to users.

Example:

If a table cell spans positions (0,0) to (0,2) in the grid: - get_table_cell_at(0, 0), get_table_cell_at(0, 1), get_table_cell_at(0, 2) all return the same TableCell - This TableCell has row_range=(0,0), col_range=(0,2) and contains all content

__init__(row_id: int, col_id: int, start: Point2D, end: Point2D, row_range: tuple[int, int], col_range: tuple[int, int], content: list[Entity] | None = None, name: str = '')#

Initialize a TableCell.

Parameters:
  • row_id – Row index of the first grid position this cell occupies

  • col_id – Column index of the first grid position this cell occupies

  • start – Bottom-left corner point of the cell

  • end – Top-right corner point of the cell

  • row_range – Tuple of (start_row, end_row) indicating the cell’s row span

  • col_range – Tuple of (start_col, end_col) indicating the cell’s column span

  • content – Optional list of Entity objects (text, annotations) contained in this cell

  • name – Optional name for the cell

property is_column_merged: bool#

Check if this cell spans multiple columns.

property is_row_merged: bool#

Check if this cell spans multiple rows.

property column_span: int#

Number of columns this cell spans.

property row_span: int#

Number of rows this cell spans.

property width: float#

Get the width of the cell.

property height: float#

Get the height of the cell.

property center: Point2D#

Get the center point of the cell.

property area: float#

Get the area of the cell.

property bounding_rectangle: BoundingRectangle#

Convert cell to a BoundingRectangle.

property has_content: bool#

Check if this cell has any content.

property content_count: int#

Get the number of entities in this cell.

contains_point(point: Point2D) → bool#

Check if a point is inside this cell.

contains_entity(entity: Entity) → bool#

Check if an entity belongs to this cell.

An entity belongs to a cell if its bounding rectangle center is inside the cell’s bounding rectangle.

Parameters:

entity – The entity to check

Returns:

True if the entity belongs to this cell

add_content(entity: Entity) → None#

Add an entity to this cell’s content.

Parameters:

entity – The entity to add to the cell

get_text_content(ordered: bool = True, line_tolerance: float = 2.0) → list[str]#

Extract text content from all entities in this cell.

Parameters:
  • ordered – If True, orders content in reading order (top-to-bottom, left-to-right)

  • line_tolerance – Tolerance for grouping items on the same line (in drawing units)

Returns:

List of text strings from text-searchable entities

get_ordered_content(line_tolerance: float = 2.0) → list[Entity]#

Get cell content ordered in reading order (top-to-bottom, left-to-right).

Groups entities by line (similar Y coordinate) then sorts each line left-to-right.

Parameters:

line_tolerance – Tolerance for grouping items on the same line

Returns:

List of entities ordered for reading

get_combined_text(ordered: bool = True, separator: str = ' ', line_tolerance: float = 2.0) → str#

Get all text content combined into a single string.

Parameters:
  • ordered – If True, orders content in reading order

  • separator – Separator between text elements

  • line_tolerance – Tolerance for grouping items on the same line

Returns:

Combined text string

contains_text(text: str, case_sensitive: bool = False, ordered: bool = True) → bool#

Check if this cell contains the given text.

Searches through the cell’s combined text content and returns True if the specified text is found as a substring.

Parameters:
  • text – The text to search for (e.g., “SAFETY CLASS”)

  • case_sensitive – If True, the search is case-sensitive (default: False)

  • ordered – If True, orders content in reading order before searching

Returns:

True if the cell contains the text, False otherwise

debug_content_order(line_tolerance: float = 2.0) → str#

Debug method to visualize content ordering.

Parameters:

line_tolerance – Tolerance for grouping items on the same line

Returns:

String showing the ordering of content with positions

plot_data_primitives(opacity: float = 0.4, detailed: bool = False, cell_color: Color | None = None) → list[PlotDataObject]#

Get the plot data representation of the table cell.

Parameters:
  • opacity – The opacity of the cell region.

  • detailed – If activated, add the cell information label (with the merge ranges when the cell spans several grid positions).

  • cell_color – When given, unified cell color: text content in this color, background in the same color lightened.

class drawing_tools.table.core.GridCell(row: int, col: int, start: Point2D, end: Point2D, merge_origin_position: tuple[int, int] | None = None, name: str = '')#

Bases: DessiaObject

Represents a single position in the virtual grid structure.

GridCell represents a cell at a unique position in the table’s underlying grid structure. Each grid position has its own GridCell, even if it’s part of a merged logical cell. GridCell provides geometric information but does not contain content directly.

Example:

If a TableCell spans positions (0,0) to (0,2) in the grid: - get_grid_cell_at(0, 0) returns GridCell at (0,0) with merge_origin_position=None - get_grid_cell_at(0, 1) returns GridCell at (0,1) with merge_origin_position=(0,0) - get_grid_cell_at(0, 2) returns GridCell at (0,2) with merge_origin_position=(0,0) - All three positions reference the same TableCell via get_table_cell_at()

__init__(row: int, col: int, start: Point2D, end: Point2D, merge_origin_position: tuple[int, int] | None = None, name: str = '')#

Initialize a GridCell.

Parameters:
  • row – Row index in the virtual grid (0-based)

  • col – Column index in the virtual grid (0-based)

  • start – Bottom-left corner point of this grid cell

  • end – Top-right corner point of this grid cell

  • merge_origin_position – (row, col) tuple of the first grid position if this cell is part of a merged TableCell. None if this is the first position of a logical cell.

  • name – Optional name for the grid cell

property center: Point2D#

Get the center point of the grid cell.

property width: float#

Get the width of the cell.

property height: float#

Get the height of the cell.

class drawing_tools.table.core.TableGrid(x_positions: list[float], y_positions: list[float], name: str = '')#

Bases: DessiaObject

Represents the virtual grid structure of a table.

__init__(x_positions: list[float], y_positions: list[float], name: str = '') → None#

Initialize the table grid.

Parameters:
  • x_positions – List of x-coordinates for vertical lines

  • y_positions – List of y-coordinates for horizontal lines

classmethod from_table_lines(v_lines: list[TableLine], h_lines: list[TableLine]) → TableGrid#

Create a grid from a set of horizontal and vertical lines.

property nb_rows: int#

Number of rows in the grid.

property nb_cols: int#

Number of columns in the grid.

get_cell(row_index: int, column_index: int) → GridCell | None#

Get a cell by row and column index.

plot_data_primitives(show_indices: bool = True, extra_primitives: list[PlotDataObject] | None = None) → list[PlotDataObject]#

Generate plot data primitives for the virtual grid.

Parameters:
  • extra_primitives – Optional plot data primitives to be integrated in the table plot

  • show_indices – Whether to show row/col indices

Returns:

List of plot data primitives

plot_data(show_indices: bool = True, extra_primitives: list[PlotDataObject] | None = None) → None#

Generate plot data visualization for the virtual grid.

Parameters:
  • extra_primitives – Optional plot data primitives to be integrated in the table plot

  • show_indices – Whether to show row/col indices

Returns:

List of plot data primitives

class drawing_tools.table.core.TableStructure(lines: list[TableLine], bounds: BoundingRectangle, tolerance: float = 0.001, name: str = '')#

Bases: DessiaObject

Represents the reverse-engineered structure of a table.

Contains organized lines, grid information, and methods to detect cells within the structure. Serves as the structural foundation for creating a DetectedTable.

MIN_LINES_PER_DIRECTION: ClassVar[int] = 1#
LINE_COVERAGE_THRESHOLD: ClassVar[float] = 0.95#
__init__(lines: list[TableLine], bounds: BoundingRectangle, tolerance: float = 0.001, name: str = '')#

Initialize by extracting structure from lines.

Parameters:
  • lines – Collection of TableLine objects

  • bounds – Bounding rectangle of the structure

  • tolerance – Tolerance for line processing

property grid: TableGrid#

Get the table grid (lazy initialization).

property is_valid: bool#

Check if this structure forms a valid table.

property nb_rows: int#

Get the number of rows (horizontal divisions).

property nb_cols: int#

Get the number of columns (vertical divisions).

detect_cells(min_cell_size: float = 2.0, annotations: list[Entity] | None = None) → list[TableCell]#

Detect TableCell objects by analyzing the grid structure and optionally populate with annotations.

Parameters:
  • min_cell_size – Minimum dimension for a valid cell

  • annotations – Optional list of annotations to associate with cells

Returns:

List of detected TableCell objects with their content

drawing_tools.table.detection module#

Table detection: the DetectedTable model, structure generation, and orchestration.

The pipeline’s other two phases live in their own modules: candidate_preparation (Phase 1) and validation (Phase 3). SheetTableDetector instantiates and orchestrates the three phases.

drawing_tools.table.detection.is_symbol_with_rectangle_frame(symbol_entity: Entity) → bool#

Check if a Symbol entity has a rectangular frame with a valid bounding rectangle.

Parameters:

symbol_entity – A Symbol entity to test.

Returns:

True if the symbol has a frame with entity_type == "RECTANGLE" and a valid bounding rectangle.

class drawing_tools.table.detection.TableGenerationConfig(tolerance: float = 0.5, min_table_size: float = 10.0, min_cell_size: float = 2.0, min_edges_for_table: int = 4, min_lines_in_component: int = 4, intersection_tolerance: float = 0.5, min_lines_for_extent: int = 2)#

Bases: object

Configuration for table structure detection (Phase 2).

tolerance: float = 0.5#
min_table_size: float = 10.0#
min_cell_size: float = 2.0#
min_edges_for_table: int = 4#
min_lines_in_component: int = 4#
intersection_tolerance: float = 0.5#
min_lines_for_extent: int = 2#
__init__(tolerance: float = 0.5, min_table_size: float = 10.0, min_cell_size: float = 2.0, min_edges_for_table: int = 4, min_lines_in_component: int = 4, intersection_tolerance: float = 0.5, min_lines_for_extent: int = 2) → None#
class drawing_tools.table.detection.DetectedTable(structure: TableStructure, cells: list[TableCell], source: str = '', name: str = '')#

Bases: DessiaObject

Represents a detected table with its cells.

__init__(structure: TableStructure, cells: list[TableCell], source: str = '', name: str = '')#

Initialize a DetectedTable.

Parameters:
  • structure – The table structure containing lines and grid

  • cells – List of TableCell objects that make up the table

  • source – Origin of the table (e.g. candidate set name, “annotation”)

  • name – Optional name for the table

property horizontal_lines: list[TableLine]#

The structure’s horizontal lines.

property vertical_lines: list[TableLine]#

The structure’s vertical lines.

property grid: TableGrid#

The structure’s grid.

property bounds: BoundingRectangle#

The structure’s bounding rectangle.

property bounding_rectangle: BoundingRectangle#

Get the table’s BoundingRectangle.

property area: float#

Get the table’s area from its bounding rectangle.

property nb_cells: int#

Get the number of cells in the table.

property nb_rows: int#

Get the number of rows in the grid.

property nb_cols: int#

Get the number of columns in the grid.

property total_content_count: int#

Get the total number of content items across all cells.

property non_empty_cells: list[TableCell]#

Get list of cells that have content.

get_table_cell_at(row_index: int, column_index: int) → TableCell | None#

Get the TableCell object at a specific grid position.

get_grid_cell_at(row_index: int, column_index: int) → GridCell | None#

Get the GridCell object at a specific grid position.

get_all_content() → list[Entity]#

Get all content entities from all cells.

find_table_cells_with_text(text: str, case_sensitive: bool = False) → list[TableCell]#

Find all cells containing the given text.

Searches through all cells in the table and returns all cells that contain the specified text.

Parameters:
  • text – The text to search for (e.g., “SAFETY CLASS”)

  • case_sensitive – If True, the search is case-sensitive (default: False)

Returns:

List of TableCell objects containing the text

plot_data_primitives(extra_primitives: list[PlotDataObject] | None = None, opacity: float = 0.4, detailed: bool = False) → list[PlotDataObject]#

Get the plot data representation for the detected table.

Each cell gets its own color from a rotating palette (texts in the color, background in the same color lightened), showing which text was attached to which cell.

Parameters:
  • extra_primitives – Optional plot data primitives to be integrated in the table plot

  • opacity – The opacity of the table regions.

  • detailed – If activated, add cells information texts (position and merge ranges).

plot_data(extra_primitives: list[PlotDataObject] | None = None, detailed: bool = False, opacity: float = 0.4) → None#

Visualize the final detected table with cells.

Each cell gets its own color from a rotating palette (texts in the color, background in the same color lightened), showing which text was attached to which cell.

Parameters:
  • extra_primitives – Optional plot data primitives to be integrated in the table plot.

  • detailed – If activated, add cells information texts (position and merge ranges).

  • opacity – The opacity of the table regions.

class drawing_tools.table.detection.TableGenerator(config: TableGenerationConfig | None = None)#

Bases: DessiaObject

Detects tables in technical drawings by analyzing line segments.

Integrates connection analysis and orchestrates the detection process.

__init__(config: TableGenerationConfig | None = None)#

Initialize the TableGenerator.

Parameters:

config – Configuration for table generation

structures: list[TableStructure]#
detect_tables_from_edges(edges: list[Edge], annotations: list[Entity] | None = None, source: str = '') → list[DetectedTable]#

Detect tables from a list of edge geometries and optionally populate with annotations.

Parameters:
  • edges – List of edges that may form tables

  • annotations – Optional list of annotations to associate with table cells

  • source – Source identifier to tag on each detected table (e.g. “background_geometries”)

Returns:

List of detected tables with populated content

capture_generation_data(source: str, detected_tables: list[DetectedTable]) → TableGenerationSetData#

Snapshot the current generator state into a TableGenerationSetData.

Parameters:
  • source – Source identifier for this candidate set.

  • detected_tables – Tables detected from the current candidate set.

Returns:

A data snapshot of the generation phase for this candidate set.

class drawing_tools.table.detection.SheetTableDetector(sheet: Sheet, candidate_preparation_config: CandidatePreparationConfig | None = None, table_generation_config: TableGenerationConfig | None = None, table_selection_config: TableSelectionConfig | None = None, store_detection_data: bool = False)#

Bases: DessiaObject

Detects and extracts table structures from technical drawing sheets.

__init__(sheet: Sheet, candidate_preparation_config: CandidatePreparationConfig | None = None, table_generation_config: TableGenerationConfig | None = None, table_selection_config: TableSelectionConfig | None = None, store_detection_data: bool = False)#

Initialize the detector with a sheet.

Parameters:
  • sheet – The sheet containing tables.

  • candidate_preparation_config – Configuration for candidate edge preparation (Phase 1)

  • table_generation_config – Configuration for table structure detection (Phase 2)

  • table_selection_config – Configuration for table validation and selection (Phase 3)

  • store_detection_data – If True, store all intermediate pipeline data for visualization/debugging. Access via detection_data property after detection.

property tables: dict[int, list[Table | DetectedTable]]#

All detected tables (computed once on first access).

Returns:

Dictionary mapping view index to list of detected tables.

property detection_data: TableDetectionData#

Access stored detection data. Only available when store_detection_data=True.

get_title_block_annotations() → list[Entity]#

Extract annotations that belong to the title block area.

Reads the content-side annotations of the memoized reference_frame_data. Beyond-content annotations that are not validated grid labels — e.g. the drawing-number continuation strip — are available via reference_frame_data.other_beyond_content_annotations should title-block detection ever need to consider them.

get_title_block_table() → DetectedTable | None#

Detect the table that represents the title block in the drawing.

Reuses the memoized tables so that requesting the title block after (or before) the tables does not run the detection twice.

detect_title_block() → TitleBlock | None#

Detect the title block and extract structured information.

detect_view_tables(view: View, is_background: bool = False, _view_index: int | None = None) → list[Table | DetectedTable]#

Detect tables in a specific view.

This method finds tables in the view through three independent branches: 1. tables_from_tables — existing Table annotation instances 2. tables_from_notes — Symbol entities (TypeNote) with a rectangular frame 3. tables_from_geometries — edge-based detection from view geometries

Parameters:
  • view – The view to search for tables in

  • is_background – Whether this is the background view (affects detection strategy)

  • _view_index – Optional view index for storing intermediate data

Returns:

List of detected table objects (mix of Table annotations and DetectedTable instances)

detect_tables_in_background_view() → list[Table | DetectedTable]#

Detect tables specifically in the background view.

This is a convenience method that calls detect_view_tables with the background view and appropriate settings for background view processing.

Returns:

List of detected table objects in the background view

detect_all_tables() → dict[int, list[Table | DetectedTable]]#

Detect all tables in the drawing across all views.

Every view goes through the three branches of detect_view_tables() (Table annotations, framed Symbol notes, edge-based detection).

The background view gets ONE extra candidate source: its own geometries (the sheet frame layer, where the title block and the revision block live), minus the grid reference geometries. For the other views only the geometries carried by CompositeEntity annotations are candidates — their direct geometries draw the part, not tables.

Edge-based tables are validated to ensure they have sufficient content before being included.

Returns:

Dictionary mapping view index to list of validated detected tables

get_tables_summary(include_validation_stats: bool = False) → dict[str, Any]#

Get a summary of all detected tables in the drawing.

Parameters:

include_validation_stats – If True, also include statistics about invalid tables

Returns:

Dictionary with summary statistics and details

collect_overlay_primitives() → list[PlotDataObject]#

Build overlay primitives for all detected tables and the title block.

Thin delegate: the rendering lives with the other table visualizations in collect_table_overlay_primitives().

Returns:

List of plot_data primitives.

drawing_tools.table.detection_data module#

Intermediate data storage for the table detection pipeline.

When SheetTableDetector is created with store_detection_data=True, every pipeline stage populates a TableDetectionData instance so that visualisation and debugging can happen after detection without re-running the pipeline.

class drawing_tools.table.detection_data.CandidatePreparationSetData(source: str = '', content_geometries: list[Edge] = <factory>, line_segments: list[Edge] = <factory>, filtered_edges: list[Edge] = <factory>, excluded_edges: list[Edge] = <factory>, connectivity_debug_info: dict[str, ~typing.Any] | None=None)#

Bases: object

Phase 1 intermediate data for a single candidate set.

Stores content geometries extracted from a view source (background geometries or CompositeEntity), the H/V line segments found, and the results of connectivity density filtering.

source: str = ''#
content_geometries: list[Edge]#
line_segments: list[Edge]#
filtered_edges: list[Edge]#
excluded_edges: list[Edge]#
connectivity_debug_info: dict[str, Any] | None = None#
store_filtering_results(line_segments: list[Edge], filtered: list[Edge], debug_info: dict[str, Any] | None = None) → None#

Populate filtering results from the connectivity density filter.

Parameters:
  • line_segments – H/V line segments extracted from content geometries.

  • filtered – Edges that passed the connectivity filter.

  • debug_info – Optional debug info from the filter.

__init__(source: str = '', content_geometries: list[Edge] = <factory>, line_segments: list[Edge] = <factory>, filtered_edges: list[Edge] = <factory>, excluded_edges: list[Edge] = <factory>, connectivity_debug_info: dict[str, ~typing.Any] | None=None) → None#
class drawing_tools.table.detection_data.TableGenerationSetData(source: str = '', structural_lines: list[TableLine] = <factory>, excluded_lines: list[TableLine] = <factory>, structures: list[TableStructure] = <factory>, detected_tables: list[DetectedTable] = <factory>)#

Bases: object

Phase 2 intermediate data for a single candidate set.

Stores the output of TableGenerator after running detect_tables_from_edges on one candidate set’s filtered edges.

source: str = ''#
structural_lines: list[TableLine]#
excluded_lines: list[TableLine]#
structures: list[TableStructure]#
detected_tables: list[DetectedTable]#
__init__(source: str = '', structural_lines: list[TableLine] = <factory>, excluded_lines: list[TableLine] = <factory>, structures: list[TableStructure] = <factory>, detected_tables: list[DetectedTable] = <factory>) → None#
class drawing_tools.table.detection_data.CandidatePreparationData(sets: list[CandidatePreparationSetData] = <factory>)#

Bases: object

Phase 1: Candidate preparation data across all candidate sets of a view.

sets: list[CandidatePreparationSetData]#
property all_content_geometries: list[Edge]#

All content geometries across candidate sets.

property all_line_segments: list[Edge]#

All H/V line segments across candidate sets.

property all_filtered_edges: list[Edge]#

All filtered edges across candidate sets.

property all_excluded_edges: list[Edge]#

All excluded edges across candidate sets.

__init__(sets: list[CandidatePreparationSetData] = <factory>) → None#
class drawing_tools.table.detection_data.GeneratedTablesData(sets: list[TableGenerationSetData] = <factory>)#

Bases: object

Phase 2: Table generation results for a view.

sets: list[TableGenerationSetData]#
property all_structural_lines: list[TableLine]#

All structural TableLines across candidate sets.

property all_excluded_lines: list[TableLine]#

All excluded TableLines across candidate sets.

property all_structures: list[TableStructure]#

All TableStructures across candidate sets.

__init__(sets: list[TableGenerationSetData] = <factory>) → None#
class drawing_tools.table.detection_data.ValidatedTablesData(tables: list[TableType] = <factory>)#

Bases: object

Phase 3: Validated tables for a view.

tables: list[TableType]#
__init__(tables: list[TableType] = <factory>) → None#
class drawing_tools.table.detection_data.ViewDetectionData(view_index: int = 0, is_background: bool = False, candidates: CandidatePreparationData = <factory>, generation: GeneratedTablesData = <factory>, tables_from_tables: list[TableType] = <factory>, tables_from_notes: list[DetectedTable] = <factory>, tables_from_geometries: list[DetectedTable] = <factory>, validation: ValidatedTablesData = <factory>)#

Bases: object

All intermediate data for a single view’s detection pass.

view_index: int = 0#
is_background: bool = False#
candidates: CandidatePreparationData#
generation: GeneratedTablesData#
tables_from_tables: list[TableType]#
tables_from_notes: list[DetectedTable]#
tables_from_geometries: list[DetectedTable]#
validation: ValidatedTablesData#
__init__(view_index: int = 0, is_background: bool = False, candidates: CandidatePreparationData = <factory>, generation: GeneratedTablesData = <factory>, tables_from_tables: list[TableType] = <factory>, tables_from_notes: list[DetectedTable] = <factory>, tables_from_geometries: list[DetectedTable] = <factory>, validation: ValidatedTablesData = <factory>) → None#
class drawing_tools.table.detection_data.TableSelectionData(title_block_table: DetectedTable | None = None)#

Bases: object

Cross-view selection results: title block identification.

title_block_table: DetectedTable | None = None#
__init__(title_block_table: DetectedTable | None = None) → None#
class drawing_tools.table.detection_data.TableDetectionData(views: dict[int, ~drawing_tools.table.detection_data.ViewDetectionData]=<factory>, selection: TableSelectionData = <factory>)#

Bases: object

Top-level container for all pipeline intermediate data.

Populated by SheetTableDetector when store_detection_data=True.

views: dict[int, ViewDetectionData]#
selection: TableSelectionData#
get_view_data(view_index: int) → ViewDetectionData | None#

Get detection data for any view by index.

__init__(views: dict[int, ~drawing_tools.table.detection_data.ViewDetectionData]=<factory>, selection: TableSelectionData = <factory>) → None#

drawing_tools.table.title_block module#

Title block extraction from detected tables.

class drawing_tools.table.title_block.TitleBlockExtractor(detected_table: DetectedTable, line_tolerance: float = 2.0)#

Bases: object

Extracts title block information from a DetectedTable.

Uses pattern matching and keyword search to identify fields.

MIN_TITLE_LENGTH = 5#
__init__(detected_table: DetectedTable, line_tolerance: float = 2.0)#

Initialize the extractor with a detected table.

Parameters:
  • detected_table – The table containing title block information

  • line_tolerance – Tolerance for grouping text items on the same line

extract(debug: bool = False) → TitleBlock#

Extract title block information from the table.

Parameters:

debug – If True, print debug information about content ordering

Returns:

TitleBlock instance with extracted information

drawing_tools.table.utils module#

Utility functions for table detection and text processing.

This module provides sophisticated filtering of line segments based on their spatial density and connectivity patterns, useful for isolating structured elements like tables from scattered individual lines.

class drawing_tools.table.utils.ConnectivityFilterConfig(threshold_ratio: float = 0.2, use_elliptical: bool = True, std_deviation_factor: float = 1.2, debug_visualization: bool = False, output_path: str = 'connectivity_filter_debug.html')#

Bases: object

Configuration for connectivity density filtering.

Parameters:
  • threshold_ratio – Base ratio for threshold calculation (0.2 = 20%)

  • use_elliptical – Use elliptical neighborhoods (True) or rectangular (False)

  • std_deviation_factor – Multiplier for standard deviation in threshold calculation. Higher values lower the min_neighbors threshold, making the filter more permissive. Formula: min_neighbors = max(1, int(avg - factor * std))

  • debug_visualization – Enable debug visualization output

  • output_path – Path for debug visualization file

threshold_ratio: float = 0.2#
use_elliptical: bool = True#
std_deviation_factor: float = 1.2#
debug_visualization: bool = False#
output_path: str = 'connectivity_filter_debug.html'#
__init__(threshold_ratio: float = 0.2, use_elliptical: bool = True, std_deviation_factor: float = 1.2, debug_visualization: bool = False, output_path: str = 'connectivity_filter_debug.html') → None#
class drawing_tools.table.utils.ConnectivityDensityFilter(drawing_width: float, drawing_height: float, config: ConnectivityFilterConfig | None = None)#

Bases: object

Advanced connectivity density filter for isolating connected line structures.

This filter analyzes spatial relationships between lines and removes isolated or sparsely connected lines, keeping only densely connected clusters that likely represent structured elements like table grids.

Features: - Adaptive thresholds based on drawing aspect ratio - Elliptical or rectangular neighborhood analysis - Statistical filtering using standard deviation - Comprehensive debug visualization capabilities

MIN_LINES_FOR_PROCESSING = 3#
MIN_LINES_FOR_TABLE = 4#
__init__(drawing_width: float, drawing_height: float, config: ConnectivityFilterConfig | None = None)#

Initialize the connectivity density filter.

Parameters:
  • drawing_width – Width of the drawing for threshold calculation

  • drawing_height – Height of the drawing for threshold calculation

  • config – Configuration object with filtering parameters

filter(lines: list[Edge], store_debug_info: bool = False) → list[Edge]#

Apply connectivity density filtering to the provided lines.

Parameters:
  • lines – List of line edges to filter

  • store_debug_info – If True, store debug info without generating visualization. Access via debug_info property after calling this method.

Returns:

Filtered list of lines with sufficient connectivity density

property debug_info: dict | None#

Get the debug information from the last filter operation.

get_filter_summary() → dict#

Get a summary of the last filtering operation.

Returns:

Dictionary containing filter operation summary

drawing_tools.table.utils.parse_date(date_str: str) → str | None#

Parse various date formats to ISO format.

Parameters:

date_str – Date string in various formats

Returns:

ISO formatted date (YYYY-MM-DD) or original string

drawing_tools.table.utils.extract_value_after_keyword(text: str, keyword: str) → str | None#

Extract value that appears after a keyword.

Parameters:
  • text – The text to search in (should be normalized)

  • keyword – The keyword to find (should be normalized)

Returns:

The extracted value or None

drawing_tools.table.validation module#

Table validation and selection (Phase 3): which tables to keep and why.

The TableValidator runs the selection criteria over the raw tables produced by the generation phase. TableValidator.validation_failure() is the single source of truth for the criteria, their order and their config gates: both the pass/fail verdict and the rejection reasons (logs, validation statistics) derive from it.

class drawing_tools.table.validation.TableSelectionConfig(min_content_ratio: float = 0.3, max_sheet_coverage_ratio: float = 0.8, min_nb_cells: int = 2, validate_has_cells: bool = True, validate_sheet_coverage: bool = True, validate_content_ratio: bool = True)#

Bases: object

Configuration for table validation and selection (Phase 3).

min_content_ratio: float = 0.3#
max_sheet_coverage_ratio: float = 0.8#
min_nb_cells: int = 2#
validate_has_cells: bool = True#
validate_sheet_coverage: bool = True#
validate_content_ratio: bool = True#
__init__(min_content_ratio: float = 0.3, max_sheet_coverage_ratio: float = 0.8, min_nb_cells: int = 2, validate_has_cells: bool = True, validate_sheet_coverage: bool = True, validate_content_ratio: bool = True) → None#
class drawing_tools.table.validation.TableValidator(sheet_area: float, config: TableSelectionConfig | None = None)#

Bases: object

Validates detected tables against the selection criteria of a sheet.

__init__(sheet_area: float, config: TableSelectionConfig | None = None)#

Initialize the validator.

Parameters:
  • sheet_area – Area of the sheet, for the coverage-ratio criterion.

  • config – Configuration for table validation and selection.

validation_failure(table: DetectedTable) → str | None#

Run the validation checks and name the first failing one.

Single source of truth for the criteria, their order and their config gates: both the pass/fail verdict (is_valid_table()) and the rejection reasons (logs, validation statistics) derive from it.

Parameters:

table – The DetectedTable to validate

Returns:

The failing check’s reason, or None when the table is valid.

is_valid_table(table: DetectedTable) → bool#

Validate if a detected table is actually a valid table.

Parameters:

table – The DetectedTable to validate

Returns:

True if the table is valid, False otherwise

filter_valid_tables(raw_tables: list[DetectedTable]) → list[DetectedTable]#

Silently keep the tables that pass validation (no logging: diagnostic paths).

Parameters:

raw_tables – Unvalidated tables.

Returns:

The tables that pass is_valid_table().

validate_raw_tables(raw_tables: list[DetectedTable]) → list[DetectedTable]#

Filter raw tables through validation, keeping the losses observable.

Rejection is routine (the connectivity peeling deliberately produces candidate groups that are not tables), so each rejected table is logged at DEBUG and a single aggregated WARNING per call summarizes the counts and reasons. Rejections used to be completely silent, only visible by re-running with SheetTableDetector(store_detection_data=True).

Parameters:

raw_tables – Unvalidated tables from the generation phase.

Returns:

The tables that pass is_valid_table().

Module contents#

Table extraction from dessia_drawing package.

class drawing_tools.table.SheetTableDetector(sheet: Sheet, candidate_preparation_config: CandidatePreparationConfig | None = None, table_generation_config: TableGenerationConfig | None = None, table_selection_config: TableSelectionConfig | None = None, store_detection_data: bool = False)#

Bases: DessiaObject

Detects and extracts table structures from technical drawing sheets.

__init__(sheet: Sheet, candidate_preparation_config: CandidatePreparationConfig | None = None, table_generation_config: TableGenerationConfig | None = None, table_selection_config: TableSelectionConfig | None = None, store_detection_data: bool = False)#

Initialize the detector with a sheet.

Parameters:
  • sheet – The sheet containing tables.

  • candidate_preparation_config – Configuration for candidate edge preparation (Phase 1)

  • table_generation_config – Configuration for table structure detection (Phase 2)

  • table_selection_config – Configuration for table validation and selection (Phase 3)

  • store_detection_data – If True, store all intermediate pipeline data for visualization/debugging. Access via detection_data property after detection.

property tables: dict[int, list[Table | DetectedTable]]#

All detected tables (computed once on first access).

Returns:

Dictionary mapping view index to list of detected tables.

property detection_data: TableDetectionData#

Access stored detection data. Only available when store_detection_data=True.

get_title_block_annotations() → list[Entity]#

Extract annotations that belong to the title block area.

Reads the content-side annotations of the memoized reference_frame_data. Beyond-content annotations that are not validated grid labels — e.g. the drawing-number continuation strip — are available via reference_frame_data.other_beyond_content_annotations should title-block detection ever need to consider them.

get_title_block_table() → DetectedTable | None#

Detect the table that represents the title block in the drawing.

Reuses the memoized tables so that requesting the title block after (or before) the tables does not run the detection twice.

detect_title_block() → TitleBlock | None#

Detect the title block and extract structured information.

detect_view_tables(view: View, is_background: bool = False, _view_index: int | None = None) → list[Table | DetectedTable]#

Detect tables in a specific view.

This method finds tables in the view through three independent branches: 1. tables_from_tables — existing Table annotation instances 2. tables_from_notes — Symbol entities (TypeNote) with a rectangular frame 3. tables_from_geometries — edge-based detection from view geometries

Parameters:
  • view – The view to search for tables in

  • is_background – Whether this is the background view (affects detection strategy)

  • _view_index – Optional view index for storing intermediate data

Returns:

List of detected table objects (mix of Table annotations and DetectedTable instances)

detect_tables_in_background_view() → list[Table | DetectedTable]#

Detect tables specifically in the background view.

This is a convenience method that calls detect_view_tables with the background view and appropriate settings for background view processing.

Returns:

List of detected table objects in the background view

detect_all_tables() → dict[int, list[Table | DetectedTable]]#

Detect all tables in the drawing across all views.

Every view goes through the three branches of detect_view_tables() (Table annotations, framed Symbol notes, edge-based detection).

The background view gets ONE extra candidate source: its own geometries (the sheet frame layer, where the title block and the revision block live), minus the grid reference geometries. For the other views only the geometries carried by CompositeEntity annotations are candidates — their direct geometries draw the part, not tables.

Edge-based tables are validated to ensure they have sufficient content before being included.

Returns:

Dictionary mapping view index to list of validated detected tables

get_tables_summary(include_validation_stats: bool = False) → dict[str, Any]#

Get a summary of all detected tables in the drawing.

Parameters:

include_validation_stats – If True, also include statistics about invalid tables

Returns:

Dictionary with summary statistics and details

collect_overlay_primitives() → list[PlotDataObject]#

Build overlay primitives for all detected tables and the title block.

Thin delegate: the rendering lives with the other table visualizations in collect_table_overlay_primitives().

Returns:

List of plot_data primitives.