Module: Metanorma::Mirror::Handlers::Inline::RichHtmlRenderer

Defined in:
lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb

Overview

Renders inline Metanorma elements as HTML strings. Used when an HTML representation of inline content is required (e.g., attribute values, fallback rendering). All HTML construction goes through Nokogiri::HTML5::Builder to guarantee well-formed, escaped output.

Simple-wrap elements (em, strong, sub, sup, code, u, s, smallcap, bcp14, concept) take their tag-and-attrs mapping from the shared Inline::Catalog table.

Constant Summary collapse

COMPLEX_RENDERERS =

Complex element handlers — these have per-instance attrs or multi-step rendering and bypass the shared Catalog.

{
  Metanorma::Document::Components::Inline::StemInlineElement => ->(el) {
    for_stem(el)
  },
  Metanorma::Document::Components::TextElements::StemElement => ->(el) {
    for_stem(el)
  },
  Metanorma::Document::Components::Inline::XrefElement => ->(el) {
    for_xref(el)
  },
  Metanorma::Document::Components::Inline::LinkElement => ->(el) {
    for_link(el)
  },
  Metanorma::Document::Components::Inline::ErefElement => ->(el) {
    for_eref(el)
  },
  Metanorma::Document::Components::Inline::FnElement => ->(el) {
    for_fn(el)
  },
  Metanorma::Document::Components::Inline::SpanElement => ->(el) {
    for_span(el)
  },
  Metanorma::Document::Components::Inline::BrElement => ->(_el) {
    wrap(&:br)
  },
}.freeze

Class Method Summary collapse

Class Method Details

.extract(element) ⇒ Object



47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 47

def self.extract(element)
  return "" unless element

  unless element.is_a?(Lutaml::Model::Serializable)
    return CGI.escapeHTML(element.to_s)
  end

  parts = []
  element.each_mixed_content { |node| parts << render_node(node) }
  result = parts.join.strip
  result.empty? ? TextExtractor.extract_element_text(element) : result
end

.for_eref(element) ⇒ Object



121
122
123
124
125
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 121

def self.for_eref(element)
  citeas = SafeAttr.read(element, :citeas) || ""
  label = TextExtractor.extract_formatted_text(element)
  wrap { |d| d.a(class: "eref", cite: citeas) { d.text label } }
end

.for_fn(element) ⇒ Object



127
128
129
130
131
132
133
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 127

def self.for_fn(element)
  reference = SafeAttr.read(element, :reference) || ""
  spec = Catalog::SIMPLE_WRAPS["footnote"]
  wrap do |d|
    d.public_send(spec[:tag], spec.except(:tag)) { d.text reference }
  end
end


114
115
116
117
118
119
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 114

def self.for_link(element)
  href = SafeAttr.read(element,
                       :target) || SafeAttr.read(element, :href) || ""
  label = TextExtractor.extract_formatted_text(element)
  wrap { |d| d.a(href: href) { d.text label } }
end

.for_span(element) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 135

def self.for_span(element)
  cls = SafeAttr.read(element, :class_attr)
  inner = extract(element)
  wrap do |d|
    if cls
      d.span(class: cls) { d << raw(inner) }
    else
      d.span { d << raw(inner) }
    end
  end
end

.for_stem(element) ⇒ Object



93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 93

def self.for_stem(element)
  math = SafeAttr.read(element, :math)
  return TextExtractor.extract_element_text(element) unless math

  mathml = MathUtil.mathml_from_math(math)
    .sub(/\s*xmlns(:\w+)?="[^"]*"\s*/, " ")
    .gsub(/\s+/, " ")
    .strip
  spec = Catalog::SIMPLE_WRAPS["stem"]
  wrap do |d|
    d.public_send(spec[:tag], spec.except(:tag)) { d << raw(mathml) }
  end
end

.for_xref(element) ⇒ Object



107
108
109
110
111
112
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 107

def self.for_xref(element)
  target = SafeAttr.read(element, :target) || ""
  label = TextExtractor.extract_formatted_text(element)
  label = target if label.strip.empty?
  wrap { |d| d.a(href: "##{target}") { d.text label } }
end

.raw(html_string) ⇒ Object



151
152
153
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 151

def self.raw(html_string)
  Nokogiri::HTML5::DocumentFragment.parse(html_string.to_s)
end

.render_element(element) ⇒ Object



71
72
73
74
75
76
77
78
79
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 71

def self.render_element(element)
  complex = COMPLEX_RENDERERS[element.class]
  return complex.call(element) if complex

  mark_type = Catalog::SIMPLE_ELEMENTS[element.class]
  return render_simple(mark_type, element) if mark_type

  extract(element)
end

.render_node(node) ⇒ Object



60
61
62
63
64
65
66
67
68
69
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 60

def self.render_node(node)
  case node
  when String
    CGI.escapeHTML(node)
  when Lutaml::Model::Serializable
    render_element(node)
  else
    ""
  end
end

.render_simple(mark_type, element) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 81

def self.render_simple(mark_type, element)
  spec = Catalog::SIMPLE_WRAPS[mark_type]
  attrs = spec.except(:tag)
  wrap do |d|
    if attrs.empty?
      d.public_send(spec[:tag]) { d << raw(extract(element)) }
    else
      d.public_send(spec[:tag], attrs) { d << raw(extract(element)) }
    end
  end
end

.wrapObject



147
148
149
# File 'lib/metanorma/mirror/handlers/inline/rich_html_renderer.rb', line 147

def self.wrap(&)
  Nokogiri::HTML5::Builder.new(&).doc.root.to_html
end