docs: def identify_and_tag_authors(line, authors_kb):
      """Given a reference, look for a group of author names,
         place tags around the author group, return the newly tagged line.
      """
      re_auth, re_auth_near_miss = get_author_regexps()
  
      # Replace authors which do not convert well from utf-8
      for pattern, repl in authors_kb:
          line = line.replace(pattern, repl)
  
      output_line = line
  
      # We matched authors here
      line = strip_tags(output_line)
      matched_authors = list(re_auth.finditer(line))
      # We try to have better results by unidecoding
      unidecoded_line = strip_tags(unidecode(output_line))
      matched_authors_unidecode = list(re_auth.finditer(unidecoded_line))
  
      if len(matched_authors_unidecode) > len(matched_authors):
          output_line = unidecode(output_line)
          matched_authors = matched_authors_unidecode
  
      # If there is at least one matched author group
      if matched_authors:
          matched_positions = []
          preceeding_text_string = line
          preceeding_text_start = 0
          for auth_no, match in enumerate(matched_authors):
              # Only if there are no underscores or closing arrows found in the matched author group
              # This must be checked for here, as it cannot be applied to the re without clashing with
              # other Unicode characters
              if line[match.start():match.end()].find("_") == -1:
                  # Has the group with name 'et' (for 'et al') been found in the pattern?
                  # Has the group with name 'es' (for ed. before the author) been found in the pattern?
                  # Has the group with name 'ee' (for ed. after the author) been
                  # found in the pattern?
                  matched_positions.append({
                      'start': match.start(),
                      'end': match.end(),
                      'etal': match.group('et') or match.group('et2'),
                      'ed_start': match.group('es'),
                      'ed_end': match.group('ee'),
                      'multi_auth': match.group('multi_auth'),
                      'multi_surs': match.group('multi_surs'),
                      'text_before': preceeding_text_string[preceeding_text_start:match.start()],
                      'auth_no': auth_no,
                      'author_names': match.group('author_names')
                  })
                  # Save the end of the match, from where to snip the misc text
                  # found before an author match
                  preceeding_text_start = match.end()
  
          # Work backwards to avoid index problems when adding AUTH tags
          matched_positions.reverse()
          for m in matched_positions:
              dump_in_misc = False
              start = m['start']
              end = m['end']
  
              # Check the text before the current match to see if it has a bad
              # 'et al'
              lower_text_before = m['text_before'].strip().lower()
              for e in etal_matches:
                  if lower_text_before.endswith(e):
                      # If so, this author match is likely to be a bad match on a
                      # missed title
                      dump_in_misc = True
                      break
  
              # An AND found here likely indicates a missed author before this text
              # Thus, triggers weaker author searching, within the previous misc text
              # (Check the text before the current match to see if it has a bad 'and')
              # A bad 'and' will only be denoted as such if there exists only one author after it
              # and the author group is legit (not to be dumped in misc)
              if not dump_in_misc and not (m['multi_auth'] or m['multi_surs']) \
                      and (lower_text_before.endswith(' and')):
                  # Search using a weaker author pattern to try and find the
                  # missed author(s) (cut away the end 'and')
                  weaker_match = re_auth_near_miss.match(m['text_before'])
                  if weaker_match and not (weaker_match.group('es') or weaker_match.group('ee')):
                      # Change the start of the author group to include this new
                      # author group
                      start = start - \
                          (len(m['text_before']) - weaker_match.start())
                  # Still no match, do not add tags for this author match.. dump
                  # it into misc
                  else:
                      dump_in_misc = True
  
              add_to_misc = ""
              # If a semi-colon was found at the end of this author group, keep it in misc
              # so that it can be looked at for splitting heurisitics
              if len(output_line) > m['end']:
                  if output_line[m['end']].strip(" ,.") == ';':
                      add_to_misc = ';'
  
              # Standardize eds. notation
              tmp_output_line = re.sub(re_ed_notation, '(ed.)',
                                       output_line[start:end], re.IGNORECASE)
              # Standardize et al. notation
              tmp_output_line = re.sub(re_etal, 'et al.',
                                       tmp_output_line, re.IGNORECASE)
              # Strip
              tmp_output_line = tmp_output_line.lstrip('.').strip(",:;- [](")
              if not tmp_output_line.endswith('(ed.)'):
                  tmp_output_line = tmp_output_line.strip(')')
  
              # ONLY wrap author data with tags IF there is no evidence that it is an
              # ed. author. (i.e. The author is not referred to as an editor)
              # Does this author group string have 'et al.'?
              if m['etal'] and not (m['ed_start'] or m['ed_end'] or dump_in_misc):
                  output_line = output_line[:start] \
                      + "<cds.AUTHetal>" \
                      + tmp_output_line \
                      + CFG_REFEXTRACT_MARKER_CLOSING_AUTHOR_ETAL \
                      + add_to_misc \
                      + output_line[end:]
              elif not (m['ed_start'] or m['ed_end'] or dump_in_misc):
                  # Insert the std (standard) tag
                  output_line = output_line[:start] \
                      + "<cds.AUTHstnd>" \
                      + tmp_output_line \
                      + CFG_REFEXTRACT_MARKER_CLOSING_AUTHOR_STND \
                      + add_to_misc \
                      + output_line[end:]
              # Apply the 'include in $h' method to author groups marked as
              # editors
              elif m['ed_start'] or m['ed_end']:
                  ed_notation = " (eds.)"
                  # Standardize et al. notation
                  tmp_output_line = re.sub(re_etal, 'et al.',
                                           m['author_names'], re.IGNORECASE)
                  # remove any characters which denote this author group
                  # to be editors, just take the
                  # author names, and append '(ed.)'
                  output_line = output_line[:start] \
                      + "<cds.AUTHincl>" \
                      + tmp_output_line.strip(",:;- [](") \
                      + ed_notation \
                      + CFG_REFEXTRACT_MARKER_CLOSING_AUTHOR_INCL \
                      + add_to_misc \
                      + output_line[end:]
  
      return output_line