require "fileutils" require "base64" module IsoDoc::HtmlFunction module Html def make_body1(body, _docxml) body.div **{ class: "title-section" } do |div1| div1.p { |p| p << " " } # placeholder end section_break(body) end def make_body2(body, docxml) body.div **{ class: "prefatory-section" } do |div2| div2.p { |p| p << " " } # placeholder end section_break(body) end def make_body3(body, docxml) body.div **{ class: "main-section" } do |div3| abstract docxml, div3 foreword docxml, div3 introduction docxml, div3 middle docxml, div3 footnotes div3 comments div3 end end def postprocess(result, filename, dir) result = from_xhtml(cleanup(to_xhtml(result))) toHTML(result, filename) @files_to_delete.each { |f| FileUtils.rm_rf f } end def script_cdata(result) result.gsub(%r{<script([^>]*)>\s*<!\[CDATA\[}m, "<script\\1>"). gsub(%r{\]\]>\s*</script>}, "</script>"). gsub(%r{<!\[CDATA\[\s*<script([^>]*)>}m, "<script\\1>"). gsub(%r{</script>\s*\]\]>}, "</script>") end def toHTML(result, filename) result = (from_xhtml(html_cleanup(to_xhtml(result)))) result = populate_template(result, :html) result = script_cdata(from_xhtml(move_images(to_xhtml(result)))) File.open("#{filename}.html", "w:UTF-8") do |f| f.write(result) end end def html_cleanup(x) footnote_backlinks(html_toc( term_header((html_footnote_filter(html_preface(htmlstyle(x)))))) ) end MATHJAX_ADDR = "https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/latest.js".freeze MATHJAX = <<~"MATHJAX".freeze <script type="text/x-mathjax-config"> MathJax.Hub.Config({ asciimath2jax: { delimiters: [['OPEN', 'CLOSE']] } }); </script> <script src="#{MATHJAX_ADDR}?config=AM_HTMLorMML" async="async"></script> MATHJAX def mathjax(open, close) MATHJAX.gsub("OPEN", open).gsub("CLOSE", close) end def term_header(docxml) %w(h1 h2 h3 h4 h5 h6 h7 h8).each do |h| docxml.xpath("//p[@class = 'TermNum'][../#{h}]").each do |p| p.name = "h#{h[1].to_i + 1}" end end docxml end def googlefonts() <<~HEAD.freeze <link href="https://fonts.googleapis.com/css?family=Overpass:300,300i,600,900" rel="stylesheet"> <link href="https://fonts.googleapis.com/css?family=Lato:400,400i,700,900" rel="stylesheet"> HEAD end def toclevel <<~HEAD.freeze function toclevel() { var i; var text = ""; for(i = 1; i <= #{@htmlToClevels}; i++) { if (i > 1) { text += ","; } text += "h" + i + ":not(:empty):not(.TermNum)"; } return text;} HEAD end def html_head() <<~HEAD.freeze <title>{{ doctitle }}</title> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <!--TOC script import--> <script type="text/javascript" src="https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"></script> <script type="text/javascript">#{toclevel}</script> <!--Google fonts--> #{googlefonts} <!--Font awesome import for the link icon--> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/solid.css" integrity="sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk" crossorigin="anonymous"> <link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css" integrity="sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P" crossorigin="anonymous"> <style class="anchorjs"></style> HEAD end def html_button() '<button onclick="topFunction()" id="myBtn" '\ 'title="Go to top">Top</button>'.freeze end def html_main(docxml) docxml.at("//head").add_child(html_head()) d = docxml.at('//div[@class="main-section"]') d.name = "main" d.children.empty? or d.children.first.previous = html_button() end def sourcecode_highlighter '<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>' end def sourcecodelang(lang) return unless lang case lang.downcase when "javascript" then "lang-js" when "c" then "lang-c" when "c+" then "lang-cpp" when "console" then "lang-bsh" when "ruby" then "lang-rb" when "html" then "lang-html" when "java" then "lang-java" when "xml" then "lang-xml" when "perl" then "lang-perl" when "python" then "lang-py" when "xsl" then "lang-xsl" else "" end end def sourcecode_parse(node, out) name = node.at(ns("./name")) out.pre **attr_code(id: node["id"], class: "prettyprint #{sourcecodelang(node&.at(ns('./@lang'))&.value)}") do |div| @sourcecode = true node.children.each do |n| parse(n, div) unless n.name == "name" end @sourcecode = false sourcecode_name_parse(node, div, name) if name end end def html_preface(docxml) html_cover(docxml) if @htmlcoverpage html_intro(docxml) if @htmlintropage docxml.at("//body") << mathjax(@openmathdelim, @closemathdelim) docxml.at("//body") << sourcecode_highlighter if @scripts scripts = File.read(@scripts, encoding: "UTF-8") a = docxml.at("//body").add_child docxml.create_cdata(scripts) end html_main(docxml) docxml end def html_cover(docxml) cover = File.read(@htmlcoverpage, encoding: "UTF-8") coverxml = to_xhtml_fragment(cover) d = docxml.at('//div[@class="title-section"]') d.children.first.add_previous_sibling coverxml.to_xml(encoding: "US-ASCII") end def html_intro(docxml) intro = File.read(@htmlintropage, encoding: "UTF-8") introxml = to_xhtml_fragment(intro) d = docxml.at('//div[@class="prefatory-section"]') d.children.first.add_previous_sibling introxml.to_xml(encoding: "US-ASCII") end def htmlstylesheet stylesheet = File.read(@htmlstylesheet, encoding: "UTF-8") xml = Nokogiri::XML("<style/>") xml.children.first << Nokogiri::XML::Comment.new(xml, "\n#{stylesheet}\n") xml.root.to_s end def htmlstyle(docxml) return docxml unless @htmlstylesheet title = docxml.at("//*[local-name() = 'head']/*[local-name() = 'title']") head = docxml.at("//*[local-name() = 'head']") css = htmlstylesheet if title.nil? then head.children.first.add_previous_sibling css else title.add_next_sibling css end docxml end def update_footnote_filter(fn, x, i, seen) if seen[fn.text] x.at("./sup").content = seen[fn.text][:num].to_s fn.remove unless x["href"] == seen[fn.text][:href] x["href"] = seen[fn.text][:href] else seen[fn.text] = { num: i, href: x["href"] } x.at("./sup").content = i.to_s i += 1 end [i, seen] end def html_footnote_filter(docxml) seen = {} i = 1 docxml.xpath('//a[@epub:type = "footnote"]').each do |x| fn = docxml.at(%<//*[@id = '#{x['href'].sub(/^#/, '')}']>) || next i, seen = update_footnote_filter(fn, x, i, seen) end docxml end def footnote_backlinks(docxml) seen = {} docxml.xpath('//a[@epub:type = "footnote"]').each_with_index do |x, i| next if seen[x["href"]] seen[x["href"]] = true fn = docxml.at(%<//*[@id = '#{x['href'].sub(/^#/, '')}']>) || next x["id"] || x["id"] = "fnref:#{i + 1}" fn.elements.first.children.first.previous = x.dup fn.add_child "<a href='##{x['id']}'>↩</a>" end docxml end # presupposes that the image source is local def move_images(docxml) FileUtils.rm_rf tmpimagedir FileUtils.mkdir tmpimagedir docxml.xpath("//*[local-name() = 'img']").each do |i| i["width"], i["height"] = Html2Doc.image_resize(i, image_localfile(i), @maxheight, @maxwidth) next if /^data:image/.match i["src"] @datauriimage ? datauri(i) : move_image1(i) end docxml end def image_localfile(i) if /^data:image/.match i["src"] save_dataimage(i["src"], false) else File.join(@localdir, i["src"]) end end def datauri(i) type = i["src"].split(".")[-1] bin = IO.binread(File.join(@localdir, i["src"])) data = Base64.strict_encode64(bin) i["src"] = "data:image/#{type};base64,#{data}" end def move_image1(i) matched = /\.(?<suffix>[^. \r\n\t]+)$/.match i["src"] uuid = UUIDTools::UUID.random_create.to_s fname = "#{uuid}.#{matched[:suffix]}" new_full_filename = File.join(tmpimagedir, fname) local_filename = image_localfile(i) FileUtils.cp local_filename, new_full_filename i["src"] = File.join(rel_tmpimagedir, fname) end def html_toc_entry(level, header) %(<li class="h#{level}"><a href="##{header['id']}">#{header_strip(header)}</a></li>) end def html_toc(docxml) =begin idx = docxml.at("//div[@id = 'toc']") or return docxml toc = "<ul>" tocidx = 0 docxml.xpath("//main//h1 | //main//h2[not(@class = 'TermNum')]").each do |h| h["id"] ||= "toc#{tocidx}" toc += html_toc_entry(h.name == "h1" ? 1 : 2, h) tocidx += 1 end idx.children = "#{toc}</ul>" =end docxml end end end