'Balance the table. This means to make sure
all cells on the same row have the same height,
that all columns have the same number of rows
and that the table fits within the given width.'

def _balance(self):

    self.worktable = deepcopy(self.table)
    options = copy(self.options)
    ncols = len(self.worktable)
    nrows = [len(col) for col in self.worktable]
    nrowmax = (max(nrows) if nrows else 0)
    for (icol, nrow) in enumerate(nrows):
        self.worktable[icol].reformat(**options)
        if (nrow < nrowmax):
            empty_rows = ['' for _ in range((nrowmax - nrow))]
            self.worktable[icol].add_rows(*empty_rows)
    self.ncols = ncols
    self.nrows = nrowmax
    self._borders()
    cwidths = [max((cell.get_width() for cell in col)) for col in self.worktable]
    if (self.width or (self.maxwidth and (self.maxwidth < sum(cwidths)))):
        width = (self.width if self.width else self.maxwidth)
        if ncols:
            cwidths_min = [max((cell.get_min_width() for cell in col)) for col in self.worktable]
            cwmin = sum(cwidths_min)
            if (cwmin > width):
                raise Exception(('Cannot    shrink    table    width    to    %s.    Minimum    size    is    %s.' % (self.width, cwmin)))
            excess = (width - cwmin)
            if self.evenwidth:
                for _ in range(excess):
                    ci = cwidths_min.index(min(cwidths_min))
                    cwidths_min[ci] += 1
                cwidths = cwidths_min
            else:
                for _ in range(excess):
                    ci = cwidths.index(max(cwidths))
                    cwidths_min[ci] += 1
                    cwidths[ci] -= 3
                cwidths = cwidths_min
    for (ix, col) in enumerate(self.worktable):
        try:
            col.reformat(width=cwidths[ix], **options)
        except Exception:
            raise
    cheights = [max((cell.get_height() for cell in (col[iy] for col in self.worktable))) for iy in range(nrowmax)]
    if self.height:
        if nrowmax:
            cheights_min = [max((cell.get_min_height() for cell in (col[iy] for col in self.worktable))) for iy in range(nrowmax)]
            chmin = sum(cheights_min)
            if (chmin > self.height):
                raise Exception(('Cannot    shrink    table    height    to    %s.    Minimum    size    is    %s.' % (self.height, chmin)))
            excess = (self.height - chmin)
            even = ((self.height % 2) == 0)
            for position in range(excess):
                if ((0 <= position < nrowmax) and (nrowmax > 1)):
                    ci = (cheights[1:].index(max(cheights[1:])) + 1)
                else:
                    ci = cheights.index(max(cheights))
                cheights_min[ci] += 1
                if ((ci == 0) and self.header):
                    cheights[ci] -= (2 if even else 3)
                cheights[ci] -= (2 if even else 1)
            cheights = cheights_min
        options['enforce_size'] = True
    for (ix, col) in enumerate(self.worktable):
        for (iy, cell) in enumerate(col):
            try:
                col.reformat_cell(iy, height=cheights[iy], **options)
            except Exception as e:
                msg = ('ix=%s,    iy=%s,    height=%s:    %s' % (ix, iy, cheights[iy], e.message))
                raise Exception(('Error    in    vertical    align:\n    %s' % msg))
    self.cwidth = sum(cwidths)
    self.cheight = sum(cheights)
