class ANSI_SGR_To_HTML

ANSI SGR code to HTML converter

This gem converts text with ANSI SGR codes to HTML. SGR? Yes, fonts, bold, colors, etc. are ANSI SGR sequences, other ANSI escape codes are not supported. In other words, the only codes that actually make sense to convert are supported, as moving the cursor would not be convertible.

Overview

Attributes

_ansi_sgr_to_html_actions[RW]
_ansi_sgr_to_html_index_offsets[RW]
_ansi_sgr_to_html_key_offsets[RW]
_ansi_sgr_to_html_range_lengths[RW]
_ansi_sgr_to_html_single_lengths[RW]
_ansi_sgr_to_html_trans_actions[RW]
_ansi_sgr_to_html_trans_keys[RW]
_ansi_sgr_to_html_trans_targs[RW]
ansi_sgr_to_html_en_main[RW]
ansi_sgr_to_html_error[RW]
ansi_sgr_to_html_first_final[RW]
ansi_sgr_to_html_start[RW]

Public Class Methods

new(k_turn_eol_into_br = false, col_tab_16 = :xterm, font_styles = [nil]*10, k_1_is_bold_and_bright = true, k_21_is_double_underline = true) click to toggle source

Create new instance of converter

Example
>> ANSI_SGR_To_HTML.new(false, :osx_term, ['serif','Courier New']+[nil]*7+['Fraktur'], true, true)
Arguments
k_turn_eol_into_br

pass true if converter should turn newlines into <br/> on the fly (redundant if output is wrapped in <pre>)

col_tab_16

specify either array with 16 css colors (first 8 are low, last 8 are bright colors), or preset name (:xterm, :osx_term, :cmd, :vga)

font_styles

Array with ten font-names (or nil, to use default), to be used for SGR codes 11-20

k_1_is_bold_and_bright

true renders bold text (SGR code 1) also in bright colors (if color is from basic 16-color table)

k_21_is_double_underline

specify, whether SGR code 21 disables bold text, or enable double-underline

# File lib/ansi_sgr_to_html.rb, line 74
        def initialize(k_turn_eol_into_br = false, col_tab_16 = :xterm, font_styles = [nil]*10, k_1_is_bold_and_bright = true, k_21_is_double_underline = true)
                @k_21_is_double_underline = k_21_is_double_underline;
                @k_1_is_bold_and_bright   = k_1_is_bold_and_bright;
                @k_turn_eol_into_br       = k_turn_eol_into_br;
                col_tab_16 = @@Col_tabs_16[col_tab_16] if(col_tab_16.class == Symbol)
                @locol  = col_tab_16[0,8]
                @hicol  = col_tab_16[8,8]
                @curcol = @locol
                @font_styles = font_styles
                @html_escapes = @@HTML_escapes.clone
                @html_escapes["\r"], @html_escapes["\n"] = '', '<br/>' if(@k_turn_eol_into_br)
                @prv_css = {}
                @cur_css = {}
                @inv     = false # Inverted mode on?
                @putblnk = false # Wrote blink tag keyframes? Guard to write them only once, when needed.
                @fsm_stack = []
                @fsm_top   = 0
class << self
        attr_accessor :_ansi_sgr_to_html_actions
        private :_ansi_sgr_to_html_actions, :_ansi_sgr_to_html_actions=
end
self._ansi_sgr_to_html_actions = [
        0, 1, 0, 1, 1, 1, 2, 1, 
        3, 1, 4, 1, 5, 1, 6, 1, 
        7, 1, 8, 1, 9, 1, 10, 1, 
        11, 1, 12, 1, 13, 1, 14, 1, 
        15, 1, 16, 1, 17, 1, 18, 1, 
        19, 1, 20, 1, 21, 1, 22, 1, 
        23, 1, 24, 1, 25, 1, 26, 1, 
        27, 1, 28, 1, 29, 1, 30, 1, 
        31, 1, 32, 2, 2, 1, 2, 4, 
        1, 2, 6, 1, 2, 8, 1, 2, 
        10, 1, 2, 11, 1, 2, 13, 1, 
        2, 15, 1, 2, 17, 1, 2, 29, 
        1
]
class << self
        attr_accessor :_ansi_sgr_to_html_key_offsets
        private :_ansi_sgr_to_html_key_offsets, :_ansi_sgr_to_html_key_offsets=
end
self._ansi_sgr_to_html_key_offsets = [
        0, 0, 7, 8, 20, 22, 26, 37, 
        49, 55, 56, 58, 59, 63, 66, 67, 
        71, 74, 75, 79, 83, 87, 94, 102, 
        110, 121, 127, 128, 130, 131, 135, 138, 
        144, 147, 148, 152, 156, 167, 178, 181, 
        187, 190, 191, 195
]
class << self
        attr_accessor :_ansi_sgr_to_html_trans_keys
        private :_ansi_sgr_to_html_trans_keys, :_ansi_sgr_to_html_trans_keys=
end
self._ansi_sgr_to_html_trans_keys = [
        10, 13, 27, 34, 38, 60, 62, 91, 
        48, 49, 50, 51, 52, 53, 54, 55, 
        56, 57, 59, 109, 59, 109, 59, 109, 
        48, 57, 49, 50, 51, 52, 53, 54, 
        55, 56, 57, 59, 109, 48, 49, 50, 
        51, 52, 53, 54, 55, 56, 57, 59, 
        109, 56, 57, 59, 109, 48, 55, 59, 
        50, 53, 59, 49, 50, 48, 57, 59, 
        48, 57, 59, 49, 50, 48, 57, 59, 
        48, 57, 59, 49, 50, 48, 57, 59, 
        109, 48, 57, 59, 109, 48, 57, 53, 
        59, 109, 48, 52, 54, 57, 54, 55, 
        56, 57, 59, 109, 48, 53, 54, 55, 
        56, 57, 59, 109, 48, 53, 49, 50, 
        51, 52, 53, 54, 55, 56, 57, 59, 
        109, 56, 57, 59, 109, 48, 55, 59, 
        50, 53, 59, 49, 50, 48, 57, 59, 
        48, 57, 53, 59, 48, 52, 54, 57, 
        59, 48, 53, 59, 49, 50, 48, 57, 
        59, 109, 48, 57, 49, 50, 51, 52, 
        53, 54, 55, 56, 57, 59, 109, 49, 
        50, 51, 52, 53, 54, 55, 56, 57, 
        59, 109, 59, 48, 57, 53, 59, 48, 
        52, 54, 57, 59, 48, 53, 59, 49, 
        50, 48, 57, 10, 13, 27, 34, 38, 
        60, 62, 0
]
class << self
        attr_accessor :_ansi_sgr_to_html_single_lengths
        private :_ansi_sgr_to_html_single_lengths, :_ansi_sgr_to_html_single_lengths=
end
self._ansi_sgr_to_html_single_lengths = [
        0, 7, 1, 12, 2, 2, 11, 12, 
        4, 1, 2, 1, 2, 1, 1, 2, 
        1, 1, 2, 2, 2, 3, 6, 6, 
        11, 4, 1, 2, 1, 2, 1, 2, 
        1, 1, 2, 2, 11, 11, 1, 2, 
        1, 1, 2, 7
]
class << self
        attr_accessor :_ansi_sgr_to_html_range_lengths
        private :_ansi_sgr_to_html_range_lengths, :_ansi_sgr_to_html_range_lengths=
end
self._ansi_sgr_to_html_range_lengths = [
        0, 0, 0, 0, 0, 1, 0, 0, 
        1, 0, 0, 0, 1, 1, 0, 1, 
        1, 0, 1, 1, 1, 2, 1, 1, 
        0, 1, 0, 0, 0, 1, 1, 2, 
        1, 0, 1, 1, 0, 0, 1, 2, 
        1, 0, 1, 0
]
class << self
        attr_accessor :_ansi_sgr_to_html_index_offsets
        private :_ansi_sgr_to_html_index_offsets, :_ansi_sgr_to_html_index_offsets=
end
self._ansi_sgr_to_html_index_offsets = [
        0, 0, 8, 10, 23, 26, 30, 42, 
        55, 61, 63, 66, 68, 72, 75, 77, 
        81, 84, 86, 90, 94, 98, 104, 112, 
        120, 132, 138, 140, 143, 145, 149, 152, 
        157, 160, 162, 166, 170, 182, 194, 197, 
        202, 205, 207, 211
]
class << self
        attr_accessor :_ansi_sgr_to_html_trans_targs
        private :_ansi_sgr_to_html_trans_targs, :_ansi_sgr_to_html_trans_targs=
end
self._ansi_sgr_to_html_trans_targs = [
        43, 43, 2, 43, 43, 43, 43, 1, 
        3, 0, 4, 5, 7, 8, 25, 35, 
        23, 24, 36, 37, 3, 43, 0, 3, 
        43, 0, 3, 43, 6, 0, 5, 7, 
        8, 25, 35, 23, 24, 36, 37, 3, 
        43, 0, 6, 6, 6, 6, 6, 6, 
        6, 6, 6, 6, 3, 43, 0, 9, 
        6, 3, 43, 6, 0, 10, 0, 11, 
        41, 0, 12, 0, 30, 31, 13, 0, 
        15, 14, 0, 15, 0, 38, 39, 16, 
        0, 18, 17, 0, 18, 0, 20, 21, 
        19, 0, 3, 43, 6, 0, 3, 43, 
        19, 0, 22, 3, 43, 19, 6, 0, 
        23, 24, 36, 37, 3, 43, 6, 0, 
        23, 24, 36, 37, 3, 43, 6, 0, 
        5, 7, 8, 25, 35, 23, 24, 36, 
        37, 3, 43, 0, 26, 6, 3, 43, 
        6, 0, 27, 0, 28, 33, 0, 29, 
        0, 30, 31, 13, 0, 15, 13, 0, 
        32, 15, 13, 14, 0, 15, 14, 0, 
        34, 0, 20, 21, 19, 0, 3, 43, 
        6, 0, 5, 7, 8, 25, 35, 23, 
        24, 36, 37, 3, 43, 0, 5, 7, 
        8, 25, 35, 23, 24, 36, 37, 3, 
        43, 0, 18, 16, 0, 40, 18, 16, 
        17, 0, 18, 17, 0, 42, 0, 20, 
        21, 19, 0, 43, 43, 2, 43, 43, 
        43, 43, 1, 0
]
class << self
        attr_accessor :_ansi_sgr_to_html_trans_actions
        private :_ansi_sgr_to_html_trans_actions, :_ansi_sgr_to_html_trans_actions=
end
self._ansi_sgr_to_html_trans_actions = [
        61, 61, 1, 61, 61, 61, 61, 63, 
        0, 0, 59, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 59, 94, 0, 0, 
        3, 0, 5, 67, 55, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 
        3, 0, 57, 7, 11, 15, 19, 25, 
        65, 29, 33, 37, 9, 70, 0, 0, 
        51, 13, 73, 39, 0, 0, 0, 0, 
        0, 0, 0, 0, 47, 47, 47, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 3, 0, 0, 0, 3, 
        0, 0, 0, 0, 3, 0, 0, 0, 
        0, 0, 0, 0, 0, 3, 0, 0, 
        23, 23, 23, 23, 23, 82, 65, 0, 
        27, 27, 27, 27, 27, 27, 27, 27, 
        27, 27, 85, 0, 0, 53, 17, 76, 
        41, 0, 0, 0, 0, 0, 0, 0, 
        0, 49, 49, 49, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 45, 45, 45, 0, 21, 79, 
        65, 0, 31, 31, 31, 31, 31, 31, 
        31, 31, 31, 31, 88, 0, 35, 35, 
        35, 35, 35, 35, 35, 35, 35, 35, 
        91, 0, 0, 0, 0, 0, 0, 0, 
        0, 0, 0, 0, 0, 0, 0, 43, 
        43, 43, 0, 61, 61, 1, 61, 61, 
        61, 61, 63, 0
]
class << self
        attr_accessor :ansi_sgr_to_html_start
end
self.ansi_sgr_to_html_start = 43;
class << self
        attr_accessor :ansi_sgr_to_html_first_final
end
self.ansi_sgr_to_html_first_final = 43;
class << self
        attr_accessor :ansi_sgr_to_html_error
end
self.ansi_sgr_to_html_error = 0;
class << self
        attr_accessor :ansi_sgr_to_html_en_main
end
self.ansi_sgr_to_html_en_main = 43;
        end

Public Instance Methods

convert(i, o=nil) click to toggle source

Convert string

Arguments:
i

Input (string at the moment, future versions are planned to accept streams)

o

Output, anything with << to append to (e.g. String, $stdout, etc.) or nil to get a string returned after complete conversion

# File lib/ansi_sgr_to_html.rb, line 280
        def convert(i, o=nil) # i is string, o can be anything with <<
                o = '' unless o
begin
        p ||= 0
        pe ||=   i.length
            @fsm_cs = ansi_sgr_to_html_start
end
begin
        _klen, _trans, _keys, _acts, _nacts = nil
        _goto_level = 0
        _resume = 10
        _eof_trans = 15
        _again = 20
        _test_eof = 30
        _out = 40
        while true
        _trigger_goto = false
        if _goto_level <= 0
        if p == pe
                _goto_level = _test_eof
                next
        end
        if     @fsm_cs == 0
                _goto_level = _out
                next
        end
        end
        if _goto_level <= _resume
        _keys = _ansi_sgr_to_html_key_offsets[    @fsm_cs]
        _trans = _ansi_sgr_to_html_index_offsets[    @fsm_cs]
        _klen = _ansi_sgr_to_html_single_lengths[    @fsm_cs]
        _break_match = false
        begin
          if _klen > 0
             _lower = _keys
             _upper = _keys + _klen - 1
             loop do
                break if _upper < _lower
                _mid = _lower + ( (_upper - _lower) >> 1 )
                if   i[p].ord < _ansi_sgr_to_html_trans_keys[_mid]
                   _upper = _mid - 1
                elsif   i[p].ord > _ansi_sgr_to_html_trans_keys[_mid]
                   _lower = _mid + 1
                else
                   _trans += (_mid - _keys)
                   _break_match = true
                   break
                end
             end # loop
             break if _break_match
             _keys += _klen
             _trans += _klen
          end
          _klen = _ansi_sgr_to_html_range_lengths[    @fsm_cs]
          if _klen > 0
             _lower = _keys
             _upper = _keys + (_klen << 1) - 2
             loop do
                break if _upper < _lower
                _mid = _lower + (((_upper-_lower) >> 1) & ~1)
                if   i[p].ord < _ansi_sgr_to_html_trans_keys[_mid]
                  _upper = _mid - 2
                elsif   i[p].ord > _ansi_sgr_to_html_trans_keys[_mid+1]
                  _lower = _mid + 2
                else
                  _trans += ((_mid - _keys) >> 1)
                  _break_match = true
                  break
                end
             end # loop
             break if _break_match
             _trans += _klen
          end
        end while false
            @fsm_cs = _ansi_sgr_to_html_trans_targs[_trans]
        if _ansi_sgr_to_html_trans_actions[_trans] != 0
                _acts = _ansi_sgr_to_html_trans_actions[_trans]
                _nacts = _ansi_sgr_to_html_actions[_acts]
                _acts += 1
                while _nacts > 0
                        _nacts -= 1
                        _acts += 1
                        case _ansi_sgr_to_html_actions[_acts - 1]
when 0 then
                begin
                end
when 1 then
                begin
 if(@cur_css != @prv_css) then   o<<('</span>') if(@prv_css != {});   if(!@putblnk && @cur_css['animation']) then @putblnk=true; o<<@@CSS_blink_keyframes; end;   o<<('<span style="'+@cur_css.map{|k,v|"#{k}:#{v}" if(v)}.compact.join(';')+'">') if(@cur_css != {});   end;   @prv_css = @cur_css.clone;              end
when 2 then
                begin
 @cur_css['font-weight'] = 'bold'; if(@k_1_is_bold_and_bright) then @curcol=@hicol; if(@cur_css['color']) then k = @locol.index(@cur_css['color'][1,6]); @cur_css['color'] = '#'+@curcol[k] if(k); end; end;            end
when 3 then
                begin
 if(@k_21_is_double_underline) then @cur_css.delete('text-decoration'); @cur_css['border-bottom'] = '3px double currentColor'; else @cur_css.delete('font-weight'); @curcol = @locol; end;              end
when 4 then
                begin
 @cur_css['font-weight'] = 'lighter';           end
when 5 then
                begin
 @cur_css.delete('font-weight');                end
when 6 then
                begin
 @cur_css['font-style'] = 'italic';             end
when 7 then
                begin
 @cur_css.delete('font-style');                 end
when 8 then
                begin
 @cur_css.delete('border-bottom'); @cur_css['text-decoration'] = 'underline';           end
when 9 then
                begin
 @cur_css.delete('text-decoration'); @cur_css.delete('border-bottom');          end
when 10 then
                begin
 @cur_css['animation'] = 'term_blink normal 1.2s infinite ease-in-out';                 end
when 11 then
                begin
 @cur_css['animation'] = 'term_blink normal 0.7s infinite ease-in-out';                 end
when 12 then
                begin
 @cur_css.delete('animation');          end
when 13 then
                begin
 @cur_css['color'],@cur_css['background-color'] = @cur_css['background-color'],@cur_css['color'] unless(@inv); @inv = true;             end
when 14 then
                begin
 @cur_css['color'],@cur_css['background-color'] = @cur_css['background-color'],@cur_css['color']     if(@inv); @inv = false;            end
when 15 then
                begin
                end
when 16 then
                begin
                end
when 17 then
                begin
 @cur_css['text-decoration-line'] = 'line-through';             end
when 18 then
                begin
 @cur_css.delete('text-decoration-line');               end
when 19 then
                begin
 @cur_css['color'] = '#'+@curcol[  i[p].ord-0x30];              end
when 20 then
                begin
 @cur_css['background-color'] = '#'+@locol[  i[p].ord-0x30];            end
when 21 then
                begin
 @cur_css['color'] = '#'+@@Col_tab_256[i[p,3].to_i];            end
when 22 then
                begin
 @cur_css['background-color'] = '#'+@@Col_tab_256[i[p,3].to_i];                 end
when 23 then
                begin
 @cur_css['color'] = 'rgb('+i[p,11].match(/(\d+);(\d+);(\d+)/)[1,3].join(',')+')';              end
when 24 then
                begin
 @cur_css['background-color'] = 'rgb('+i[p,11].match(/(\d+);(\d+);(\d+)/)[1,3].join(',')+')';           end
when 25 then
                begin
 @cur_css.delete('color');              end
when 26 then
                begin
 @cur_css.delete('background-color');           end
when 27 then
                begin
 if(  i[p].ord == 0x30) then @cur_css.delete('font-family'); else @cur_css['font-family'] = @font_styles[(  i[p].ord-0x30)-1]; end;             end
when 28 then
                begin
 @cur_css['font-family'] = @font_styles[9]              end
when 29 then
                begin
 @cur_css = {}; @curcol = @locol                end
when 30 then
                begin
 o<<(@html_escapes[  i[p].ord]);                end
when 31 then
                begin
 o<<  i[p].ord.chr(Encoding::UTF_8);            end
when 32 then
                begin
                end
                        end # action switch
                end
        end
        if _trigger_goto
                next
        end
        end
        if _goto_level <= _again
        if     @fsm_cs == 0
                _goto_level = _out
                next
        end
        p += 1
        if p != pe
                _goto_level = _resume
                next
        end
        end
        if _goto_level <= _test_eof
        end
        if _goto_level <= _out
                break
        end
        end
        end
                o << '</span>' if(@prv_css != {})
                o
        end