A block is considered any region where we have the following structure:

        text | text or number | ... | text of number
        text | number         | ... | number
        text | number         | ... | number
        ...    .                .     .
        ...    .                .     .
        ...    .                .     .

        or:

        text           | ... | text           | text
        text or number | ... | text or number | number
        text or number | ... | text of number | number
        ...              .     .                .
        ...              .     .                .
        ...              .     .                .

        With the default cases of all text and all numbers matching to a single block encompassing
        the entire table.

def _find_blocks(self, converted_table, worksheet, flags, units,
                     block_meta=None, start_pos=None, end_pos=None):
        '''
        A block is considered any region where we have the following structure:

        text | text or number | ... | text of number
        text | number         | ... | number
        text | number         | ... | number
        ...    .                .     .
        ...    .                .     .
        ...    .                .     .

        or:

        text           | ... | text           | text
        text or number | ... | text or number | number
        text or number | ... | text of number | number
        ...              .     .                .
        ...              .     .                .
        ...              .     .                .

        With the default cases of all text and all numbers matching to a single block encompassing
        the entire table.
        '''
        blocks = []
        used_cells = []

        if start_pos == None:
            start_pos = (0, 0)
        if end_pos == None:
            end_pos = (len(converted_table), max(
                            len(row) for row in converted_table))

        # Track used cells -- these can be non-rectangular, but must be 2D
        for row in converted_table:
            used_cells.append([False]*len(row))
        # Catch an empty table or blank rows
        if not converted_table or all(not row for row in converted_table):
            blocks.append(converted_table)
            self.flag_change(flags, 'error', worksheet=worksheet, message="Empty table")
            return blocks

        # Start with a boolean to get the while loop going
        block = True
        block_search_start = start_pos
        while block:
            # Returns None if no more blocks exist
            block = self._find_valid_block(converted_table, worksheet, flags, units, used_cells,
                        block_search_start, end_pos)
            if block:
                blocks.append(block)
                # Restart on the row of the last block at the
                # beginning column
                block_search_start = (block.start[0], start_pos[1])

        return blocks