metanorma-utils 1.6.4 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 57cdd3eb9d930975ba70e75b1a93fa61b071ad0a84f6d9acb9173f39e4ab9461
4
- data.tar.gz: 7d7db96c6baf145635b80c7538191582c98acde5d47698e0f5743b706e5c9a8b
3
+ metadata.gz: d976aa0a315c0de6418c649e96065e3b5f0792a232e55ed9b2a6044ed7257491
4
+ data.tar.gz: 50829e2e03b2c4da90cbccf7773309c4e6a88dfc5e3eaeb9d93f33608fe36539
5
5
  SHA512:
6
- metadata.gz: f32fd8887a434a77e6f99ad81c5dafb8677096787c7fbae70cf4aeac4d5d264913639a9f216aa0793f8f3b7637ff78303372d7c8ee5072c516ca983436a3c47e
7
- data.tar.gz: d9434cdece35bd9f95f9366a987d11995c19dca2bf2cd348ab1cf877a3fff96c66f69dc137cebc9d548a3b8c7a01ef3f0d196993b3873e62d58cceea9c277a21
6
+ metadata.gz: fbee4963987764258501a416f9cf662612240b15f53f169fe2f104738d8bda20c83f9fa7a782077e9668b9764ea1ac968a6a155709e0bc4d04bc15f0a8a2507c
7
+ data.tar.gz: e25371b4a3c0efa083a7086495207fede9ac7c8df0524b49a5951f83557e8367ecbf52de7aea34d0cb73103180acc077e0afe9b6fe964816aa42c49d1cda339f
data/Gemfile CHANGED
@@ -6,6 +6,4 @@ git_source(:github) { |repo| "https://github.com/#{repo}" }
6
6
 
7
7
  gemspec
8
8
 
9
- if File.exist? "Gemfile.devel"
10
- eval File.read("Gemfile.devel"), nil, "Gemfile.devel" # rubocop:disable Security/Eval
11
- end
9
+ eval_gemfile("Gemfile.devel") rescue nil
data/lib/utils/image.rb CHANGED
@@ -1,187 +1,68 @@
1
1
  require "tempfile"
2
2
  require "marcel"
3
3
  require "base64"
4
+ require "image_size"
5
+ require_relative "namespace"
4
6
 
5
7
  module Metanorma
6
8
  module Utils
7
9
  class << self
8
- class Namespace
9
- def initialize(xmldoc)
10
- @namespace = xmldoc.root.namespace
11
- end
12
-
13
- def ns(path)
14
- return path if @namespace.nil?
15
-
16
- path.gsub(%r{/([a-zA-z])}, "/xmlns:\\1")
17
- .gsub(%r{::([a-zA-z])}, "::xmlns:\\1")
18
- .gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]* ?=)}, "[xmlns:\\1")
19
- .gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]*\])}, "[xmlns:\\1")
20
- end
21
- end
22
-
23
- def save_dataimage(uri)
24
- %r{^data:(?:image|application)/(?<imgtype>[^;]+);(?:charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri
25
- imgtype.sub!(/\+[a-z0-9]+$/, "") # svg+xml
26
- imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
27
- Tempfile.open(["image", ".#{imgtype}"]) do |f|
28
- f.binmode
29
- f.write(Base64.strict_decode64(imgdata))
30
- f.path
31
- end
32
- end
33
-
34
- SVG_NS = "http://www.w3.org/2000/svg".freeze
35
-
36
- def svgmap_rewrite(xmldoc, localdirectory = "")
37
- n = Namespace.new(xmldoc)
38
- xmldoc.xpath(n.ns("//svgmap")).each_with_index do |s, i|
39
- next unless svgmap_rewrite0(s, n, localdirectory, i)
40
- next if s.at(n.ns("./target/eref"))
41
-
42
- s.replace(s.at(n.ns("./figure")))
43
- end
44
- end
45
-
46
- def svgmap_rewrite0(svgmap, namespace, localdirectory, idx)
47
- if (i = svgmap.at(namespace.ns(".//image"))) &&
48
- (src = i["src"]) && !src.empty?
49
- path = svgmap_rewrite0_path(src, localdirectory)
50
- File.file?(path) or return false
51
- svg = Nokogiri::XML(File.read(path, encoding: "utf-8"))
52
- i.replace(svgmap_rewrite1(svgmap, svg.root, namespace, idx))
53
- /^data:/.match(src) and i["src"] = datauri(path)
54
- elsif i = svgmap.at(".//m:svg", "m" => SVG_NS)
55
- i.replace(svgmap_rewrite1(svgmap, i, namespace, idx))
56
- else return false
57
- end
58
- true
10
+ def image_resize(img, path, maxheight, maxwidth)
11
+ s, realsize = get_image_size(img, path)
12
+ img.name == "svg" && !img["viewBox"] && s[0] && s[1] and
13
+ img["viewBox"] = "0 0 #{s[0]} #{s[1]}"
14
+ s, skip = image_dont_resize(s, realsize)
15
+ skip and return s
16
+ s = image_size_fillin(s, realsize)
17
+ image_shrink(s, maxheight, maxwidth)
59
18
  end
60
19
 
61
- def svgmap_rewrite0_path(src, localdirectory)
62
- if /^data:/.match?(src)
63
- save_dataimage(src)
64
- else
65
- File.file?(src) ? src : localdirectory + src
66
- end
20
+ def image_dont_resize(dim, realsize)
21
+ dim.nil? and return [[nil, nil], true]
22
+ realsize.nil? and return [dim, true]
23
+ dim[0] == nil && dim[1] == nil and return [dim, true]
24
+ [dim, false]
67
25
  end
68
26
 
69
- def svgmap_rewrite1(svgmap, svg, namespace, idx)
70
- svg_update_href(svgmap, svg, namespace)
71
- svg_update_ids(svg, idx)
72
- svg.xpath("processing-instruction()|.//processing-instruction()").remove
73
- svg.to_xml
27
+ def image_size_fillin(dim, realsize)
28
+ dim[1].zero? && !dim[0].zero? and
29
+ dim[1] = dim[0] * realsize[1] / realsize[0]
30
+ dim[0].zero? && !dim[1].zero? and
31
+ dim[0] = dim[1] * realsize[0] / realsize[1]
32
+ dim
74
33
  end
75
34
 
76
- def svg_update_href(svgmap, svg, namespace)
77
- targ = svgmap_rewrite1_targets(svgmap, namespace)
78
- svg.xpath(".//m:a", "m" => SVG_NS).each do |a|
79
- ["xlink:href", "href"].each do |p|
80
- a[p] and x = targ[File.expand_path(a[p])] and a[p] = x
81
- end
82
- end
35
+ def image_shrink(dim, maxheight, maxwidth)
36
+ dim[1] > maxheight and
37
+ dim = [(dim[0] * maxheight / dim[1]).ceil, maxheight]
38
+ dim[0] > maxwidth and
39
+ dim = [maxwidth, (dim[1] * maxwidth / dim[0]).ceil]
40
+ dim
83
41
  end
84
42
 
85
- def svgmap_rewrite1_targets(svgmap, namespace)
86
- svgmap.xpath(namespace.ns("./target"))
87
- .each_with_object({}) do |t, m|
88
- x = t.at(namespace.ns("./xref")) and
89
- m[File.expand_path(t["href"])] = "##{x['target']}"
90
- x = t.at(namespace.ns("./link")) and
91
- m[File.expand_path(t["href"])] = x["target"]
92
- t.remove if t.at(namespace.ns("./xref | ./link"))
93
- end
43
+ def get_image_size(img, path)
44
+ realsize = ImageSize.path(path).size
45
+ s = image_size_interpret(img, realsize || [nil, nil])
46
+ image_size_zeroes_complete(s, realsize)
94
47
  end
95
48
 
96
- def svg_update_ids(svg, idx)
97
- ids = svg.xpath("./@id | .//@id")
98
- .each_with_object([]) { |i, m| m << i.value }
99
- return if ids.empty?
100
-
101
- svg_update_ids_attrs(svg, ids, idx)
102
- svg_update_ids_css(svg, ids, idx)
49
+ def image_size_interpret(img, realsize)
50
+ w = image_size_percent(img["width"], realsize[0])
51
+ h = image_size_percent(img["height"], realsize[1])
52
+ [w, h]
103
53
  end
104
54
 
105
- def svg_update_ids_attrs(svg, ids, idx)
106
- svg.xpath(". | .//*[@*]").each do |a|
107
- a.attribute_nodes.each do |x|
108
- ids.include?(x.value) and x.value += sprintf("_%09d", idx)
109
- end
110
- end
55
+ def image_size_percent(value, real)
56
+ /%$/.match?(value) && !real.nil? and
57
+ value = real * (value.sub(/%$/, "").to_f / 100)
58
+ value.to_i
111
59
  end
112
60
 
113
- def svg_update_ids_css(svg, ids, idx)
114
- svg.xpath("//m:style", "m" => SVG_NS).each do |s|
115
- c = s.children.to_xml
116
- ids.each do |i|
117
- c = c.gsub(%r[##{i}\b],
118
- sprintf("#%<id>s_%<idx>09d", id: i, idx: idx))
119
- .gsub(%r(\[id\s*=\s*['"]?#{i}['"]?\]),
120
- sprintf("[id='%<id>s_%<idx>09d']", id: i, idx: idx))
121
- end
122
- s.children = c
61
+ def image_size_zeroes_complete(dim, realsize)
62
+ if dim[0].zero? && dim[1].zero?
63
+ dim = realsize
123
64
  end
124
- end
125
-
126
- # sources/plantuml/plantuml20200524-90467-1iqek5i.png
127
- # already includes localdir
128
- # Check whether just the local path or the other specified relative path
129
- # works.
130
- def datauri(uri, local_dir = ".")
131
- (datauri?(uri) || url?(uri)) and return uri
132
- options = absolute_path?(uri) ? [uri] : [uri, File.join(local_dir, uri)]
133
- path = options.detect do |p|
134
- File.exist?(p) ? p : nil
135
- end
136
- path and return encode_datauri(path)
137
- warn "Image specified at `#{uri}` does not exist."
138
- uri # Return original provided location
139
- end
140
-
141
- def encode_datauri(path)
142
- return nil unless File.exist?(path)
143
-
144
- type = Marcel::MimeType.for(Pathname.new(path)) ||
145
- 'text/plain; charset="utf-8"'
146
-
147
- bin = File.binread(path)
148
- data = Base64.strict_encode64(bin)
149
- "data:#{type};base64,#{data}"
150
- rescue StandardError
151
- warn "Data-URI encoding of `#{path}` failed."
152
- nil
153
- end
154
-
155
- def datauri?(uri)
156
- /^data:/.match?(uri)
157
- end
158
-
159
- def url?(url)
160
- %r{^[A-Z]{2,}://}i.match?(url)
161
- end
162
-
163
- def absolute_path?(uri)
164
- %r{^/}.match?(uri) || %r{^[A-Z]:/}.match?(uri)
165
- end
166
-
167
- def decode_datauri(uri)
168
- %r{^data:(?<mimetype>[^;]+);base64,(?<mimedata>.+)$} =~ uri
169
- return nil unless mimetype && mimedata
170
-
171
- data = Base64.strict_decode64(mimedata)
172
- {
173
- type_declared: mimetype,
174
- type_detected: Marcel::MimeType.for(data, declared_type: mimetype),
175
- data: data,
176
- }
177
- end
178
-
179
- # FIXME: This method should ONLY return 1 type, remove Array wrapper
180
- def datauri2mime(uri)
181
- output = decode_datauri(uri)
182
- return nil unless output && output[:type_detected]
183
-
184
- [output[:type_detected]]
65
+ [dim, realsize]
185
66
  end
186
67
  end
187
68
  end
data/lib/utils/log.rb CHANGED
@@ -11,23 +11,32 @@ module Metanorma
11
11
  @mapid = {}
12
12
  end
13
13
 
14
- def add(category, loc, msg)
14
+ # severity: 0: abort; 1: serious; 2: not serious
15
+ def add(category, loc, msg, severity: 2)
15
16
  @novalid and return
16
17
  @log[category] ||= []
17
- item = create_entry(loc, msg)
18
+ item = create_entry(loc, msg, severity)
18
19
  @log[category] << item
19
20
  loc = loc.nil? ? "" : "(#{current_location(loc)}): "
20
21
  suppress_display?(category, loc, msg) or
21
22
  warn "#{category}: #{loc}#{msg}"
22
23
  end
23
24
 
25
+ def abort_messages
26
+ @log.values.each_with_object([]) do |v, m|
27
+ v.each do |e|
28
+ e[:severity].zero? and m << e[:message]
29
+ end
30
+ end
31
+ end
32
+
24
33
  def suppress_display?(category, _loc, _msg)
25
34
  ["Metanorma XML Syntax"].include?(category)
26
35
  end
27
36
 
28
- def create_entry(loc, msg)
37
+ def create_entry(loc, msg, severity)
29
38
  msg = msg.encode("UTF-8", invalid: :replace, undef: :replace)
30
- item = { location: current_location(loc),
39
+ item = { location: current_location(loc), severity: severity,
31
40
  message: msg, context: context(loc), line: line(loc, msg) }
32
41
  if item[:message].include?(" :: ")
33
42
  a = item[:message].split(" :: ", 2)
@@ -101,7 +110,11 @@ module Metanorma
101
110
  <<~HTML
102
111
  <html><head><title>#{file} errors</title>
103
112
  <meta charset="UTF-8"/>
104
- <style> pre { white-space: pre-wrap; } </style>
113
+ <style> pre { white-space: pre-wrap; }
114
+ thead th { font-weight: bold; background-color: aqua; }
115
+ .severity0 { font-weight: bold; background-color: lightpink }
116
+ .severity1 { font-weight: bold; }
117
+ .severity2 { } </style>
105
118
  </head><body><h1>#{file} errors</h1>
106
119
  HTML
107
120
  end
@@ -118,25 +131,31 @@ module Metanorma
118
131
  def write_key(file, key)
119
132
  file.puts <<~HTML
120
133
  <h2>#{key}</h2>\n<table border="1">
121
- <thead><th width="5%">Line</th><th width="20%">ID</th><th width="30%">Message</th><th width="45%">Context</th></thead>
134
+ <thead><th width="5%">Line</th><th width="20%">ID</th>
135
+ <th width="30%">Message</th><th width="40%">Context</th><th width="5%">Severity</th></thead>
122
136
  <tbody>
123
137
  HTML
124
138
  @log[key].sort_by { |a| [a[:line], a[:location], a[:message]] }
125
139
  .each do |n|
126
- write1(file, n)
140
+ write_entry(file, render_preproc_entry(n))
127
141
  end
128
142
  file.puts "</tbody></table>\n"
129
143
  end
130
144
 
131
- def write1(file, entry)
132
- line = entry[:line]
133
- line = nil if line == "000000"
134
- loc = loc_link(entry)
135
- msg = break_up_long_str(entry[:message], 10, 2)
145
+ def render_preproc_entry(entry)
146
+ ret = entry.dup
147
+ ret[:line] = nil if ret[:line] == "000000"
148
+ ret[:location] = loc_link(entry)
149
+ ret[:message] = break_up_long_str(entry[:message], 10, 2)
136
150
  .gsub(/`([^`]+)`/, "<code>\\1</code>")
137
- entry[:context] and context = entry[:context].split("\n").first(5)
151
+ ret[:context] = context_render(entry)
152
+ ret.compact
153
+ end
154
+
155
+ def context_render(entry)
156
+ entry[:context] or return nil
157
+ entry[:context].split("\n").first(5)
138
158
  .join("\n").gsub("><", "> <")
139
- write_entry(file, line, loc, msg, context)
140
159
  end
141
160
 
142
161
  def mapid(old, new)
@@ -160,10 +179,12 @@ module Metanorma
160
179
  Metanorma::Utils.break_up_long_str(str, threshold, punct)
161
180
  end
162
181
 
163
- def write_entry(file, line, loc, msg, context)
164
- context &&= @c.encode(break_up_long_str(context, 40, 2))
182
+ def write_entry(file, entry)
183
+ entry[:context] &&= @c.encode(break_up_long_str(entry[:context], 40, 2))
165
184
  file.print <<~HTML
166
- <tr><td>#{line}</td><th><code>#{loc}</code></th><td>#{msg}</td><td><pre>#{context}</pre></td></tr>
185
+ <tr class="severity#{entry[:severity]}">
186
+ <td>#{entry[:line]}</td><th><code>#{entry[:location]}</code></th>
187
+ <td>#{entry[:message]}</td><td><pre>#{entry[:context]}</pre></td><td>#{entry[:severity]}</td></tr>
167
188
  HTML
168
189
  end
169
190
  end
@@ -0,0 +1,26 @@
1
+ module Metanorma
2
+ module Utils
3
+ class << self
4
+ # NOTE: It was used in methods of an eigenclass of Metanorma::Utils.
5
+ # Not sure if it's still used somewhere but could be.
6
+ class Namespace
7
+ def initialize(xmldoc)
8
+ @namespace = xmldoc.root.namespace
9
+ end
10
+
11
+ def ns(path)
12
+ return path if @namespace.nil?
13
+
14
+ path.gsub(%r{/([a-zA-Z])}, "/xmlns:\\1")
15
+ .gsub(%r{::([a-zA-Z])}, "::xmlns:\\1")
16
+ .gsub(%r{\[([a-zA-Z][a-z0-9A-Z@/]* ?=)}, "[xmlns:\\1")
17
+ .gsub(%r{\[([a-zA-Z][a-z0-9A-Z@/]*\])}, "[xmlns:\\1")
18
+ end
19
+ end
20
+
21
+ def create_namespace(xmldoc)
22
+ Namespace.new(xmldoc)
23
+ end
24
+ end
25
+ end
26
+ end
data/lib/utils/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Metanorma
2
2
  module Utils
3
- VERSION = "1.6.4".freeze
3
+ VERSION = "1.7.0".freeze
4
4
  end
5
5
  end
@@ -30,6 +30,7 @@ Gem::Specification.new do |spec|
30
30
  spec.add_dependency "concurrent-ruby"
31
31
  spec.add_dependency "csv"
32
32
  spec.add_dependency "htmlentities", "~> 4.3.4"
33
+ spec.add_dependency "image_size", ">= 3.2.0"
33
34
  spec.add_dependency "marcel", "~> 1.0.0"
34
35
  spec.add_dependency "mime-types"
35
36
  spec.add_dependency "nokogiri", ">= 1.11"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: metanorma-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.4
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-11-20 00:00:00.000000000 Z
11
+ date: 2024-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: 4.3.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: image_size
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: 3.2.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: 3.2.0
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: marcel
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -324,6 +338,7 @@ files:
324
338
  - lib/utils/image.rb
325
339
  - lib/utils/log.rb
326
340
  - lib/utils/main.rb
341
+ - lib/utils/namespace.rb
327
342
  - lib/utils/version.rb
328
343
  - lib/utils/xml.rb
329
344
  - metanorma-utils.gemspec