def convert_attrs(str, attrs)
      # first do matching ones
      tags = MATCHING_WORD_PAIRS.keys.join("")
      re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
#      re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
      1 while str.gsub!(Regexp.new(re)) {
        attr = MATCHING_WORD_PAIRS[$2];
        attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
        $1 + NULL*$2.length + $3 + NULL*$2.length + $4
      }

      # then non-matching
      unless WORD_PAIR_MAP.empty?
        WORD_PAIR_MAP.each do |regexp, attr|
          str.gsub!(regexp) { 
            attrs.set_attrs($`.length + $1.length, $2.length, attr)
            NULL*$1.length + $2 + NULL*$3.length
          }
        end
      end
    end
def convert_attrs(str, attrs)
      # first do matching ones
      tags = MATCHING_WORD_PAIRS.keys.join("")
      re = "(^|\\W)([#{tags}])([A-Za-z_]+?)\\2(\\W|\$)"
#      re = "(^|\\W)([#{tags}])(\\S+?)\\2(\\W|\$)"
      1 while str.gsub!(Regexp.new(re)) {
        attr = MATCHING_WORD_PAIRS[$2];
        attrs.set_attrs($`.length + $1.length + $2.length, $3.length, attr)
        $1 + NULL*$2.length + $3 + NULL*$2.length + $4
      }

      # then non-matching
      unless WORD_PAIR_MAP.empty?
        WORD_PAIR_MAP.each do |regexp, attr|
          str.gsub!(regexp) { 
            attrs.set_attrs($`.length + $1.length, $2.length, attr)
            NULL*$1.length + $2 + NULL*$3.length
          }
        end
      end
    end
Map attributes like <b>text</b>to the sequence \001\002<char>\001\003<char>,
 where <char> is a per-attribute specific character