Created
January 28, 2012 10:02
-
-
Save epitron/1693810 to your computer and use it in GitHub Desktop.
Convert ANSI to HTML
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'epitools' | |
class State | |
attr_accessor :fore, :back, :attrs | |
COLORS = [:black, :red, :green, :yellow, :blue, :magenta, :cyan, :white] | |
ATTRS = { | |
0 => :reset, | |
1 => :bright, | |
2 => :dim, | |
4 => :underscore, | |
5 => :blink, | |
7 => :reverse, | |
8 => :hidden, | |
} | |
FORES = { | |
30 => :black, | |
31 => :red, | |
32 => :green, | |
33 => :yellow, | |
34 => :blue, | |
35 => :magenta, | |
36 => :cyan, | |
37 => :white, | |
} | |
BACKS = { | |
40 => :black, | |
41 => :red, | |
42 => :green, | |
43 => :yellow, | |
44 => :blue, | |
45 => :magenta, | |
46 => :cyan, | |
47 => :white, | |
} | |
PALETTE = [ | |
"#000000", | |
"#CC0000", | |
"#4E9A06", | |
"#C4A000", | |
"#3465A4", | |
"#75507B", | |
"#06989A", | |
"#D3D7CF", | |
"#555753", | |
"#EF2929", | |
"#8AE234", | |
"#FCE94F", | |
"#729FCF", | |
"#AD7FA8", | |
"#34E2E2", | |
"#EEEEEC" | |
] | |
def initialize | |
reset | |
end | |
def reset | |
@fore = :white | |
@back = :black | |
@attrs ||= Set.new | |
@attrs.clear | |
end | |
def update(code) | |
case | |
when attr = ATTRS[code] | |
if attr == :reset | |
reset | |
else | |
attrs << attr | |
end | |
when fore = FORES[code] | |
@fore = fore | |
when back = BACKS[code] | |
@back = back | |
end | |
end | |
def html_color(sym, bright=false) | |
n = COLORS.index(sym) | |
n += 8 if bright | |
PALETTE[n] | |
end | |
def html_for(text) | |
bright = @attrs.include?(:bright) | |
style = "color:#{html_color @fore, bright }; background-color:#{ html_color @back };" | |
result = "<span style='#{style}'>" | |
if bright | |
result << "<b>#{text}</b>" | |
else | |
result << text | |
end | |
result << "</span>" | |
result | |
end | |
end | |
def entities(text) | |
text.gsub("<", "<").gsub(">", ">") | |
end | |
def ansi2html(ansi) | |
# problem: | |
# * minimize the number of tags | |
# | |
# complex algorithm: | |
# * parse the ansi into a structure with color/runlength for each extent of text | |
# * an extent of color A, bracketed by extents of color B, should be turned into a nested tag | |
# [ INPUT = flat, OUTPUT = tree ] | |
# questions: | |
# * what scenario(s) will cause this scheme to produce more tags than a naive approach? | |
# | |
# simpler algorithm: | |
# * split on color codes | |
# * delete redundant codes | |
# * create <span>s | |
# debugging info | |
tokens = ansi.split(/(\e\[\d{1,4}(?:;\d{1,4})*[mhclnRABCDfsugJKi])/) | |
# remove non-color terminal codes | |
tokens = tokens.select{|s| not ( s.blank? or s =~ /^\e\[([\d;]+)?[hclnRABCDfsugJKi]$/ ) } | |
output = [] # will contain series of html tags | |
state = State.new # the state of the terminal (current color and attributes) | |
tokens.each do |token| | |
if token =~ /^\e\[(.+)m/ | |
codes = $1.scan(/(\d+);?/).flatten.map(&:to_i) # grab all the code numbers | |
codes.each do |code| | |
state.update(code) | |
end | |
else # it's a blob of text. | |
output << state.html_for(entities(token)) | |
end | |
end | |
puts | |
output.join("")#.gsub("\n", "<br/>\n") | |
end | |
def ansi2file(ansi, f) | |
f.puts "<pre style='background-color: black;'>" | |
f.puts ansi2html(ansi) | |
f.puts "</pre>" | |
end | |
def test_pattern | |
result = ansi2html("\e[c" + "<red>red <light_green>green <blue>blue".colorize + "\e[31;1m") | |
# print the output | |
puts "result: "; pp result | |
colors = State::COLORS + State::COLORS.map {|color| "light_#{color}" } | |
ansi = colors.map{|color| "<8>[<#{color}>#{color}<8>]\n" }.join('').colorize | |
open("testpat.html", "w") do |f| | |
f.puts "<code style='background-color: black; display: block;'>" | |
f.puts ansi2html(ansi) | |
f.puts "</code>" | |
puts "* testpat.html written!" | |
end | |
end | |
if $0 == __FILE__ | |
ansi2file(ARGF.read, $stdout) | |
end |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
input: | |
"\e[c\e[31mred \e[0m\e[32m\e[1mgreen \e[0m\e[34mblue\e[0m\e[31;1m" | |
tokens: | |
[:code, "\e[31m"] | |
[:text, "red "] | |
[:code, "\e[0m"] | |
[:code, "\e[32m"] | |
[:code, "\e[1m"] | |
[:text, "green "] | |
[:code, "\e[0m"] | |
[:code, "\e[34m"] | |
[:text, "blue"] | |
[:code, "\e[0m"] | |
[:code, "\e[31;1m"] | |
result: | |
"<span style='color:#CC0000; background-color:#000000;'>red <span><span style='color:#8AE234; background-color:#000000;'>green <span><span style='color:#3465A4; background-color:#000000;'>blue<span>" | |
input: | |
"\e[30m\e[1m[\e[0m\e[30mblack\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[31mred\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[32mgreen\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[33myellow\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[34mblue\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[35mmagenta\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[36mcyan\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[37mwhite\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[30m\e[1mlight_black\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[31m\e[1mlight_red\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[32m\e[1mlight_green\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[33m\e[1mlight_yellow\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[34m\e[1mlight_blue\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[35m\e[1mlight_magenta\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[36m\e[1mlight_cyan\e[0m\e[30m\e[1m]\n\e[0m\e[30m\e[1m[\e[0m\e[37m\e[1mlight_white\e[0m\e[30m\e[1m]\n\e[0m" | |
tokens: | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:text, "black"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[31m"] | |
[:text, "red"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[32m"] | |
[:text, "green"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[33m"] | |
[:text, "yellow"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[34m"] | |
[:text, "blue"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[35m"] | |
[:text, "magenta"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[36m"] | |
[:text, "cyan"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[37m"] | |
[:text, "white"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "light_black"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[31m"] | |
[:code, "\e[1m"] | |
[:text, "light_red"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[32m"] | |
[:code, "\e[1m"] | |
[:text, "light_green"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[33m"] | |
[:code, "\e[1m"] | |
[:text, "light_yellow"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[34m"] | |
[:code, "\e[1m"] | |
[:text, "light_blue"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[35m"] | |
[:code, "\e[1m"] | |
[:text, "light_magenta"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[36m"] | |
[:code, "\e[1m"] | |
[:text, "light_cyan"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "["] | |
[:code, "\e[0m"] | |
[:code, "\e[37m"] | |
[:code, "\e[1m"] | |
[:text, "light_white"] | |
[:code, "\e[0m"] | |
[:code, "\e[30m"] | |
[:code, "\e[1m"] | |
[:text, "]\n"] | |
[:code, "\e[0m"] | |
* testpat.html written! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<code style='background-color: black; display: block;'> | |
<span style='color:#555753; background-color:#000000;'>[<span><span style='color:#000000; background-color:#000000;'>black<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#CC0000; background-color:#000000;'>red<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#4E9A06; background-color:#000000;'>green<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#C4A000; background-color:#000000;'>yellow<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#3465A4; background-color:#000000;'>blue<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#75507B; background-color:#000000;'>magenta<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#06989A; background-color:#000000;'>cyan<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#D3D7CF; background-color:#000000;'>white<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#555753; background-color:#000000;'>light_black<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#EF2929; background-color:#000000;'>light_red<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#8AE234; background-color:#000000;'>light_green<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#FCE94F; background-color:#000000;'>light_yellow<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#729FCF; background-color:#000000;'>light_blue<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#AD7FA8; background-color:#000000;'>light_magenta<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#34E2E2; background-color:#000000;'>light_cyan<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span><span style='color:#555753; background-color:#000000;'>[<span><span style='color:#EEEEEC; background-color:#000000;'>light_white<span><span style='color:#555753; background-color:#000000;'>]<br/> | |
<span> | |
</code> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment