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 127
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 152
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 162
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 140
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
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
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 127
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 111
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
save_dataimage(uri) click to toggle source
# File lib/utils/image.rb, line 25
def save_dataimage(uri)
  %r{^data:(image|application)/(?<imgtype>[^;]+);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)
                  (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 102
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 94
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("#%s_%09d", i, idx))
        .gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]), sprintf("[id='%s_%09d']", i, 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