diff --git a/lib/gherkin/formatter/html_formatter.rb b/lib/gherkin/formatter/html_formatter.rb
new file mode 100644
index 00000000..8c529480
--- /dev/null
+++ b/lib/gherkin/formatter/html_formatter.rb
@@ -0,0 +1,182 @@
+# encoding: utf-8
+require 'gherkin/formatter/argument'
+require 'gherkin/formatter/escaping'
+require 'gherkin/formatter/model'
+require 'gherkin/native'
+
+module Gherkin
+ module Formatter
+ class HtmlFormatter
+ native_impl('gherkin')
+
+ include Escaping
+
+ def initialize(io, executing)
+ @io = io
+ @executing = executing
+ @background = nil
+ @tag_statement = nil
+ @steps = []
+ @io.puts "
"
+ end
+
+ def uri(uri)
+ @uri = uri
+ end
+
+ def feature(feature)
+ print_comments(feature.comments)
+ print_tags(feature.tags)
+ @io.puts "
#{feature.keyword}: #{feature.name}
"
+ print_description(feature.description, false)
+ end
+
+ def background(background)
+ replay
+ @statement = background
+ end
+
+ def scenario(scenario)
+ replay
+ @io.puts ""
+ print_tags(scenario.tags)
+ @io.puts "
" + scenario.keyword + ": " + escape_brackets(scenario.name) + "
"
+ end
+
+ def scenario_outline(scenario_outline)
+ scenario scenario_outline
+ end
+
+ def replay
+ print_statement
+ print_steps
+ end
+
+ def print_statement
+ return if @statement.nil?
+ @io.puts
+ print_comments(@statement.comments)
+ print_tags(@statement.tags) if @statement.respond_to?(:tags) # Background doesn't
+ @io.write " #{@statement.keyword}: #{@statement.name}"
+ location = @executing ? "#{@uri}:#{@statement.line}" : nil
+ @io.write location
+ print_description(@statement.description)
+ @statement = nil
+ end
+
+ def print_steps
+ while(@steps.any?)
+ print_step('b', [], nil, true)
+ end
+ end
+
+ def examples(examples)
+ replay
+ @io.puts
+ print_comments(examples.comments)
+ print_tags(examples.tags)
+ @io.write "#{examples.keyword}: #{examples.name}"
+ print_description(examples.description)
+ table(examples.rows, true)
+ end
+
+ def step(step)
+ @steps << step
+ end
+
+ def match(match)
+ @match = match
+ print_statement
+ print_step('executing', @match.arguments, @match.location, false)
+ end
+
+ def result(result)
+ @io.write(up(1))
+ print_step(result.status, @match.arguments, @match.location, true)
+ end
+
+ def print_step(status, arguments, location, proceed)
+ step = proceed ? @steps.shift : @steps[0]
+
+ print_comments(step.comments)
+ @io.write("<" + status + ">" + step.keyword + "" + status + ">")
+ @io.write(escape_brackets(step.name))
+ @io.write(escape_brackets(location))
+ @io.puts("
")
+
+ doc_string(step.doc_string) if step.doc_string
+ table(step.rows) if step.rows
+ end
+
+ def eof
+ replay
+ # NO-OP
+ end
+
+ def done
+ @io.puts("")
+ end
+
+ def table(rows, header=false)
+ @io.puts("")
+
+ rows.each_with_index do |row, i|
+ @io.puts("")
+ row.cells.each do |cell|
+ bal = header && i == 0 ? "th" : "td"
+ @io.puts(" <" + bal + ">" + cell + "" + bal + ">")
+ end
+ @io.puts("
")
+ end
+
+ @io.puts("
")
+ end
+
+ private
+ def escape_brackets(s)
+ return if s.nil?
+ s.gsub("<", "<").gsub(">", ">")
+ end
+
+ def doc_string(doc_string)
+ @io.puts doc_string.content_type + "
\n" + escape_triple_quotes(doc_string.value) + "
\n"
+ end
+
+ def exception(exception)
+ exception_text = "#{exception.message} (#{exception.class})\n#{(exception.backtrace || []).join("\n")}".gsub(/^/, ' ')
+ @io.puts(failed(exception_text))
+ end
+
+ if(RUBY_VERSION =~ /^1\.9|2\.0/)
+ START = /#{'^'.encode('UTF-8')}/
+ TRIPLE_QUOTES = /#{'"""'.encode('UTF-8')}/
+ else
+ START = /^/
+ TRIPLE_QUOTES = /"""/
+ end
+
+ def escape_triple_quotes(s)
+ s.gsub(TRIPLE_QUOTES, '\"\"\"')
+ end
+
+ def print_tags(tags)
+ # @MonTag
+ @io.write(tags.empty? ? '' : tags.map{|tag| "" + tag.name + "" }.join(' ') + "\n")
+ end
+
+ def print_comments(comments)
+ @io.puts(comments.empty? ? '' : "" + comments.map{|comment| "" + comment.value.gsub( /#/m, "" ) + ""}.join("
\n") + "
\n")
+ end
+
+ def print_description(description, newline=true)
+ if description != ""
+ description = description.gsub( //m, ">" )
+ description = description.gsub( /\n/m, "
\n\r" )
+ @io.puts "" + description + "
"
+ @io.puts if newline
+ end
+ end
+ end
+ end
+end