metanorma-mpfd 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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