'Parse a form for rectangular formfields identified by formchar
enclosing an identifier.'

def _parse_rectangles(self, cellchar, tablechar, form, **kwargs):

    custom_options = copy.copy(self.options)
    custom_options.update(kwargs)
    nform = len(form)
    mapping = {}
    cell_coords = {}
    table_coords = {}
    re_cellchar = re.compile(('%s+([^%s%s]+)%s+' % (cellchar, INVALID_FORMCHARS, cellchar, cellchar)))
    re_tablechar = re.compile(('%s+([^%s%s|+])%s+' % (tablechar, INVALID_FORMCHARS, tablechar, tablechar)))
    for (iy, line) in enumerate(_to_ansi(form, regexable=True)):
        ix0 = 0
        while True:
            match = re_cellchar.search(line, ix0)
            if match:
                cell_coords[match.group(1)] = [iy, match.start(), match.end()]
                ix0 = match.end()
            else:
                break
        ix0 = 0
        while True:
            match = re_tablechar.search(line, ix0)
            if match:
                table_coords[match.group(1)] = [iy, match.start(), match.end()]
                ix0 = match.end()
            else:
                break
    for (key, (iy, leftix, rightix)) in cell_coords.items():
        dy_up = 0
        if (iy > 0):
            for i in range(1, iy):
                if all(((form[(iy - i)][ix] == cellchar) for ix in range(leftix, rightix))):
                    dy_up += 1
                else:
                    break
        dy_down = 0
        if (iy < (nform - 1)):
            for i in range(1, ((nform - iy) - 1)):
                if all(((form[(iy + i)][ix] == cellchar) for ix in range(leftix, rightix))):
                    dy_down += 1
                else:
                    break
        iyup = (iy - dy_up)
        iydown = (iy + dy_down)
        width = (rightix - leftix)
        height = (abs((iyup - iydown)) + 1)
        data = self.cells_mapping.get(key, '')
        options = {'pad_left': 0, 'pad_right': 0, 'pad_top': 0, 'pad_bottom': 0, 'align': 'l', 'valign': 't', 'enforce_size': True}
        options.update(custom_options)
        mapping[key] = (iyup, leftix, width, height, EvCell(data, width=width, height=height, **options))
    for (key, (iy, leftix, rightix)) in table_coords.items():
        dy_up = 0
        if (iy > 0):
            for i in range(1, iy):
                if all(((form[(iy - i)][ix] == tablechar) for ix in range(leftix, rightix))):
                    dy_up += 1
                else:
                    break
        dy_down = 0
        if (iy < (nform - 1)):
            for i in range(1, ((nform - iy) - 1)):
                if all(((form[(iy + i)][ix] == tablechar) for ix in range(leftix, rightix))):
                    dy_down += 1
                else:
                    break
        iyup = (iy - dy_up)
        iydown = (iy + dy_down)
        width = (rightix - leftix)
        height = (abs((iyup - iydown)) + 1)
        table = self.tables_mapping.get(key, None)
        options = {'pad_left': 0, 'pad_right': 0, 'pad_top': 0, 'pad_bottom': 0, 'align': 'l', 'valign': 't', 'enforce_size': True}
        options.update(custom_options)
        if table:
            table.reformat(width=width, height=height, **options)
        else:
            table = EvTable(width=width, height=height, **options)
        mapping[key] = (iyup, leftix, width, height, table)
    return mapping
