def to_string(self):
        """
        Render a DataFrame to a console-friendly tabular output.
        """
        from pandas import Series

        frame = self.frame

        if len(frame.columns) == 0 or len(frame.index) == 0:
            info_line = ('Empty {name}\nColumns: {col}\nIndex: {idx}'
                         .format(name=type(self.frame).__name__,
                                 col=pprint_thing(frame.columns),
                                 idx=pprint_thing(frame.index)))
            text = info_line
        else:

            strcols = self._to_str_columns()
            if self.line_width is None:  # no need to wrap around just print
                # the whole frame
                text = self.adj.adjoin(1, *strcols)
            elif (not isinstance(self.max_cols, int) or
                    self.max_cols > 0):  # need to wrap around
                text = self._join_multiline(*strcols)
            else:  # max_cols == 0. Try to fit frame to terminal
                text = self.adj.adjoin(1, *strcols).split('\n')
                max_len = Series(text).str.len().max()
                # plus truncate dot col
                dif = max_len - self.w
                # '+ 1' to avoid too wide repr (GH PR #17023)
                adj_dif = dif + 1
                col_lens = Series([Series(ele).apply(len).max()
                                   for ele in strcols])
                n_cols = len(col_lens)
                counter = 0
                while adj_dif > 0 and n_cols > 1:
                    counter += 1
                    mid = int(round(n_cols / 2.))
                    mid_ix = col_lens.index[mid]
                    col_len = col_lens[mid_ix]
                    # adjoin adds one
                    adj_dif -= (col_len + 1)
                    col_lens = col_lens.drop(mid_ix)
                    n_cols = len(col_lens)
                # subtract index column
                max_cols_adj = n_cols - self.index
                # GH-21180. Ensure that we print at least two.
                max_cols_adj = max(max_cols_adj, 2)
                self.max_cols_adj = max_cols_adj

                # Call again _chk_truncate to cut frame appropriately
                # and then generate string representation
                self._chk_truncate()
                strcols = self._to_str_columns()
                text = self.adj.adjoin(1, *strcols)
        self.buf.writelines(text)

        if self.should_show_dimensions:
            self.buf.write("\n\n[{nrows} rows x {ncols} columns]"
                           .format(nrows=len(frame), ncols=len(frame.columns)))