isodoc 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c60f8eb9fcf49c289d99f64d901511387ae9d928a93c20ec81c011eb536f8a6a
4
- data.tar.gz: f4c33c72526c543b3d7c4929a20dfaa2413e9d007b8d2315aed424e156ad4061
3
+ metadata.gz: aed515059a4bfbb122ff29141ec687c3fab6fed9539fde9e2fc6d89b54ddc6ec
4
+ data.tar.gz: 35c681874fc12a48baa1ceffaff00680a577c90cde69c58caf896eeab8c8abb4
5
5
  SHA512:
6
- metadata.gz: bd64d2a35ac86b7b70d46b4ea3631070291b32ee014199d1bc395c5bf33c130d41527afcce573fddd49dc96a4f97d77a6e21a81ed45afec325ce2da61ad4c6ed
7
- data.tar.gz: 758249109707c1a6132311e2706f9b791799960f063546cdc8d125f36919845e03bfc5546d2a68d47c30f782fd20b3de1143e66e88a53b5b5bb766e52407a2a7
6
+ metadata.gz: 2313eec736db0f20d8ce87a8505e49a263d93ec67ee82d28d76f07c12388bce1ac213f380677784e1a9dacc12e78b655d39ecada99745f06e7e4a91f71124458
7
+ data.tar.gz: d680c24a277d386ab74cce9c87d1c0ff27087db33f4db42c973b3f51b5e098cae7a7d66aeb61dad50c36bf3ca23d7b9befe718584b3311f3f6ab971893872027
data/isodoc.gemspec CHANGED
@@ -34,15 +34,14 @@ Gem::Specification.new do |spec|
34
34
  spec.add_dependency "asciimath"
35
35
  spec.add_dependency "html2doc", "~> 1.4.1"
36
36
  spec.add_dependency "htmlentities", "~> 4.3.4"
37
- spec.add_dependency "isodoc-i18n", "~> 1.0.0"
37
+ spec.add_dependency "isodoc-i18n", "~> 1.0.6"
38
38
  spec.add_dependency "liquid", "~> 4"
39
39
  # spec.add_dependency "metanorma", ">= 1.2.0"
40
40
  spec.add_dependency "emf2svg"
41
41
  spec.add_dependency "mathml2asciimath"
42
- spec.add_dependency "metanorma-utils"
43
-
42
+ spec.add_dependency "mn-requirements", "~> 0.0.1"
44
43
  spec.add_dependency "relaton-cli"
45
- spec.add_dependency "relaton-render", ">= 0.3.1"
44
+ spec.add_dependency "relaton-render", "~> 0.4.0"
46
45
  spec.add_dependency "roman-numerals"
47
46
  spec.add_dependency "thread_safe"
48
47
  spec.add_dependency "twitter_cldr", ">= 6.6.0"
@@ -13,10 +13,7 @@ module IsoDoc
13
13
  end
14
14
 
15
15
  def ns(xpath)
16
- xpath.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")
16
+ Metanorma::Utils::ns(xpath)
20
17
  end
21
18
 
22
19
  def liquid(doc)
@@ -5,11 +5,13 @@ require "fileutils"
5
5
  require "tempfile"
6
6
  require_relative "i18n"
7
7
  require_relative "css"
8
+ require_relative "init"
8
9
  require "securerandom"
10
+ require "mn-requirements"
9
11
 
10
12
  module IsoDoc
11
13
  class Convert < ::IsoDoc::Common
12
- attr_accessor :options, :i18n, :meta, :xrefs
14
+ attr_accessor :options, :i18n, :meta, :xrefs, :reqt_models
13
15
 
14
16
  # htmlstylesheet: Generic stylesheet for HTML
15
17
  # htmlstylesheet_override: Override stylesheet for HTML
@@ -45,22 +47,28 @@ module IsoDoc
45
47
  # tocrecommendations: add ToC for rcommendations
46
48
  # fonts: fontist fonts to install
47
49
  # fontlicenseagreement: fontist font license agreement
48
- def initialize(options)
49
- @libdir ||= File.dirname(__FILE__) # rubocop:disable Lint/DisjunctiveAssignmentInConstructor
50
+ def initialize(options) # rubocop:disable Lint/MissingSuper
51
+ @options = options_preprocess(options)
52
+ init_stylesheets(@options)
53
+ init_covers(@options)
54
+ init_toc(@options)
55
+ init_fonts(@options)
56
+ init_processing
57
+ init_locations(@options)
58
+ init_i18n(@options)
59
+ init_rendering(@options)
60
+ end
61
+
62
+ def options_preprocess(options)
50
63
  options.merge!(default_fonts(options)) do |_, old, new|
51
64
  old || new
52
65
  end.merge!(default_file_locations(options)) do |_, old, new|
53
66
  old || new
54
67
  end
55
- @options = options
56
- @files_to_delete = []
57
- @tempfile_cache = []
58
- @sourcefilename = options[:sourcefilename]
59
- init_stylesheets(options)
60
- init_covers(options)
61
- init_toc(options)
62
- init_fonts(options)
63
- @i18nyaml = options[:i18nyaml]
68
+ options
69
+ end
70
+
71
+ def init_rendering(options)
64
72
  @ulstyle = options[:ulstyle]
65
73
  @olstyle = options[:olstyle]
66
74
  @datauriimage = options[:datauriimage]
@@ -69,6 +77,26 @@ module IsoDoc
69
77
  @sectionsplit = options[:sectionsplit] == "true"
70
78
  @suppressasciimathdup = options[:suppressasciimathdup] == "true"
71
79
  @bare = options[:bare]
80
+ @aligncrosselements = options[:aligncrosselements]
81
+ end
82
+
83
+ def init_i18n(options)
84
+ @i18nyaml = options[:i18nyaml]
85
+ @lang = options[:language] || "en"
86
+ @script = options[:script] || "Latn"
87
+ end
88
+
89
+ def init_locations(options)
90
+ @libdir ||= File.dirname(__FILE__)
91
+ @baseassetpath = options[:baseassetpath]
92
+ @tmpimagedir_suffix = tmpimagedir_suffix
93
+ @tmpfilesdir_suffix = tmpfilesdir_suffix
94
+ @sourcefilename = options[:sourcefilename]
95
+ @files_to_delete = []
96
+ @tempfile_cache = []
97
+ end
98
+
99
+ def init_processing
72
100
  @termdomain = ""
73
101
  @termexample = false
74
102
  @note = false
@@ -83,16 +111,10 @@ module IsoDoc
83
111
  @c = HTMLEntities.new
84
112
  @openmathdelim = "`"
85
113
  @closemathdelim = "`"
86
- @lang = options[:language] || "en"
87
- @script = options[:script] || "Latn"
88
114
  @maxwidth = 1200
89
115
  @maxheight = 800
90
116
  @bookmarks_allocated = { "X" => true }
91
117
  @fn_bookmarks = {}
92
- @baseassetpath = options[:baseassetpath]
93
- @aligncrosselements = options[:aligncrosselements]
94
- @tmpimagedir_suffix = tmpimagedir_suffix
95
- @tmpfilesdir_suffix = tmpfilesdir_suffix
96
118
  end
97
119
 
98
120
  def init_fonts(options)
@@ -171,35 +193,27 @@ module IsoDoc
171
193
  end
172
194
  end
173
195
 
174
- def metadata_init(lang, script, i18n)
175
- @meta = Metadata.new(lang, script, i18n)
176
- end
177
-
178
- def xref_init(lang, script, _klass, i18n, options)
179
- html = HtmlConvert.new(language: @lang, script: @script)
180
- @xrefs = Xref.new(lang, script, html, i18n, options)
181
- end
182
-
183
- def i18n_init(lang, script, i18nyaml = nil)
184
- @i18n = I18n.new(lang, script, i18nyaml: i18nyaml || @i18nyaml)
185
- end
186
-
187
- def l10n(expr, lang = @lang, script = @script)
188
- @i18n.l10n(expr, lang, script)
189
- end
190
-
191
196
  def convert_init(file, input_filename, debug)
192
197
  docxml = Nokogiri::XML(file) { |config| config.huge }
193
198
  filename, dir = init_file(input_filename, debug)
194
199
  docxml.root.default_namespace = ""
195
- lang = docxml&.at(ns("//bibdata/language"))&.text and @lang = lang
196
- script = docxml&.at(ns("//bibdata/script"))&.text and @script = script
197
- i18n_init(@lang, @script)
200
+ convert_i18n_init(docxml)
198
201
  metadata_init(@lang, @script, @i18n)
199
202
  xref_init(@lang, @script, self, @i18n, {})
200
203
  [docxml, filename, dir]
201
204
  end
202
205
 
206
+ def convert_i18n_init(docxml)
207
+ lang = docxml&.at(ns("//bibdata/language"))&.text and @lang = lang
208
+ script = docxml&.at(ns("//bibdata/script"))&.text and @script = script
209
+ i18n_init(@lang, @script)
210
+ @reqt_models = Metanorma::Requirements
211
+ .new({
212
+ default: "default", lang: lang, script: script,
213
+ labels: @i18n.get
214
+ })
215
+ end
216
+
203
217
  def convert(input_filename, file = nil, debug = false,
204
218
  output_filename = nil)
205
219
  file = File.read(input_filename, encoding: "utf-8") if file.nil?
@@ -219,10 +233,8 @@ module IsoDoc
219
233
  end
220
234
 
221
235
  def target_pdf(node)
222
- if /#/.match?(node["target"])
223
- node["target"].sub(/#/, ".pdf#")
224
- else
225
- "##{node['target']}"
236
+ if /#/.match?(node["target"]) then node["target"].sub(/#/, ".pdf#")
237
+ else "##{node['target']}"
226
238
  end
227
239
  end
228
240
  end
@@ -120,7 +120,7 @@ module IsoDoc
120
120
  def omit_docid_prefix(prefix)
121
121
  return true if prefix.nil? || prefix.empty?
122
122
 
123
- %w(ISO IEC IEV ITU W3C csd metanorma repository metanorma-ordinal)
123
+ %w(ISO IEC IEV ITU W3C BIPM csd metanorma repository metanorma-ordinal)
124
124
  .include? prefix
125
125
  end
126
126
 
@@ -1,121 +1,50 @@
1
1
  module IsoDoc
2
2
  module Function
3
3
  module Blocks
4
- def recommendation_labels(node)
5
- [node.at(ns("./label")), node.at(ns("./title")), node.at(ns("./name"))]
6
- end
7
-
8
- def recommendation_name(node, out, _type)
9
- label, title, lbl = recommendation_labels(node)
10
- out.p **{ class: "RecommendationTitle" } do |b|
11
- lbl&.children&.each { |n| parse(n, b) }
12
- b << l10n(":")
13
- if label || title
14
- b.br
15
- label&.children&.each { |n| parse(n, b) }
16
- b << "#{clausedelim} " if label && title
17
- title&.children&.each { |n| parse(n, b) }
18
- end
19
- end
20
- end
21
-
22
- def recommendation_attributes1(node)
23
- out = []
24
- oblig = node["obligation"] and
25
- out << l10n("#{@i18n.obligation}: #{oblig}")
26
- node.xpath(ns("./subject")).each do |subj|
27
- out << l10n("#{@i18n.subject}: #{subj.text}")
28
- end
29
- node.xpath(ns("./inherit")).each do |i|
30
- out << recommendation_attr_parse(i, @i18n.inherits)
31
- end
32
- node.xpath(ns("./classification")).each do |c|
33
- line = recommendation_attr_keyvalue(c, "tag", "value") and out << line
34
- end
35
- out
36
- end
37
-
38
- def recommendation_attr_parse(node, label)
39
- noko do |xml|
40
- xml << "#{label}: "
41
- node.children.each { |n| parse(n, xml) }
42
- end.join
43
- end
4
+ def recommendation_name(name, out)
5
+ return if name.nil?
44
6
 
45
- def recommendation_attr_keyvalue(node, key, value)
46
- tag = node.at(ns("./#{key}")) or return nil
47
- value = node.at(ns("./#{value}")) or return nil
48
- "#{tag.text.capitalize}: #{value.text}"
49
- end
50
-
51
- def recommendation_attributes(node, out)
52
- ret = recommendation_attributes1(node)
53
- return if ret.empty?
54
-
55
- out.p do |p|
56
- p.i do |i|
57
- i << ret.join("<br/>")
58
- end
7
+ out.p **{ class: "RecommendationTitle" } do |p|
8
+ name.children.each { |n| parse(n, p) }
59
9
  end
60
10
  end
61
11
 
62
- def reqt_metadata_node(node)
63
- %w(label title subject classification tag value
64
- inherit name).include? node.name
65
- end
66
-
67
12
  def reqt_attrs(node, klass)
68
13
  attr_code(class: klass, id: node["id"], style: keep_style(node))
69
14
  end
70
15
 
71
16
  def recommendation_parse(node, out)
72
17
  out.div **reqt_attrs(node, "recommend") do |t|
73
- recommendation_name(node, t, @i18n.recommendation)
74
- recommendation_attributes(node, out)
75
- node.children.each do |n|
76
- parse(n, t) unless reqt_metadata_node(n)
77
- end
18
+ recommendation_parse1(node, t)
19
+ end
20
+ end
21
+
22
+ def recommendation_parse1(node, out)
23
+ recommendation_name(node.at(ns("./name")), out)
24
+ node.children.each do |n|
25
+ parse(n, out) unless n.name == "name"
78
26
  end
79
27
  end
80
28
 
81
29
  def requirement_parse(node, out)
82
30
  out.div **reqt_attrs(node, "require") do |t|
83
- recommendation_name(node, t, @i18n.requirement)
84
- recommendation_attributes(node, out)
85
- node.children.each do |n|
86
- parse(n, t) unless reqt_metadata_node(n)
87
- end
31
+ recommendation_parse1(node, t)
88
32
  end
89
33
  end
90
34
 
91
35
  def permission_parse(node, out)
92
36
  out.div **reqt_attrs(node, "permission") do |t|
93
- recommendation_name(node, t, @i18n.permission)
94
- recommendation_attributes(node, out)
95
- node.children.each do |n|
96
- parse(n, t) unless reqt_metadata_node(n)
97
- end
37
+ recommendation_parse1(node, t)
98
38
  end
99
39
  end
100
40
 
101
- def reqt_component_attrs(node)
102
- klass = node.name
103
- klass == "component" and klass = node["class"]
104
- attr_code(class: "requirement-#{klass}",
105
- style: keep_style(node))
106
- end
107
-
108
- def requirement_component_parse(node, out)
109
- return if node["exclude"] == "true"
110
-
111
- out.div **reqt_component_attrs(node) do |div|
41
+ def div_parse(node, out)
42
+ out.div **reqt_attrs(node, node["type"]) do |t|
112
43
  node.children.each do |n|
113
- parse(n, div)
44
+ parse(n, t)
114
45
  end
115
46
  end
116
47
  end
117
-
118
- def requirement_skip_parse(node, out); end
119
48
  end
120
49
  end
121
50
  end
@@ -204,11 +204,12 @@ module IsoDoc
204
204
  when "requirement" then requirement_parse(node, out)
205
205
  when "recommendation" then recommendation_parse(node, out)
206
206
  when "permission" then permission_parse(node, out)
207
- when "subject", "classification"
208
- requirement_skip_parse(node, out)
209
- when "inherit", "description", "specification", "measurement-target",
210
- "verification", "import", "component"
211
- requirement_component_parse(node, out)
207
+ when "div" then div_parse(node, out)
208
+ #when "subject", "classification"
209
+ # requirement_skip_parse(node, out)
210
+ #when "inherit", "description", "specification", "measurement-target",
211
+ #"verification", "import", "component"
212
+ # requirement_component_parse(node, out)
212
213
  when "index" then index_parse(node, out)
213
214
  when "index-xref" then index_xref_parse(node, out)
214
215
  when "termref" then termrefelem_parse(node, out)
@@ -222,6 +222,10 @@ module IsoDoc
222
222
  application/x-msmetafile image/x-xbitmap).include? type
223
223
  end
224
224
 
225
+ def eps?(type)
226
+ %w(application/postscript image/x-eps).include? type
227
+ end
228
+
225
229
  def cleanup_entities(text, is_xml: true)
226
230
  c = HTMLEntities.new
227
231
  if is_xml
@@ -0,0 +1,20 @@
1
+ module IsoDoc
2
+ class Convert < ::IsoDoc::Common
3
+ def metadata_init(lang, script, i18n)
4
+ @meta = Metadata.new(lang, script, i18n)
5
+ end
6
+
7
+ def xref_init(lang, script, _klass, i18n, options)
8
+ html = HtmlConvert.new(language: @lang, script: @script)
9
+ @xrefs = Xref.new(lang, script, html, i18n, options)
10
+ end
11
+
12
+ def i18n_init(lang, script, i18nyaml = nil)
13
+ @i18n = I18n.new(lang, script, i18nyaml: i18nyaml || @i18nyaml)
14
+ end
15
+
16
+ def l10n(expr, lang = @lang, script = @script)
17
+ @i18n.l10n(expr, lang, script)
18
+ end
19
+ end
20
+ end
@@ -45,7 +45,6 @@ module IsoDoc
45
45
  end
46
46
  end
47
47
 
48
- # introduce name element
49
48
  def formula1(elem)
50
49
  lbl = @xrefs.anchor(elem["id"], :label, false)
51
50
  prefix_name(elem, "", lbl, "name")
@@ -73,7 +72,6 @@ module IsoDoc
73
72
  end
74
73
  end
75
74
 
76
- # introduce name element
77
75
  def note1(elem)
78
76
  return if elem.parent.name == "bibitem" || elem["notag"] == "true"
79
77
 
@@ -116,11 +114,10 @@ module IsoDoc
116
114
  end
117
115
  end
118
116
 
119
- # introduce name element
120
117
  def recommendation1(elem, type)
121
- n = @xrefs.anchor(elem["id"], :label, false)
122
- lbl = (n.nil? ? type : l10n("#{type} #{n}"))
123
- prefix_name(elem, "", lbl, "name")
118
+ lbl = @reqt_models.model(elem["model"])
119
+ .recommendation_label(elem, type, xrefs)
120
+ prefix_name(elem, "", l10n(lbl), "name")
124
121
  end
125
122
 
126
123
  def table(docxml)
@@ -175,5 +172,25 @@ module IsoDoc
175
172
  def ol1(elem)
176
173
  elem["type"] ||= ol_depth(elem).to_s
177
174
  end
175
+
176
+ def requirement_render_preprocessing(docxml); end
177
+
178
+ REQS = %w(requirement recommendation permission).freeze
179
+
180
+ def requirement_render(docxml)
181
+ requirement_render_preprocessing(docxml)
182
+ REQS.each do |x|
183
+ REQS.each do |y|
184
+ docxml.xpath(ns("//#{x}//#{y}")).each { |r| requirement_render1(r) }
185
+ end
186
+ end
187
+ docxml.xpath(ns("//requirement | //recommendation | //permission"))
188
+ .each { |r| requirement_render1(r) }
189
+ end
190
+
191
+ def requirement_render1(node)
192
+ node.replace(@reqt_models.model(node["model"])
193
+ .requirement_render1(node))
194
+ end
178
195
  end
179
196
  end
@@ -11,7 +11,14 @@ module IsoDoc
11
11
  else s.remove
12
12
  end
13
13
  end
14
- docxml.xpath(ns("//image")).each { |f| svg_emf_double(f) }
14
+ imageconvert(docxml)
15
+ end
16
+
17
+ def imageconvert(docxml)
18
+ docxml.xpath(ns("//image")).each do |f|
19
+ eps2svg(f)
20
+ svg_emf_double(f)
21
+ end
15
22
  end
16
23
 
17
24
  def svg_extract(elem)
@@ -36,6 +43,16 @@ module IsoDoc
36
43
  l10n("#{lower2cap @i18n.figure} #{lbl}"), "name")
37
44
  end
38
45
 
46
+ def eps2svg(img)
47
+ return unless eps?(img["mimetype"])
48
+
49
+ img["mimetype"] = "image/svg+xml"
50
+ if src = eps_to_svg(img)
51
+ img["src"] = src
52
+ img.children = ""
53
+ end
54
+ end
55
+
39
56
  def svg_emf_double(img)
40
57
  if emf?(img["mimetype"])
41
58
  img = emf_encode(img)
@@ -46,40 +63,59 @@ module IsoDoc
46
63
  end
47
64
 
48
65
  def emf_encode(img)
49
- img["mimetype"] = "image/svg+xml"
50
- unless %r{^data:image}.match?(img["src"])
51
- img["src"] = Metanorma::Utils::datauri(img["src"])
52
- end
66
+ svg_prep(img)
53
67
  img.children = "<emf src='#{img['src']}'/>"
54
68
  img["src"] = ""
55
69
  img
56
70
  end
57
71
 
72
+ def svg_prep(img)
73
+ img["mimetype"] = "image/svg+xml"
74
+ %r{^data:image}.match?(img["src"]) or
75
+ img["src"] = Metanorma::Utils::datauri(img["src"])
76
+ end
77
+
58
78
  def emf_to_svg(img)
59
79
  emf = Metanorma::Utils::save_dataimage(img.at(ns("./emf/@src")).text)
60
80
  Emf2svg.from_file(emf).sub(/<\?[^>]+>/, "")
61
81
  end
62
82
 
83
+ def eps_to_svg(node)
84
+ uri = eps_to_svg_uri(node)
85
+ ret = imgfile_suffix(uri, "svg")
86
+ File.exist?(ret) and return ret
87
+ inkscape_convert(uri, ret, "--export-plain-svg")
88
+ end
89
+
63
90
  def svg_to_emf(node)
64
91
  uri = svg_to_emf_uri(node)
65
- ret = svg_to_emf_filename(uri)
92
+ ret = imgfile_suffix(uri, "emf")
66
93
  File.exist?(ret) and return ret
94
+ inkscape_convert(uri, ret, '--export-type="emf"')
95
+ end
96
+
97
+ def inkscape_convert(uri, file, option)
67
98
  exe = inkscape_installed? or raise "Inkscape missing in PATH, unable" \
68
- "to convert EMF to SVG. Aborting."
99
+ "to convert image #{uri}. Aborting."
69
100
  uri = Metanorma::Utils::external_path uri
70
101
  exe = Metanorma::Utils::external_path exe
71
- system(%(#{exe} --export-type="emf" #{uri})) and
72
- return Metanorma::Utils::datauri(ret)
102
+ system(%(#{exe} #{option} #{uri})) and
103
+ return Metanorma::Utils::datauri(file)
73
104
 
74
- raise %(Fail on #{exe} --export-type="emf" #{uri})
105
+ raise %(Fail on #{exe} #{option} #{uri})
75
106
  end
76
107
 
77
108
  def svg_to_emf_uri(node)
78
- uri = if node&.elements&.first&.name == "svg"
79
- a = Base64.strict_encode64(node.children.to_xml)
80
- "data:image/svg+xml;base64,#{a}"
81
- else node["src"]
82
- end
109
+ uri = svg_to_emf_uri_convert(node)
110
+ cache_dataimage(uri)
111
+ end
112
+
113
+ def eps_to_svg_uri(node)
114
+ uri = eps_to_svg_uri_convert(node)
115
+ cache_dataimage(uri)
116
+ end
117
+
118
+ def cache_dataimage(uri)
83
119
  if %r{^data:}.match?(uri)
84
120
  uri = save_dataimage(uri)
85
121
  @tempfile_cache << uri
@@ -87,12 +123,25 @@ module IsoDoc
87
123
  uri
88
124
  end
89
125
 
90
- def svg_to_emf_filename(uri)
91
- "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.emf"
126
+ def svg_to_emf_uri_convert(node)
127
+ if node&.elements&.first&.name == "svg"
128
+ a = Base64.strict_encode64(node.children.to_xml)
129
+ "data:image/svg+xml;base64,#{a}"
130
+ else node["src"]
131
+ end
132
+ end
133
+
134
+ def eps_to_svg_uri_convert(node)
135
+ if node.text.strip.empty?
136
+ node["src"]
137
+ else
138
+ a = Base64.strict_encode64(node.children.to_xml)
139
+ "data:application/postscript;base64,#{a}"
140
+ end
92
141
  end
93
142
 
94
- def emf_to_svgfilename(uri)
95
- "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.svg"
143
+ def imgfile_suffix(uri, suffix)
144
+ "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.#{suffix}"
96
145
  end
97
146
 
98
147
  def inkscape_installed?