metanorma-mpfd 0.0.2

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +17 -0
  4. data/CODE_OF_CONDUCT.md +74 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE +25 -0
  7. data/README.adoc +1 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/rspec +18 -0
  11. data/bin/setup +8 -0
  12. data/lib/asciidoctor/mpfd/biblio.rng +836 -0
  13. data/lib/asciidoctor/mpfd/converter.rb +204 -0
  14. data/lib/asciidoctor/mpfd/isodoc.rng +1041 -0
  15. data/lib/asciidoctor/mpfd/isostandard.rng +1001 -0
  16. data/lib/asciidoctor/mpfd/pdf.js +31 -0
  17. data/lib/asciidoctor/mpfd/rsd.rng +212 -0
  18. data/lib/asciidoctor/mpfd/section.rb +94 -0
  19. data/lib/asciidoctor/mpfd/version.rb +5 -0
  20. data/lib/asciidoctor/mpfd.rb +9 -0
  21. data/lib/isodoc/mpfd/html/header.html +184 -0
  22. data/lib/isodoc/mpfd/html/html_rsd_intro.html +8 -0
  23. data/lib/isodoc/mpfd/html/html_rsd_titlepage.html +58 -0
  24. data/lib/isodoc/mpfd/html/htmlstyle.scss +1094 -0
  25. data/lib/isodoc/mpfd/html/logo.jpg +0 -0
  26. data/lib/isodoc/mpfd/html/logo.svg +1 -0
  27. data/lib/isodoc/mpfd/html/mpfa-logo-no-text@4x.png +0 -0
  28. data/lib/isodoc/mpfd/html/mpfa-logo@4x.png +0 -0
  29. data/lib/isodoc/mpfd/html/rsd.scss +564 -0
  30. data/lib/isodoc/mpfd/html/scripts.html +82 -0
  31. data/lib/isodoc/mpfd/html/word_rsd_intro.html +3 -0
  32. data/lib/isodoc/mpfd/html/word_rsd_titlepage.html +42 -0
  33. data/lib/isodoc/mpfd/html/wordstyle.scss +1096 -0
  34. data/lib/isodoc/mpfd/html_convert.rb +370 -0
  35. data/lib/isodoc/mpfd/i18n-en.yaml +1 -0
  36. data/lib/isodoc/mpfd/metadata.rb +98 -0
  37. data/lib/isodoc/mpfd/pdf_convert.rb +367 -0
  38. data/lib/isodoc/mpfd/word_convert.rb +347 -0
  39. data/lib/metanorma/mpfd/processor.rb +43 -0
  40. data/lib/metanorma/mpfd.rb +7 -0
  41. data/lib/metanorma-mpfd.rb +11 -0
  42. data/metanorma-mpfd.gemspec +46 -0
  43. metadata +326 -0
@@ -0,0 +1,347 @@
1
+ require "isodoc"
2
+ require_relative "metadata"
3
+
4
+ module IsoDoc
5
+ module Mpfd
6
+ # A {Converter} implementation that generates Word output, and a document
7
+ # schema encapsulation of the document for validation
8
+
9
+ class WordConvert < IsoDoc::WordConvert
10
+ def rsd_html_path(file)
11
+ File.join(File.dirname(__FILE__), File.join("html", file))
12
+ end
13
+
14
+ def initialize(options)
15
+ super
16
+ @wordstylesheet = generate_css(rsd_html_path("wordstyle.scss"), false, default_fonts(options))
17
+ @standardstylesheet = generate_css(rsd_html_path("rsd.scss"), false, default_fonts(options))
18
+ @header = rsd_html_path("header.html")
19
+ @wordcoverpage = rsd_html_path("word_rsd_titlepage.html")
20
+ @wordintropage = rsd_html_path("word_rsd_intro.html")
21
+ @ulstyle = "l3"
22
+ @olstyle = "l2"
23
+ system "cp #{rsd_html_path('logo.jpg')} logo.jpg"
24
+ system "cp #{rsd_html_path('mpfa-logo-no-text@4x.png')} mpfa-logo-no-text@4x.png"
25
+ @files_to_delete << "logo.jpg"
26
+ @files_to_delete << "mpfa-logo-no-text@4x.png"
27
+ end
28
+
29
+ def default_fonts(options)
30
+ b = options[:bodyfont] ||
31
+ (options[:script] == "Hans" ? '"SimSun",serif' :
32
+ '"Arial",sans-serif')
33
+ h = options[:headerfont] ||
34
+ (options[:script] == "Hans" ? '"SimHei",sans-serif' :
35
+ '"Arial",sans-serif')
36
+ m = options[:monospacefont] || '"Courier New",monospace'
37
+ "$bodyfont: #{b};\n$headerfont: #{h};\n$monospacefont: #{m};\n"
38
+ end
39
+
40
+ def metadata_init(lang, script, labels)
41
+ @meta = Metadata.new(lang, script, labels)
42
+ end
43
+
44
+ def make_body(xml, docxml)
45
+ body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72" }
46
+ xml.body **body_attr do |body|
47
+ make_body1(body, docxml)
48
+ make_body2(body, docxml)
49
+ make_body3(body, docxml)
50
+ end
51
+ end
52
+
53
+ def make_body2(body, docxml)
54
+ body.div **{ class: "WordSection2" } do |div2|
55
+ info docxml, div2
56
+ div2.p { |p| p << "&nbsp;" } # placeholder
57
+ end
58
+ #body.br **{ clear: "all", style: "page-break-before:auto;mso-break-type:section-break;" }
59
+ section_break(body)
60
+ end
61
+
62
+ def title(isoxml, _out)
63
+ main = isoxml&.at(ns("//title[@language='en']"))&.text
64
+ set_metadata(:doctitle, main)
65
+ end
66
+
67
+ def generate_header(filename, dir)
68
+ return unless @header
69
+ template = Liquid::Template.parse(File.read(@header, encoding: "UTF-8"))
70
+ meta = @meta.get
71
+ meta[:filename] = filename
72
+ params = meta.map { |k, v| [k.to_s, v] }.to_h
73
+ File.open("header.html", "w") { |f| f.write(template.render(params)) }
74
+ @files_to_delete << "header.html"
75
+ "header.html"
76
+ end
77
+
78
+ def header_strip(h)
79
+ h = h.to_s.gsub(%r{<br/>}, " ").sub(/<\/?h[12][^>]*>/, "")
80
+ h1 = to_xhtml_fragment(h.dup)
81
+ h1.traverse do |x|
82
+ x.replace(" ") if x.name == "span" &&
83
+ /mso-tab-count/.match(x["style"])
84
+ x.remove if x.name == "span" && x["class"] == "MsoCommentReference"
85
+ x.remove if x.name == "a" && x["epub:type"] == "footnote"
86
+ x.replace(x.children) if x.name == "a"
87
+ end
88
+ from_xhtml(h1)
89
+ end
90
+
91
+ def info(isoxml, out)
92
+ @meta.security isoxml, out
93
+ super
94
+ end
95
+
96
+ def annex_name(annex, name, div)
97
+ div.h1 **{ class: "Annex" } do |t|
98
+ t << "#{get_anchors[annex['id']][:label]} "
99
+ t << "<b>#{name.text}</b>"
100
+ end
101
+ end
102
+
103
+ def annex_name_lbl(clause, num)
104
+ obl = l10n("(#{@inform_annex_lbl})")
105
+ obl = l10n("(#{@norm_annex_lbl})") if clause["obligation"] == "normative"
106
+ l10n("<b>#{@annex_lbl} #{num}</b> #{obl}")
107
+ end
108
+
109
+ def pre_parse(node, out)
110
+ out.pre node.text # content.gsub(/</, "&lt;").gsub(/>/, "&gt;")
111
+ end
112
+
113
+ def term_defs_boilerplate(div, source, term, preface)
114
+ if source.empty? && term.nil?
115
+ div << @no_terms_boilerplate
116
+ else
117
+ div << term_defs_boilerplate_cont(source, term)
118
+ end
119
+ end
120
+
121
+ def i18n_init(lang, script)
122
+ super
123
+ @annex_lbl = "Appendix"
124
+ end
125
+
126
+ def error_parse(node, out)
127
+ # catch elements not defined in ISO
128
+ case node.name
129
+ when "pre"
130
+ pre_parse(node, out)
131
+ when "keyword"
132
+ out.span node.text, **{ class: "keyword" }
133
+ else
134
+ super
135
+ end
136
+ end
137
+
138
+ def fileloc(loc)
139
+ File.join(File.dirname(__FILE__), loc)
140
+ end
141
+
142
+ def i18n_init(lang, script)
143
+ super
144
+ y = if lang == "en"
145
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-en.yaml"))
146
+ elsif lang == "zh" && script == "Hans"
147
+ YAML.load_file(File.join(File.dirname(__FILE__),
148
+ "i18n-zh-Hans.yaml"))
149
+ else
150
+ YAML.load_file(File.join(File.dirname(__FILE__), "i18n-zh-Hans.yaml"))
151
+ end
152
+ @labels = @labels.merge(y)
153
+ @clause_lbl = y["clause"]
154
+ end
155
+
156
+ def terms_defs_title(f)
157
+ return f&.at(ns("./title"))&.content
158
+ end
159
+
160
+ TERM_CLAUSE = "//preface/terms | "\
161
+ "//preface/clause[descendant::terms]".freeze
162
+
163
+
164
+ def terms_defs(isoxml, out, num)
165
+ f = isoxml.at(ns(TERM_CLAUSE)) or return num
166
+ out.div **attr_code(id: f["id"]) do |div|
167
+ clause_name(nil, terms_defs_title(f), div, nil)
168
+ f.elements.each do |e|
169
+ parse(e, div) unless %w{title source}.include? e.name
170
+ end
171
+ end
172
+ num
173
+ end
174
+
175
+ FRONT_CLAUSE = "//*[parent::preface]".freeze
176
+ #FRONT_CLAUSE = "//clause[parent::preface] | //terms[parent::preface]".freeze
177
+
178
+ def preface(isoxml, out)
179
+ isoxml.xpath(ns(FRONT_CLAUSE)).each do |c|
180
+ if c.name == "terms" then terms_defs isoxml, out, 0
181
+ else
182
+ out.div **attr_code(id: c["id"]) do |s|
183
+ clause_name(get_anchors[c['id']][:label],
184
+ c&.at(ns("./title"))&.content, s, nil)
185
+ c.elements.reject { |c1| c1.name == "title" }.each do |c1|
186
+ parse(c1, s)
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
192
+
193
+ def initial_anchor_names(d)
194
+ d.xpath(ns(FRONT_CLAUSE)).each do |c|
195
+ preface_names(c)
196
+ sequential_asset_names(c)
197
+ end
198
+ middle_section_asset_names(d)
199
+ clause_names(d, 0)
200
+ termnote_anchor_names(d)
201
+ termexample_anchor_names(d)
202
+ end
203
+
204
+
205
+ def middle(isoxml, out)
206
+ middle_title(out)
207
+ clause isoxml, out
208
+ annex isoxml, out
209
+ bibliography isoxml, out
210
+ end
211
+
212
+ def make_body2(body, docxml)
213
+ body.div **{ class: "WordSection2" } do |div2|
214
+ info docxml, div2
215
+ foreword docxml, div2
216
+ introduction docxml, div2
217
+ terms_defs docxml, div2, 0
218
+ div2.p { |p| p << "&nbsp;" } # placeholder
219
+ end
220
+ section_break(body)
221
+ end
222
+
223
+
224
+ def middle(isoxml, out)
225
+ middle_title(out)
226
+ clause isoxml, out
227
+ annex isoxml, out
228
+ bibliography isoxml, out
229
+ end
230
+
231
+ def termdef_parse(node, out)
232
+ set_termdomain("")
233
+ node.children.each { |n| parse(n, out) }
234
+ end
235
+
236
+ def annex_name_lbl(clause, num)
237
+ l10n("<b>#{@annex_lbl} #{num}</b>")
238
+ end
239
+
240
+ def clause_names(docxml, sect_num)
241
+ q = "//clause[parent::sections]"
242
+ @topnum = nil
243
+ lvl = 0
244
+ docxml.xpath(ns(q)).each do |c|
245
+ container_names(c, 0)
246
+ sect_num, lvl = sect_names(c, nil, sect_num, 0, lvl)
247
+ end
248
+ end
249
+
250
+ def container_names(clause, lvl)
251
+ if clause["container"]
252
+ @anchors[clause["id"]] =
253
+ { label: nil, xref: clause.at(ns("./title"))&.text, level: lvl+1 }
254
+ end
255
+ clause.xpath(ns("./clause | ./term | ./terms | "\
256
+ "./definitions")).each do |c|
257
+ container_names(c, clause["container"] ? lvl+1 : lvl)
258
+ end
259
+ end
260
+
261
+ def sect_names(clause, num, i, lvl, prev_lvl)
262
+ return i if clause.nil?
263
+ curr = i
264
+ if clause["container"]
265
+ retlvl = lvl+1
266
+ else
267
+ retlvl = lvl
268
+ i+=1
269
+ curr = i
270
+ name = num.nil? ? i.to_s : "#{num}.#{i}"
271
+ @anchors[clause["id"]] = { label: name, xref: l10n("#{@clause_lbl} #{name}"), level: lvl+1 }
272
+ end
273
+ prev = lvl
274
+ j = 0
275
+ clause.xpath(ns("./clause | ./term | ./terms | "\
276
+ "./definitions")).each do |c|
277
+ if clause["container"]
278
+ i, lvl = sect_names(c, num, i, lvl, lvl)
279
+ else
280
+ j, prev = sect_names(c, name, j, lvl+1, prev)
281
+ end
282
+ end
283
+ i = j if j >0
284
+ i = curr if lvl < prev
285
+ [i, prev]
286
+ end
287
+
288
+ def annex_naming(c, num, lvl, i)
289
+ if c["guidance"] then annex_names1(c, "#{num}E", lvl + 1)
290
+ else
291
+ i+= 1
292
+ annex_names1(c, "#{num}.#{i}", lvl + 1)
293
+ end
294
+ i
295
+ end
296
+
297
+ def annex_names(clause, num)
298
+ @anchors[clause["id"]] = { label: annex_name_lbl(clause, num),
299
+ xref: "#{@annex_lbl} #{num}", level: 1 }
300
+ i = 0
301
+ clause.xpath(ns("./clause")).each do |c|
302
+ i = annex_naming(c, num, 1, i)
303
+ end
304
+ hierarchical_asset_names(clause, num)
305
+ end
306
+
307
+ def annex_names1(clause, num, level)
308
+ @anchors[clause["id"]] = { label: num, xref: "#{@annex_lbl} #{num}",
309
+ level: level }
310
+ i = 0
311
+ clause.xpath(ns("./clause")).each do |c|
312
+ i = annex_naming(c, num, level, i)
313
+ end
314
+ end
315
+
316
+ def clause(isoxml, out)
317
+ isoxml.xpath(ns(MIDDLE_CLAUSE)).each do |c|
318
+ out.div **attr_code(id: c["id"]) do |s|
319
+ clause_name(get_anchors[c['id']][:label],
320
+ c&.at(ns("./title"))&.content, s, class: c["container"] ? "containerhdr" : nil )
321
+ c.elements.reject { |c1| c1.name == "title" }.each do |c1|
322
+ parse(c1, s)
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ def clause_parse_title(node, div, c1, out)
329
+ if node["inline-header"] == "true"
330
+ inline_header_title(out, node, c1)
331
+ else
332
+ attrs = { class: node["container"] ? "containerhdr" : nil }
333
+ div.send "h#{get_anchors[node['id']][:level]}", **attr_code(attrs) do |h|
334
+ lbl = get_anchors[node['id']][:label]
335
+ h << "#{lbl}. " if lbl
336
+ c1&.children&.each { |c2| parse(c2, h) }
337
+ end
338
+ end
339
+ end
340
+
341
+ def ol_depth(node)
342
+ ol_style(node["type"])
343
+ end
344
+
345
+ end
346
+ end
347
+ end
@@ -0,0 +1,43 @@
1
+ require "metanorma/processor"
2
+
3
+ module Metanorma
4
+ module Mpfd
5
+ class Processor < Metanorma::Processor
6
+
7
+ def initialize
8
+ @short = :mpfd
9
+ @input_format = :asciidoc
10
+ @asciidoctor_backend = :mpfd
11
+ end
12
+
13
+ def output_formats
14
+ super.merge(
15
+ html: "html",
16
+ doc: "doc",
17
+ pdf: "pdf"
18
+ )
19
+ end
20
+
21
+ def version
22
+ "Asciidoctor::Mpfd #{Asciidoctor::Mpfd::VERSION}"
23
+ end
24
+
25
+ def input_to_isodoc(file)
26
+ Metanorma::Input::Asciidoc.new.process(file, @asciidoctor_backend)
27
+ end
28
+
29
+ def output(isodoc_node, outname, format, options={})
30
+ case format
31
+ when :html
32
+ IsoDoc::Mpfd::HtmlConvert.new(options).convert(outname, isodoc_node)
33
+ when :doc
34
+ IsoDoc::Mpfd::WordConvert.new(options).convert(outname, isodoc_node)
35
+ when :pdf
36
+ IsoDoc::Mpfd::PdfConvert.new(options).convert(outname, isodoc_node)
37
+ else
38
+ super
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,7 @@
1
+ require_relative "./mpfd/processor"
2
+
3
+ module Metanorma
4
+ module Mpfd
5
+
6
+ end
7
+ end
@@ -0,0 +1,11 @@
1
+ require "asciidoctor" unless defined? Asciidoctor::Converter
2
+ require_relative "asciidoctor/mpfd/converter"
3
+ require_relative "isodoc/mpfd/html_convert"
4
+ require_relative "isodoc/mpfd/word_convert"
5
+ require_relative "isodoc/mpfd/pdf_convert"
6
+ require_relative "asciidoctor/mpfd/version"
7
+
8
+ if defined? Metanorma
9
+ require_relative "metanorma/mpfd"
10
+ Metanorma::Registry.instance.register(Metanorma::Mpfd::Processor)
11
+ end
@@ -0,0 +1,46 @@
1
+ lib = File.expand_path("../lib", __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "asciidoctor/mpfd/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "metanorma-mpfd"
7
+ spec.version = Asciidoctor::Mpfd::VERSION
8
+ spec.authors = ["Ribose Inc."]
9
+ spec.email = ["open.source@ribose.com"]
10
+
11
+ spec.summary = "metanorma-mpfd lets you write MPF documents in AsciiDoc."
12
+ spec.description = <<~DESCRIPTION
13
+ metanorma-mpfd lets you write MPF documents in AsciiDoc syntax.
14
+
15
+ This gem is in active development.
16
+ DESCRIPTION
17
+
18
+ spec.homepage = "https://github.com/riboseinc/metanorma-mpfd"
19
+ spec.license = "BSD-2-Clause"
20
+
21
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
22
+ f.match(%r{^(test|spec|features)/})
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "asciidoctor", "~> 1.5.7"
29
+ spec.add_dependency "htmlentities", "~> 4.3.4"
30
+ spec.add_dependency "nokogiri"
31
+ spec.add_dependency "asciidoctor-iso", "~> 0.10.1"
32
+ spec.add_dependency "isodoc", ">= 0.8.4"
33
+ spec.add_dependency "twitter_cldr"
34
+
35
+ spec.add_development_dependency "bundler", "~> 1.15"
36
+ spec.add_development_dependency "byebug", "~> 9.1"
37
+ spec.add_development_dependency "equivalent-xml", "~> 0.6"
38
+ spec.add_development_dependency "guard", "~> 2.14"
39
+ spec.add_development_dependency "guard-rspec", "~> 4.7"
40
+ spec.add_development_dependency "rake", "~> 12.0"
41
+ spec.add_development_dependency "rspec", "~> 3.6"
42
+ spec.add_development_dependency "rubocop", "~> 0.50"
43
+ spec.add_development_dependency "simplecov", "~> 0.15"
44
+ spec.add_development_dependency "timecop", "~> 0.9"
45
+ spec.add_development_dependency "metanorma", "~> 0.2.6"
46
+ end