docs: def _preformat( text, layers, markup_settings = None ):
     ''' Formats given text, adding a special ( ANSI-terminal compatible ) markup
         to the annotations of given layers, and returns formatted text as a
         string.
         *) layers is a list containing names of the layers to be preformatted in 
            the text (these layers must be present in Text);
         *) markup_settings should be a list containing annotation options for each
            layer: one dict with options per layer;
            One dict can contain the following visualization options:
            * 'bracket' : True -- annotations will be surrounded with brackets; This 
                                  works in any terminal;
            * 'underline' : True -- annotations will be underlined; This works in ANSI 
                                    compatible terminal;
            * 'color' : ('red', 'green', 'blue' etc. ) -- annotated text will be 
                        displayed in given color; this works in ANSI compatible 
                        terminal;
         *) Alternatively, if markup_settings is undefined, up to 12 layers can be 
            visualized following the default settings;
          
         Parameters
         ----------
         text: Text
            a text object. Must contain given layers;
         layer: list of str
            list of layer names to be visualised;
         markup_settings: list of dict
            list of dictionaries containing user-defined visualization options;
            (one dict per layer)
          
         Returns
         -------
         text: str
            preformatted text, where elements of given layers have been marked up, using 
            an ANSI-terminal compatible markup;
     '''
     if markup_settings and len(layers) != len(markup_settings):
         raise Exception(' Input arguments layers and markup_settings should be equal size lists.')
     elif not markup_settings and len(layers) <= len(default_markup_settings):
         # Use default markup settings
         markup_settings = default_markup_settings[0:len(layers)]
     elif not markup_settings:
         raise Exception(' Input argument markup_settings not defined.')
     #
     # 1) Construct the index of annotations (for each layer); 
     #    Annotations are indexed by their start positions;
     #    The index also contains start and end tags of each annotation;
     spansStartingFrom = {}
     for i in range( len(layers) ):
         layer    = layers[i]
         settings = markup_settings[i]
         spansStartingFrom = _construct_start_index(text, layer, settings, spansStartingFrom)
     #
     # 2) Fix overlapping graphic annotations in the index
     #    (to ensure continuous formatting of annotations)
     _fix_overlapping_graphics( spansStartingFrom )
     #
     # 3) Index the annotations by their end positions
     endTags = _construct_end_index( spansStartingFrom )
     
     #
     # 4) Construct the output string
     return_str = []
     for i in range( len(text[TEXT]) ):
         c = text[TEXT][i]
         emptyTags = []
         if i in endTags:
            for tag in endTags[i]:
                if tag[1] != i:
                   # Non-empty tag
                   return_str.append( tag[0] )
                else:
                   # Empty tag
                   emptyTags.append( tag )
         if i in spansStartingFrom:
            for span in spansStartingFrom[i]:
                return_str.append( span[3] )
                if span[0] == span[1]:
                   # Empty tag: Add the closing tag
                   for emptyEndTag in emptyTags:
                       if span[2] == emptyEndTag[2]:
                          return_str.append( emptyEndTag[0] )
         return_str.append( c )
     if len(text[TEXT]) in spansStartingFrom:
         for span in spansStartingFrom[len(text[TEXT])]:
             return_str.append( span[3] )
     if len(text[TEXT]) in endTags:
         for tag in endTags[len(text[TEXT])]:
             return_str.append( tag[0] )
     # Hack: fix for a potential overflow / unclosed graphics
     if return_str and '\033' in return_str[-1] and \
        not return_str[-1].endswith('\033[0m'):
        return_str.append( '\033[0m' )
     return ''.join(return_str)