module Metanorma::Utils

Constants

NAMECHAR
NAMESTARTCHAR
SVG_NS
VERSION

Public Class Methods

anchor_or_uuid(node = nil) click to toggle source
# File lib/utils/main.rb, line 29
def anchor_or_uuid(node = nil)
  uuid = UUIDTools::UUID.random_create
  node.nil? || node.id.nil? || node.id.empty? ? "_#{uuid}" : node.id
end
asciidoc_sub(text, flavour = :standoc) click to toggle source
# File lib/utils/main.rb, line 34
def asciidoc_sub(text, flavour = :standoc)
  return nil if text.nil?
  return "" if text.empty?

  d = Asciidoctor::Document.new(
    text.lines.entries,
    { header_footer: false, backend: flavour },
  )
  b = d.parse.blocks.first
  b.apply_subs(b.source)
end
datauri(uri, localdirectory = ".") click to toggle source

sources/plantuml/plantuml20200524-90467-1iqek5i.png already includes localdir

# File lib/utils/image.rb, line 129
def datauri(uri, localdirectory = ".")
  return uri if /^data:/.match?(uri)

  path = datauri_path(uri, localdirectory)
  return path unless File.exist?(path)

  types = MIME::Types.type_for(path)
  type = types ? types.first.to_s : 'text/plain; charset="utf-8"'
  bin = File.open(path, "rb", &:read)
  data = Base64.strict_encode64(bin)
  "data:#{type};base64,#{data}"
end
datauri2mime(uri) click to toggle source
# File lib/utils/image.rb, line 154
def datauri2mime(uri)
  %r{^data:image/(?<imgtype>[^;]+);base64,(?<imgdata>.+)$} =~ uri
  type = nil
  imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
  ::Tempfile.open(["imageuri", ".#{imgtype}"]) do |file|
    type = datauri2mime1(file, imgdata)
  end
  [type]
end
datauri2mime1(file, imgdata) click to toggle source
# File lib/utils/image.rb, line 164
def datauri2mime1(file, imgdata)
  type = nil
  begin
    file.binmode
    file.write(Base64.strict_decode64(imgdata))
    file.rewind
    type = Marcel::MimeType.for file
  ensure
    file.close!
  end
  type
end
datauri_path(uri, localdirectory) click to toggle source
# File lib/utils/image.rb, line 142
def datauri_path(uri, localdirectory)
  path = if %r{^([A-Z]:)?/}.match?(uri) then uri
         else
           File.exist?(uri) ? uri : File.join(localdirectory, uri)
         end
  unless File.exist?(path)
    warn "image at #{path} not found"
    return uri
  end
  path
end
default_script(lang) click to toggle source
# File lib/utils/main.rb, line 119
def default_script(lang)
  case lang
  when "ar", "fa" then "Arab"
  when "ur" then "Aran"
  when "ru", "bg" then "Cyrl"
  when "hi" then "Deva"
  when "el" then "Grek"
  when "zh" then "Hans"
  when "ko" then "Kore"
  when "he" then "Hebr"
  when "ja" then "Jpan"
  else
    "Latn"
  end
end
endash_date(elem) click to toggle source
# File lib/utils/main.rb, line 58
def endash_date(elem)
  elem.traverse do |n|
    next unless n.text?

    n.replace(n.text.gsub(/\s+--?\s+/, "&#8211;").gsub(/--/, "&#8211;"))
  end
end
external_path(path) click to toggle source
# File lib/utils/main.rb, line 108
def external_path(path)
  win = !!((RUBY_PLATFORM =~ /(win|w)(32|64)$/) ||
           (RUBY_PLATFORM =~ /mswin|mingw/))
  if win
    path.gsub!(%{/}, "\\")
    path[/\s/] ? "\"#{path}\"" : path
  else
    path
  end
end
flatten_rawtext(node) click to toggle source

not currently used if node contains blocks, flatten them into a single line; and extract only raw text

# File lib/utils/main.rb, line 156
def flatten_rawtext(node)
  result = []
  if node.respond_to?(:blocks) && node.blocks?
    node.blocks.each { |b| result << flatten_rawtext(b) }
  elsif node.respond_to?(:lines)
    result = flatten_rawtext_lines(node, result)
  elsif node.respond_to?(:text)
    result << node.text.gsub(/<[^>]*>+/, "")
  else
    result << node.content.gsub(/<[^>]*>+/, "")
  end
  result.reject(&:empty?)
end
flatten_rawtext_lines(node, result) click to toggle source

not currently used

# File lib/utils/main.rb, line 140
def flatten_rawtext_lines(node, result)
  node.lines.each do |x|
    result << if node.respond_to?(:context) &&
        (node.context == :literal || node.context == :listing)
                x.gsub(/</, "&lt;").gsub(/>/, "&gt;")
              else
                # strip not only HTML <tag>, and Asciidoc xrefs <<xref>>
                x.gsub(/<[^>]*>+/, "")
              end
  end
  result
end
localdir(node) click to toggle source
# File lib/utils/main.rb, line 46
def localdir(node)
  docfile = node.attr("docfile")
  docfile.nil? ? "./" : "#{Pathname.new(docfile).parent}/"
end
rtl_script?(script) click to toggle source
# File lib/utils/main.rb, line 135
def rtl_script?(script)
  %w(Arab Aran Hebr).include? script
end
save_dataimage(uri) click to toggle source
# File lib/utils/image.rb, line 25
def save_dataimage(uri)
  %r{^data:(image|application)/(?<imgtype>[^;]+);(charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri
  imgtype.sub!(/\+[a-z0-9]+$/, "") # svg+xml
  imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
  Tempfile.open(["image", ".#{imgtype}"]) do |f|
    f.binmode
    f.write(Base64.strict_decode64(imgdata))
    f.path
  end
end
set_nested_value(hash, keys, new_val) click to toggle source

Set hash value using keys path mod from stackoverflow.com/a/42425884

# File lib/utils/main.rb, line 68
def set_nested_value(hash, keys, new_val)
  key = keys[0]
  if keys.length == 1
    hash[key] = if hash[key].is_a?(Array) then (hash[key] << new_val)
                else hash[key].nil? ? new_val : [hash[key], new_val]
                end
  elsif hash[key].is_a?(Array)
    hash[key][-1] = {} if !hash[key].empty? && hash[key][-1].nil?
    hash[key] << {} if hash[key].empty? || !hash[key][-1].is_a?(Hash)
    set_nested_value(hash[key][-1], keys[1..-1], new_val)
  elsif hash[key].nil? || hash[key].empty?
    hash[key] = {}
    set_nested_value(hash[key], keys[1..-1], new_val)
  elsif hash[key].is_a?(Hash) && !hash[key][keys[1]]
    set_nested_value(hash[key], keys[1..-1], new_val)
  elsif !hash[key][keys[1]]
    hash[key] = [hash[key], {}]
    set_nested_value(hash[key][-1], keys[1..-1], new_val)
  else
    set_nested_value(hash[key], keys[1..-1], new_val)
  end
  hash
end
smartformat(text) click to toggle source

TODO needs internationalisation

# File lib/utils/main.rb, line 52
def smartformat(text)
  text.gsub(/ --? /, "&#8201;&#8212;&#8201;")
    .gsub(/--/, "&#8212;").smart_format.gsub(/</, "&lt;")
    .gsub(/>/, "&gt;")
end
strict_capitalize_first(str) click to toggle source
# File lib/utils/main.rb, line 100
def strict_capitalize_first(str)
  str.split(/ /).each_with_index.map do |w, i|
    letters = w.chars
    letters.first.upcase! if i.zero?
    letters.join
  end.join(" ")
end
strict_capitalize_phrase(str) click to toggle source
# File lib/utils/main.rb, line 92
def strict_capitalize_phrase(str)
  str.split(/ /).map do |w|
    letters = w.chars
    letters.first.upcase!
    letters.join
  end.join(" ")
end
svg_update_href(svgmap, svg, namespace) click to toggle source
# File lib/utils/image.rb, line 77
def svg_update_href(svgmap, svg, namespace)
  targ = svgmap_rewrite1_targets(svgmap, namespace)
  svg.xpath(".//m:a", "m" => SVG_NS).each do |a|
    ["xlink:href", "href"].each do |p|
      a[p] and x = targ[File.expand_path(a[p])] and a[p] = x
    end
  end
end
svg_update_ids(svg, idx) click to toggle source
# File lib/utils/image.rb, line 97
def svg_update_ids(svg, idx)
  ids = svg.xpath("./@id | .//@id")
    .each_with_object([]) { |i, m| m << i.value }
  return if ids.empty?

  svg_update_ids_attrs(svg, ids, idx)
  svg_update_ids_css(svg, ids, idx)
end
svg_update_ids_attrs(svg, ids, idx) click to toggle source
# File lib/utils/image.rb, line 106
def svg_update_ids_attrs(svg, ids, idx)
  svg.xpath(". | .//*[@*]").each do |a|
    a.attribute_nodes.each do |x|
      ids.include?(x.value) and x.value += sprintf("_%09d", idx)
    end
  end
end
svg_update_ids_css(svg, ids, idx) click to toggle source
# File lib/utils/image.rb, line 114
def svg_update_ids_css(svg, ids, idx)
  svg.xpath("//m:style", "m" => SVG_NS).each do |s|
    c = s.children.to_xml
    ids.each do |i|
      c = c.gsub(%r[##{i}\b],
                 sprintf("#%<id>s_%<idx>09d", id: i, idx: idx))
        .gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]),
              sprintf("[id='%<id>s_%<idx>09d']", id: i, idx: idx))
    end
    s.children = c
  end
end
svgmap_rewrite(xmldoc, localdirectory = "") click to toggle source
# File lib/utils/image.rb, line 38
def svgmap_rewrite(xmldoc, localdirectory = "")
  n = Namespace.new(xmldoc)
  xmldoc.xpath(n.ns("//svgmap")).each_with_index do |s, i|
    next unless svgmap_rewrite0(s, n, localdirectory, i)
    next if s.at(n.ns("./target/eref"))

    s.replace(s.at(n.ns("./figure")))
  end
end
svgmap_rewrite0(svgmap, namespace, localdirectory, idx) click to toggle source
# File lib/utils/image.rb, line 48
def svgmap_rewrite0(svgmap, namespace, localdirectory, idx)
  if (i = svgmap.at(namespace.ns(".//image"))) && (src = i["src"])
    path = svgmap_rewrite0_path(src, localdirectory)
    File.file?(path) or return false
    svg = Nokogiri::XML(File.read(path, encoding: "utf-8"))
    i.replace(svgmap_rewrite1(svgmap, svg.root, namespace, idx))
    /^data:/.match(src) and i["src"] = datauri(path)
  elsif i = svgmap.at(".//m:svg", "m" => SVG_NS)
    i.replace(svgmap_rewrite1(svgmap, i, namespace, idx))
  else return false
  end
  true
end
svgmap_rewrite0_path(src, localdirectory) click to toggle source
# File lib/utils/image.rb, line 62
def svgmap_rewrite0_path(src, localdirectory)
  if /^data:/.match?(src)
    save_dataimage(src)
  else
    File.file?(src) ? src : localdirectory + src
  end
end
svgmap_rewrite1(svgmap, svg, namespace, idx) click to toggle source
# File lib/utils/image.rb, line 70
def svgmap_rewrite1(svgmap, svg, namespace, idx)
  svg_update_href(svgmap, svg, namespace)
  svg_update_ids(svg, idx)
  svg.xpath("processing-instruction()|.//processing-instruction()").remove
  svg.to_xml
end
svgmap_rewrite1_targets(svgmap, namespace) click to toggle source
# File lib/utils/image.rb, line 86
def svgmap_rewrite1_targets(svgmap, namespace)
  svgmap.xpath(namespace.ns("./target"))
    .each_with_object({}) do |t, m|
    x = t.at(namespace.ns("./xref")) and
      m[File.expand_path(t["href"])] = "##{x['target']}"
    x = t.at(namespace.ns("./link")) and
      m[File.expand_path(t["href"])] = x["target"]
    t.remove if t.at(namespace.ns("./xref | ./link"))
  end
end
to_ncname(tag, asciionly: true) click to toggle source
# File lib/utils/main.rb, line 17
def to_ncname(tag, asciionly: true)
  asciionly and tag = HTMLEntities.new.encode(tag, :basic, :hexadecimal)
  start = tag[0]
  ret1 = if %r([#{NAMECHAR}#])o.match?(start)
           "_"
         else
           (%r([#{NAMESTARTCHAR}#])o.match?(start) ? "_#{start}" : start)
         end
  ret2 = tag[1..-1] || ""
  (ret1 || "") + ret2.gsub(%r([#{NAMECHAR}#])o, "_")
end