Module: Metanorma::Standoc::Image

Included in:
Cleanup
Defined in:
lib/metanorma/cleanup/image.rb

Constant Summary collapse

IRI_TAG_PROPERTIES_MAP =
{
  clipPath: ["clip-path"],
  "color-profile": nil,
  cursor: nil,
  filter: nil,
  linearGradient: ["fill", "stroke"],
  marker: ["marker", "marker-end", "marker-mid", "marker-start"],
  mask: nil,
  pattern: ["fill", "stroke"],
  radialGradient: ["fill", "stroke"],
}.freeze
SVG_NS =
"http://www.w3.org/2000/svg".freeze

Instance Method Summary collapse

Instance Method Details

#altmedia_cleanup(xmldoc) ⇒ Object



73
74
75
76
77
78
79
80
# File 'lib/metanorma/cleanup/image.rb', line 73

def altmedia_cleanup(xmldoc)
  xmldoc.xpath("//image[@altmedia]").each do |i|
    altmedia_root_cleanup(i)
    d = altmedia_prep(i)
    d or next
    altsource_populate(i, d)
  end
end

#altmedia_prep(img) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
# File 'lib/metanorma/cleanup/image.rb', line 115

def altmedia_prep(img)
  dl = img.at("./dl") or return
  d = extract_symbols_list(dl.remove).map do |e|
    { dt: e[:dt].text, dd: e[:dd].at(".//image") }
  end
  unless d.detect { |e| e[:dt] == "default" }
    d << d.first.dup
    d[-1][:dt] = "default"
  end
  d
end

#altmedia_root_cleanup(img) ⇒ Object



109
110
111
112
113
# File 'lib/metanorma/cleanup/image.rb', line 109

def altmedia_root_cleanup(img)
  %w(altmedia src filename mimetype).each do |k|
    img.delete(k)
  end
end

#altsource_attr_copy(src, dest, img) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/metanorma/cleanup/image.rb', line 97

def altsource_attr_copy(src, dest, img)
  %w(media).each do |k|
    src[k] and dest[k] = src[k]
  end
  %w(height width).each do |k|
    dest[k] = img[k]
    src[k] or next
    src[k] == "auto" and next
    dest[k] = src[k]
  end
end

#altsource_populate(img, dlist) ⇒ Object



82
83
84
85
86
87
88
89
# File 'lib/metanorma/cleanup/image.rb', line 82

def altsource_populate(img, dlist)
  dlist.each do |e|
    e[:dd].nil? and next
    img << "<altsource tag='#{e[:dt]}'/>"
    image_attr_copy(e[:dd], img.elements.last)
    altsource_attr_copy(e[:dd], img.elements.last, img)
  end
end

#datauri_image(xmldoc) ⇒ Object



64
65
66
67
68
69
70
71
# File 'lib/metanorma/cleanup/image.rb', line 64

def datauri_image(xmldoc)
  xmldoc.xpath("//image | //altsource").each do |i|
    # do not datauri encode SVG, we need to deduplicate its IDs
    unless read_in_if_svg?(i, @localdir)
      i["src"] &&= Vectory::Utils::datauri(i["src"], @localdir)
    end
  end
end

#image_attr_copy(src, dest) ⇒ Object



91
92
93
94
95
# File 'lib/metanorma/cleanup/image.rb', line 91

def image_attr_copy(src, dest)
  %w(src mimetype filename alt).each do |k|
    src[k] and dest[k] = src[k]
  end
end

#img_cleanup(xmldoc) ⇒ Object



57
58
59
60
61
62
# File 'lib/metanorma/cleanup/image.rb', line 57

def img_cleanup(xmldoc)
  altmedia_cleanup(xmldoc)
  @datauriimage and datauri_image(xmldoc)
  svg_cleanup(xmldoc)
  xmldoc
end

#read_in_if_svg?(img, localdir) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
135
136
137
138
139
140
141
# File 'lib/metanorma/cleanup/image.rb', line 132

def read_in_if_svg?(img, localdir)
  img["src"] or return false
  path = Vectory::Utils.svgmap_rewrite0_path(img["src"], localdir)
  File.file?(path) or return false
  types = MIME::Types.type_for(path) or return false
  types.first == "image/svg+xml" or return false
  svg = File.read(path, encoding: "utf-8") or return false
  img.add_first_child Nokogiri::XML(svg).root
  true
end

#svg_attrupdate(elem, iri_properties, idx, ids) ⇒ Object



237
238
239
240
241
242
# File 'lib/metanorma/cleanup/image.rb', line 237

def svg_attrupdate(elem, iri_properties, idx, ids)
  iri_properties.each do |p|
    elem[p] or next
    elem[p] = svg_update_url(elem[p], idx, ids)
  end
end

#svg_classupdate(xmldoc) ⇒ Object



143
144
145
146
147
148
# File 'lib/metanorma/cleanup/image.rb', line 143

def svg_classupdate(xmldoc)
  xmldoc.xpath("//m:svg[m:style]", "m" => SVG_NS)
    .each_with_index do |s, i|
    svg_classupdate1(s, s.at("./m:style", "m" => SVG_NS), i)
  end
end

#svg_classupdate1(svg, style, idx) ⇒ Object



150
151
152
153
154
155
156
157
158
# File 'lib/metanorma/cleanup/image.rb', line 150

def svg_classupdate1(svg, style, idx)
  tree = Crass.parse(style.text)
  tree.each { |n| svg_suffix_css_style(n, idx) }
  style.children = Crass::Parser.stringify(tree)
  svg.xpath(".//*[@class]").each do |n|
    n["class"] = n["class"].split(/\s+/)
      .map { |x| "#{x}_inject_#{idx}" }.join(" ")
  end
end

#svg_cleanup(xmldoc) ⇒ Object



127
128
129
130
# File 'lib/metanorma/cleanup/image.rb', line 127

def svg_cleanup(xmldoc)
  svg_uniqueids(xmldoc)
  svg_classupdate(xmldoc)
end

#svg_idupdate(elem, idx, ids) ⇒ Object



253
254
255
256
257
# File 'lib/metanorma/cleanup/image.rb', line 253

def svg_idupdate(elem, idx, ids)
  elem["id"] or return
  ids[elem["id"]] or return
  elem["id"] += "_inject_#{idx}"
end

#svg_iri_properties(id_elems) ⇒ Object



192
193
194
195
196
197
198
199
200
201
# File 'lib/metanorma/cleanup/image.rb', line 192

def svg_iri_properties(id_elems)
  iri_tag_names = id_elems.each_with_object([]) do |e, m|
    IRI_TAG_PROPERTIES_MAP.key?(e.name.to_sym) and m << e.name
  end.uniq
  iri_properties = iri_tag_names.each_with_object([]) do |t, m|
    (IRI_TAG_PROPERTIES_MAP[t.to_sym] || [t]).each { |t1| m = m << t1 }
  end.uniq
  iri_properties.empty? and return []
  iri_properties << "style"
end

#svg_linkupdate(elem, idx, ids) ⇒ Object



244
245
246
247
248
249
250
251
# File 'lib/metanorma/cleanup/image.rb', line 244

def svg_linkupdate(elem, idx, ids)
  %w(xlink:href href).each do |ref|
    iri = elem[ref]&.strip
    /^#/.match?(iri) or next
    ids[iri.sub(/^#/, "")] or next
    elem[ref] += "_inject_#{idx}"
  end
end

#svg_styleupdate(elem, idx, ids) ⇒ Object



233
234
235
# File 'lib/metanorma/cleanup/image.rb', line 233

def svg_styleupdate(elem, idx, ids)
  elem.children = svg_update_url(elem.text, idx, ids)
end

#svg_suffix_css_style(node, idx) ⇒ Object



160
161
162
163
164
165
166
# File 'lib/metanorma/cleanup/image.rb', line 160

def svg_suffix_css_style(node, idx)
  (node[:node] == :style_rule && /\./.match?(node[:selector][:value])) or
    return
  v = node[:selector][:value]
    .gsub(/([^.\s]*\.\S+)/, "\\1_inject_#{idx}")
  node[:selector] = Crass.parse("#{v} {}").first[:selector]
end

#svg_uniqueids(xmldoc) ⇒ Object



182
183
184
185
186
187
188
189
190
# File 'lib/metanorma/cleanup/image.rb', line 182

def svg_uniqueids(xmldoc)
  # only keep non-unique identifiers
  ids = xmldoc.xpath("//m:svg//*/@id | //svg/@id", "m" => SVG_NS)
    .map(&:text).group_by(&:itself).transform_values(&:count)
    .delete_if { |_, v| v < 2 }
  xmldoc.xpath("//m:svg", "m" => SVG_NS).each_with_index do |s, i|
    ids = svg_uniqueids1(s, i, ids)
  end
end

#svg_uniqueids1(svg, idx, ids) ⇒ Object



203
204
205
206
207
208
209
210
# File 'lib/metanorma/cleanup/image.rb', line 203

def svg_uniqueids1(svg, idx, ids)
  id_elems = svg.xpath(".//*[@id] | ./@id/..")
  iri_properties = svg_iri_properties(id_elems)
  svg_uniqueids2(svg, iri_properties, idx, ids)
  new_ids = id_elems.map { |x| x["id"] }
    .map { |x| x + (ids[x] ? "_inject_#{idx}" : "") }
  ids.merge(new_ids.each.to_h { |value| [value, true] })
end

#svg_uniqueids2(svg, iri_properties, idx, ids) ⇒ Object



212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/metanorma/cleanup/image.rb', line 212

def svg_uniqueids2(svg, iri_properties, idx, ids)
  svg.traverse do |e|
    e.element? or next
    if e.name == "style"
      svg_styleupdate(e, idx, ids)
    elsif !e.attributes.empty?
      svg_attrupdate(e, iri_properties, idx, ids)
    end
    svg_linkupdate(e, idx, ids)
    svg_idupdate(e, idx, ids)
  end
end

#svg_update_url(text, idx, ids) ⇒ Object



225
226
227
228
229
230
231
# File 'lib/metanorma/cleanup/image.rb', line 225

def svg_update_url(text, idx, ids)
  text.gsub(/url\("?#([a-zA-Z][\w:.-]*)"?\)/) do |x|
    if ids[$1] then "url(##{$1}_inject_#{idx})"
    else x
    end
  end
end

#svgmap_cleanup(xmldoc) ⇒ Object



7
8
9
10
11
# File 'lib/metanorma/cleanup/image.rb', line 7

def svgmap_cleanup(xmldoc)
  svgmap_moveattrs(xmldoc)
  svgmap_populate(xmldoc)
  Vectory::SvgMapping.new(xmldoc, @localdir).call
end

#svgmap_moveattrs(xmldoc) ⇒ Object



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/metanorma/cleanup/image.rb', line 13

def svgmap_moveattrs(xmldoc)
  xmldoc.xpath("//svgmap").each do |s|
    f = s.at(".//figure") or next
    (t = s.at("./name")) && !f.at("./name") and
      f.add_first_child t.remove
    if s["anchor"] # && Metanorma::Utils::guid_anchor?(f["id"])
      f["anchor"] = s["anchor"]
      s.delete("anchor")
    end
    svgmap_moveattrs1(s, f)
  end
end

#svgmap_moveattrs1(svgmap, figure) ⇒ Object



26
27
28
29
30
31
32
33
# File 'lib/metanorma/cleanup/image.rb', line 26

def svgmap_moveattrs1(svgmap, figure)
  %w(unnumbered number subsequence keep-with-next
     keep-lines-together tag multilingual-rendering).each do |a|
    figure[a] || !svgmap[a] and next
    figure[a] = svgmap[a]
    svgmap.delete(a)
  end
end

#svgmap_populate(xmldoc) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
# File 'lib/metanorma/cleanup/image.rb', line 35

def svgmap_populate(xmldoc)
  xmldoc.xpath("//svgmap").each do |s|
    s1 = s.dup
    s.children.remove
    f = s1.at(".//figure") and s << f
    s1.xpath(".//li").each do |li|
      t = li.at(".//eref | .//link | .//xref") or next
      href = t.xpath("./following-sibling::node()")
      href.empty? or
        s << %[<target href="#{svgmap_target(href)}">#{t.to_xml}</target>]
    end
  end
end

#svgmap_target(nodeset) ⇒ Object



49
50
51
52
53
54
55
# File 'lib/metanorma/cleanup/image.rb', line 49

def svgmap_target(nodeset)
  nodeset.each do |n|
    n.name == "link" or next
    n.children = n["target"]
  end
  nodeset.text.sub(/^[,; ]/, "").strip
end