module IsoDoc
class PresentationXMLConvert < ::IsoDoc::Convert
def sourcehighlighter_css(docxml)
ret = custom_css(docxml)
ret.empty? and return
ins = extension_insert(docxml)
ins << "#{ret}" \
""
end
def custom_css(docxml)
ret = ""
@sourcehighlighter and ret += rouge_css_location
a = docxml.at(ns("//metanorma-extension/" \
"clause[title = 'user-css']/sourcecode")) and
ret += "\n#{to_xml(a.children)}"
ret
end
# replace in local gem rather than specify overrides of default
def rouge_css_location
File.read(File.join(File.dirname(__FILE__), "..", "base_style",
"rouge.css"))
end
def sourcehighlighter
@sourcehighlighter or return
Rouge::Formatter.enable_escape!
f = Rouge::Formatters::HTML.new
opts = { gutter_class: "rouge-gutter", code_class: "rouge-code" }
f1 = Rouge::Formatters::HTMLLineTable.new(f, opts)
{ formatter: f, formatter_line: f1 }
end
def callouts(elem)
elem.xpath(ns(".//callout")).each do |c|
@callouts[c["target"]] = c.children.to_xml
end
end
def sourcecode(docxml)
sourcehighlighter_css(docxml)
@highlighter = sourcehighlighter
@callouts = {}
docxml.xpath(ns("//sourcecode")).each do |f|
sourcecode1(f)
end
end
def sourcecode1(elem)
source_highlight(elem)
source_label(elem)
callouts(elem)
annotations(elem)
end
def annotations(elem)
elem.at(ns("./annotation")) or return
ret = ""
elem.xpath(ns("./annotation")).each do |a|
a.remove
ret += <<~OUT
#{@callouts[a['id']]}
#{a.children.to_xml}
OUT
end
elem << "#{@i18n.key}#{ret}
"
end
def source_highlight(elem)
@highlighter or return
markup = source_remove_markup(elem)
p = source_lex(elem)
elem.children = if elem["linenums"] == "true"
r = sourcecode_table_to_elem(elem, p)
source_restore_markup_table(r, markup)
else
r = @highlighter[:formatter].format(p)
source_restore_markup(Nokogiri::XML.fragment(r), markup)
end
end
def source_remove_markup(elem)
ret = {}
name = elem.at(ns("./name")) and ret[:name] = name.remove.to_xml
source_remove_annotations(ret, elem)
ret
end
def source_remove_annotations(ret, elem)
ret[:ann] = elem.xpath(ns("./annotation")).each(&:remove)
ret[:call] = elem.xpath(ns("./callout")).each_with_object([]) do |c, m|
m << { xml: c.remove, line: c.line - elem.line }
end
ret
end
def source_restore_markup(wrapper, markup)
ret = source_restore_callouts(wrapper, markup[:call])
"#{markup[:name]}#{ret}#{markup[:ann]}"
end
def source_restore_markup_table(wrapper, markup)
source_restore_callouts_table(wrapper, markup[:call])
ret = to_xml(wrapper)
"#{markup[:name]}#{ret}#{markup[:ann]}"
end
def source_restore_callouts(code, callouts)
text = to_xml(code)
text.split(/[\n\r]/).each_with_index do |c, i|
while !callouts.empty? && callouts[0][:line] == i
c.sub!(/\s+$/, " #{reinsert_callout(callouts[0][:xml])} ")
callouts.shift
end
end.join("\n")
end
def source_restore_callouts_table(table, callouts)
table.xpath(".//td[@class = 'rouge-code']/sourcecode")
.each_with_index do |c, i|
while !callouts.empty? && callouts[0][:line] == i
c << " #{reinsert_callout(callouts[0][:xml])} "
callouts.shift
end
end
end
def reinsert_callout(xml)
"#{to_xml(xml)}"
end
def sourcecode_table_to_elem(elem, tokens)
r = Nokogiri::XML(@highlighter[:formatter_line].format(tokens)).root
r.xpath(".//td[@class = 'rouge-code']/pre").each do |pre|
%w(style).each { |n| elem[n] and pre[n] = elem[n] }
pre.name = "sourcecode"
pre.children = to_xml(pre.children).sub(/\s+$/, "")
end
r
end
def source_lex(elem)
lexer = (Rouge::Lexer.find(elem["lang"] || "plaintext") ||
Rouge::Lexer.find("plaintext"))
l = Rouge::Lexers::Escape.new(start: "{^^{", end: "}^^}", lang: lexer)
source = to_xml(elem.children).gsub(/, "{^^{<").gsub(/>/, ">}^^}")
l.lang.reset!
l.lex(@c.decode(source))
end
def source_label(elem)
labelled_ancestor(elem) and return
lbl = @xrefs.anchor(elem["id"], :label, false) or return
prefix_name(elem, block_delim,
l10n("#{lower2cap @i18n.figure} #{lbl}"), "name")
end
end
end