lib/isodoc/presentation_function/block.rb in isodoc-2.4.0 vs lib/isodoc/presentation_function/block.rb in isodoc-2.4.1

- old
+ new

@@ -1,6 +1,7 @@ require_relative "./image" +require "rouge" module IsoDoc class PresentationXMLConvert < ::IsoDoc::Convert def lower2cap(text) return text if /^[[:upper:]][[:upper:]]/.match?(text) @@ -23,78 +24,150 @@ if name.children.empty? then name.add_child(cleanup_entities(number.strip)) else (name.children.first.previous = "#{number.strip}#{delim}") end end + def sourcehighlighter_theme + "igorpro" + end + + def sourcehighlighter_css(docxml) + @sourcehighlighter or return + ins = docxml.at(ns("//misc-container")) || + docxml.at(ns("//bibdata")).after("<misc-container/>").next_element + x = Rouge::Theme.find(sourcehighlighter_theme) + .render(scope: "sourcecode") + ins << "<source-highlighter-css>#{x}</source-highlighter-css>" + end + + def sourcehighlighter + @sourcehighlighter or return + f = Rouge::Formatters::HTML.new + { formatter: f, + formatter_line: Rouge::Formatters::HTMLTable.new(f, {}) } + end + def sourcecode(docxml) + sourcehighlighter_css(docxml) + @highlighter = sourcehighlighter docxml.xpath(ns("//sourcecode")).each do |f| sourcecode1(f) end end def sourcecode1(elem) - return if labelled_ancestor(elem) + source_highlight(elem) + source_label(elem) + end + def source_highlight(elem) + @highlighter or return + markup = source_remove_markup(elem) + p = source_lex(elem) + wrapper, code = + if elem["linenums"] == "true" then sourcecode_table_to_elem(elem, p) + else + r = Nokogiri::XML.fragment(@highlighter[:formatter].format(p)) + [r, r] + end + elem.children = source_restore_markup(wrapper, code, markup) + end + + def source_remove_markup(elem) + ret = {} + name = elem.at(ns("./name")) and ret[:name] = name.remove.to_xml + ret[:ann] = elem.xpath(ns("./annotation")).each(&:remove) + ret[:call] = elem.xpath(ns("./callout")).each_with_object([]) do |c, m| + m << { xml: c.remove.to_xml, line: c.line - elem.line } + end + ret + end + + def source_restore_markup(wrapper, code, markup) + text = source_restore_callouts(code, markup[:call]) + ret = if code == wrapper + text + else + code.replace(text) + to_xml(wrapper) + end + "#{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+$/, " #{callouts[0][:xml]} ") + callouts.shift + end + end.join("\n") + end + + def sourcecode_table_to_elem(elem, tokens) + r = Nokogiri::XML(@highlighter[:formatter_line].format(tokens)).root + pre = r.at(".//td[@class = 'rouge-code']/pre") + %w(style).each { |n| elem[n] and pre[n] = elem[n] } + pre.name = "sourcecode" + [r, pre] + end + + def source_lex(elem) + l = (Rouge::Lexer.find(elem["lang"] || "plaintext") || + Rouge::Lexer.find("plaintext")) + l.lex(@c.decode(elem.children.to_xml)) + 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 def formula(docxml) - docxml.xpath(ns("//formula")).each do |f| - formula1(f) - end + docxml.xpath(ns("//formula")).each { |f| formula1(f) } end def formula1(elem) lbl = @xrefs.anchor(elem["id"], :label, false) prefix_name(elem, "", lbl, "name") end def example(docxml) - docxml.xpath(ns("//example")).each do |f| - example1(f) - end + docxml.xpath(ns("//example")).each { |f| example1(f) } end def example1(elem) n = @xrefs.get[elem["id"]] lbl = if n.nil? || n[:label].nil? || n[:label].empty? @i18n.example - else - l10n("#{@i18n.example} #{n[:label]}") + else l10n("#{@i18n.example} #{n[:label]}") end prefix_name(elem, block_delim, lbl, "name") end def note(docxml) - docxml.xpath(ns("//note")).each do |f| - note1(f) - end + docxml.xpath(ns("//note")).each { |f| note1(f) } end def note1(elem) - return if elem.parent.name == "bibitem" || elem["notag"] == "true" - + elem.parent.name == "bibitem" || elem["notag"] == "true" and return n = @xrefs.get[elem["id"]] lbl = if n.nil? || n[:label].nil? || n[:label].empty? @i18n.note - else - l10n("#{@i18n.note} #{n[:label]}") + else l10n("#{@i18n.note} #{n[:label]}") end prefix_name(elem, "", lbl, "name") end def admonition(docxml) - docxml.xpath(ns("//admonition")).each do |f| - admonition1(f) - end + docxml.xpath(ns("//admonition")).each { |f| admonition1(f) } end def admonition1(elem) - return if elem.at(ns("./name")) || elem["notag"] == "true" - + elem.at(ns("./name")) || elem["notag"] == "true" and return prefix_name(elem, "", @i18n.admonition[elem["type"]]&.upcase, "name") end def recommendation(docxml) docxml.xpath(ns("//recommendation")).each do |f| @@ -119,41 +192,34 @@ .recommendation_label(elem, type, xrefs) prefix_name(elem, "", l10n(lbl), "name") end def table(docxml) - docxml.xpath(ns("//table")).each do |f| - table1(f) - end + docxml.xpath(ns("//table")).each { |f| table1(f) } end def table1(elem) - return if labelled_ancestor(elem) - return if elem["unnumbered"] && !elem.at(ns("./name")) - + labelled_ancestor(elem) and return + elem["unnumbered"] && !elem.at(ns("./name")) and return n = @xrefs.anchor(elem["id"], :label, false) prefix_name(elem, block_delim, l10n("#{lower2cap @i18n.table} #{n}"), "name") end # we use this to eliminate the semantic amend blocks from rendering def amend(docxml) - docxml.xpath(ns("//amend")).each do |f| - amend1(f) - end + docxml.xpath(ns("//amend")).each { |f| amend1(f) } end def amend1(elem) elem.xpath(ns("./autonumber")).each(&:remove) elem.xpath(ns("./newcontent")).each { |a| a.name = "quote" } elem.xpath(ns("./description")).each { |a| a.replace(a.children) } elem.replace(elem.children) end def ol(docxml) - docxml.xpath(ns("//ol")).each do |f| - ol1(f) - end + docxml.xpath(ns("//ol")).each { |f| ol1(f) } @xrefs.list_anchor_names(docxml.xpath(ns(@xrefs.sections_xpath))) end # We don't really want users to specify type of ordered list; # we will use by default a fixed hierarchy as practiced by ISO (though not