metanorma 1.7.6 → 2.0.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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +5 -1
  3. data/lib/metanorma/array_monkeypatch.rb +9 -0
  4. data/lib/metanorma/asciidoctor_extensions/glob_include_processor.rb +13 -11
  5. data/lib/metanorma/collection/collection.rb +225 -0
  6. data/lib/metanorma/collection/config/bibdata.rb +12 -0
  7. data/lib/metanorma/collection/config/compile_options.rb +13 -0
  8. data/lib/metanorma/collection/config/config.rb +163 -0
  9. data/lib/metanorma/collection/config/converters.rb +30 -0
  10. data/lib/metanorma/collection/config/directive.rb +10 -0
  11. data/lib/metanorma/collection/config/manifest.rb +88 -0
  12. data/lib/metanorma/collection/document/document.rb +133 -0
  13. data/lib/metanorma/collection/filelookup/filelookup.rb +250 -0
  14. data/lib/metanorma/collection/filelookup/filelookup_sectionsplit.rb +87 -0
  15. data/lib/metanorma/collection/manifest/manifest.rb +237 -0
  16. data/lib/metanorma/collection/renderer/fileparse.rb +247 -0
  17. data/lib/metanorma/collection/renderer/fileprocess.rb +173 -0
  18. data/lib/metanorma/collection/renderer/navigation.rb +133 -0
  19. data/lib/metanorma/collection/renderer/render_word.rb +133 -0
  20. data/lib/metanorma/collection/renderer/renderer.rb +157 -0
  21. data/lib/metanorma/collection/renderer/utils.rb +183 -0
  22. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +218 -0
  23. data/lib/metanorma/collection/util/disambig_files.rb +37 -0
  24. data/lib/metanorma/collection/util/util.rb +72 -0
  25. data/lib/metanorma/collection/xrefprocess/xrefprocess.rb +222 -0
  26. data/lib/metanorma/{compile.rb → compile/compile.rb} +17 -11
  27. data/lib/metanorma/{compile_options.rb → compile/compile_options.rb} +9 -5
  28. data/lib/metanorma/{compile_validate.rb → compile/compile_validate.rb} +1 -1
  29. data/lib/metanorma/{extract.rb → compile/extract.rb} +2 -2
  30. data/lib/metanorma/{config.rb → config/config.rb} +1 -1
  31. data/lib/metanorma/input/asciidoc.rb +3 -3
  32. data/lib/metanorma/input/base.rb +1 -5
  33. data/lib/metanorma/processor/processor.rb +54 -0
  34. data/lib/metanorma/processor.rb +1 -49
  35. data/lib/metanorma/{registry.rb → registry/registry.rb} +0 -1
  36. data/lib/metanorma/shale_monkeypatch.rb +15 -0
  37. data/lib/metanorma/util/fontist_helper.rb +130 -0
  38. data/lib/metanorma/util/util.rb +45 -0
  39. data/lib/metanorma/util/worker_pool.rb +39 -0
  40. data/lib/metanorma/version.rb +1 -1
  41. data/lib/metanorma.rb +13 -8
  42. data/metanorma.gemspec +2 -1
  43. metadata +51 -26
  44. data/Gemfile.devel +0 -2
  45. data/lib/metanorma/collection.rb +0 -243
  46. data/lib/metanorma/collection_fileparse.rb +0 -254
  47. data/lib/metanorma/collection_fileprocess.rb +0 -157
  48. data/lib/metanorma/collection_manifest.rb +0 -139
  49. data/lib/metanorma/collection_render_utils.rb +0 -169
  50. data/lib/metanorma/collection_render_word.rb +0 -131
  51. data/lib/metanorma/collection_renderer.rb +0 -230
  52. data/lib/metanorma/document.rb +0 -133
  53. data/lib/metanorma/files_lookup.rb +0 -208
  54. data/lib/metanorma/files_lookup_sectionsplit.rb +0 -84
  55. data/lib/metanorma/fontist_utils.rb +0 -122
  56. data/lib/metanorma/sectionsplit.rb +0 -216
  57. data/lib/metanorma/sectionsplit_links.rb +0 -189
  58. data/lib/metanorma/util.rb +0 -127
  59. data/lib/metanorma/worker_pool.rb +0 -29
@@ -0,0 +1,157 @@
1
+ require "isodoc"
2
+ require "htmlentities"
3
+ require_relative "fileprocess"
4
+ require_relative "../../util/fontist_helper"
5
+ require_relative "../../util/util"
6
+ require_relative "../filelookup/filelookup"
7
+ require_relative "utils"
8
+ require_relative "render_word"
9
+ require_relative "navigation"
10
+
11
+ module Metanorma
12
+ class Collection
13
+ class Renderer
14
+ FORMATS = %i[html xml doc pdf].freeze
15
+
16
+ attr_accessor :isodoc, :nested
17
+ attr_reader :xml, :compile, :compile_options, :documents, :outdir,
18
+ :manifest
19
+
20
+ # This is only going to render the HTML collection
21
+ # @param xml [Metanorma::Collection] input XML collection
22
+ # @param folder [String] input folder
23
+ # @param options [Hash]
24
+ # @option options [String] :coverpage cover page HTML (Liquid template)
25
+ # @option options [Array<Symbol>] :format list of formats (xml,html,doc,pdf)
26
+ # @option options [String] :output_folder output directory
27
+ #
28
+ # We presuppose that the bibdata of the document is equivalent to that of
29
+ # the collection, and that the flavour gem can sensibly process it. We may
30
+ # need to enhance metadata in the flavour gems isodoc/metadata.rb with
31
+ # collection metadata
32
+ def initialize(collection, folder, options = {}) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
33
+ check_options options
34
+ @xml = Nokogiri::XML collection.to_xml # @xml is the collection manifest
35
+ @xml.root.default_namespace = "http://metanorma.org"
36
+ @lang = collection.bibdata.language.first || "en"
37
+ @script = collection.bibdata.script.first || "Latn"
38
+ @locale = @xml.at("//xmlns:bibdata/xmlns:locale")&.text
39
+ @doctype = doctype
40
+ @compile = Compile.new
41
+ @compile.load_flavor(@doctype)
42
+
43
+ @isodoc = isodoc_create # output processor for flavour
44
+ @outdir = dir_name_cleanse(options[:output_folder])
45
+ @coverpage = options[:coverpage] || collection.coverpage
46
+ @format = ::Metanorma::Util.sort_extensions_execution(options[:format])
47
+ @compile_options = options[:compile] || {}
48
+ @compile_options[:install_fonts] = true if options[:install_fonts]
49
+ @log = options[:log]
50
+ @bibdata = collection.bibdata
51
+ @documents = collection.documents
52
+ @bibdatas = collection.documents
53
+ @directives = collection.directives
54
+ @dirname = collection.dirname
55
+ @manifest = collection.manifest.config
56
+ @disambig = Util::DisambigFiles.new
57
+ @prefatory = collection.prefatory
58
+ @final = collection.final
59
+ @c = HTMLEntities.new
60
+ @files_to_delete = []
61
+ @nested = options[:nested] # if false, this is the root instance of Renderer
62
+ # if true, then this is not the last time Renderer will be run
63
+ # (e.g. this is sectionsplit)
64
+
65
+ # list of files in the collection
66
+ @files = Metanorma::Collection::FileLookup.new(folder, self)
67
+ @files.add_section_split
68
+ isodoc_populate
69
+ create_non_existing_directory(@outdir)
70
+ end
71
+
72
+ def flush_files
73
+ warn "\n\n\n\n\nDone: #{DateTime.now.strftime('%H:%M:%S')}"
74
+ warn @files.files_to_delete
75
+ @files.files_to_delete.each { |f| FileUtils.rm_f(f) }
76
+ @files_to_delete.each { |f| FileUtils.rm_f(f) }
77
+ end
78
+
79
+ # @param col [Metanorma::Collection] XML collection
80
+ # @param options [Hash]
81
+ # @option options [String] :coverpage cover page HTML (Liquid template)
82
+ # @option options [Array<Symbol>] :format list of formats
83
+ # @option options [Strong] :ourput_folder output directory
84
+ def self.render(col, options = {})
85
+ warn "\n\n\n\n\nRender Init: #{DateTime.now.strftime('%H:%M:%S')}"
86
+ cr = new(col, File.dirname(col.file), options)
87
+ cr.files
88
+ cr.concatenate(col, options)
89
+ options[:format]&.include?(:html) and cr.coverpage
90
+ cr.flush_files
91
+ cr
92
+ end
93
+
94
+ def concatenate(col, options)
95
+ warn "\n\n\n\n\nConcatenate: #{DateTime.now.strftime('%H:%M:%S')}"
96
+ (options[:format] & %i(pdf doc)).empty? or
97
+ options[:format] << :presentation
98
+ concatenate_prep(col, options)
99
+ concatenate_outputs(options)
100
+ end
101
+
102
+ def concatenate_prep(col, options)
103
+ %i(xml presentation).each do |e|
104
+ options[:format].include?(e) or next
105
+ ext = e == :presentation ? "presentation.xml" : e.to_s
106
+ File.open(File.join(@outdir, "collection.#{ext}"), "w:UTF-8") do |f|
107
+ b = concatenate1(col.clone, e).to_xml
108
+ e == :presentation and
109
+ b.sub!("<metanorma-collection>", "<metanorma-collection xmlns='http://metanorma.org'>")
110
+ # TODO BEING FORCED TO DO THAT BECAUSE SHALE IS NOT DEALING WITH DEFAULT NAMESPACES
111
+ f.write(b)
112
+ end
113
+ end
114
+ end
115
+
116
+ def concatenate_outputs(options)
117
+ pres = File.join(@outdir, "collection.presentation.xml")
118
+ options[:format].include?(:pdf) and pdfconv.convert(pres)
119
+ options[:format].include?(:doc) and docconv_convert(pres)
120
+ end
121
+
122
+ def concatenate1(out, ext)
123
+ out.directives << ::Metanorma::Collection::Config::Directive.new(key: "documents-inline")
124
+ out.bibdatas.each_key do |ident|
125
+ id = @isodoc.docid_prefix(nil, ident.dup)
126
+ @files.get(id, :attachment) || @files.get(id, :outputs).nil? and next
127
+ out.documents[Util::key id] =
128
+ Metanorma::Collection::Document.raw_file(@files.get(id,
129
+ :outputs)[ext])
130
+ end
131
+ out
132
+ end
133
+
134
+ # infer the flavour from the first document identifier; relaton does that
135
+ def doctype
136
+ if (docid = @xml.at("//bibdata/docidentifier/@type")&.text)
137
+ dt = docid.downcase
138
+ elsif (docid = @xml.at("//bibdata/docidentifier")&.text)
139
+ dt = docid.sub(/\s.*$/, "").lowercase
140
+ else return "standoc"
141
+ end
142
+ @registry = Metanorma::Registry.instance
143
+ @registry.alias(dt.to_sym)&.to_s || dt
144
+ end
145
+
146
+ # populate liquid template of ARGV[1] with metadata extracted from
147
+ # collection manifest
148
+ def coverpage
149
+ @coverpage or return
150
+ warn "\n\n\n\n\nCoverpage: #{DateTime.now.strftime('%H:%M:%S')}"
151
+ File.open(File.join(@outdir, "index.html"), "w:UTF-8") do |f|
152
+ f.write @isodoc.populate_template(File.read(@coverpage))
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,183 @@
1
+ module Metanorma
2
+ class Collection
3
+ class Renderer
4
+ def dir_name_cleanse(name)
5
+ path = Pathname.new(name)
6
+ clean_regex = /[<>:"|?*]/
7
+ fallback_sym = "_"
8
+ return name.gsub(clean_regex, fallback_sym) unless path.absolute?
9
+
10
+ File.join(path.dirname,
11
+ path.basename.to_s.gsub(clean_regex, fallback_sym))
12
+ end
13
+
14
+ def dup_bibitem(docid, bib)
15
+ newbib = @files.get(docid, :bibdata).dup
16
+ newbib.name = "bibitem"
17
+ newbib["hidden"] = "true"
18
+ newbib&.at("./*[local-name() = 'ext']")&.remove
19
+ newbib["id"] = bib["id"]
20
+ newbib
21
+ end
22
+
23
+ def get_bibitem_docid(bib, identifier)
24
+ docid =
25
+ bib.at(ns("./docidentifier[@type = 'metanorma-collection']")) ||
26
+ bib.at(ns("./docidentifier[not(@type)]")) ||
27
+ bib.at(ns("./docidentifier"))
28
+ docid &&= docid_prefix(docid)
29
+ if @files.get(docid) then docid
30
+ else
31
+ fail_update_bibitem(docid, identifier)
32
+ nil
33
+ end
34
+ end
35
+
36
+ def hide_refs(docxml)
37
+ docxml.xpath(ns("//references[bibitem][not(./bibitem[not(@hidden) or " \
38
+ "@hidden = 'false'])]")).each do |f|
39
+ f["hidden"] = "true"
40
+ end
41
+ end
42
+
43
+ def strip_eref(eref)
44
+ eref.xpath(ns("./locality | ./localityStack")).each(&:remove)
45
+ eref.replace(eref.children)
46
+ end
47
+
48
+ def docid_to_citeas(bib)
49
+ docid = bib.at(ns("./docidentifier[@primary = 'true']")) ||
50
+ bib.at(ns("./docidentifier")) or return
51
+ docid_prefix(docid)
52
+ end
53
+
54
+ def collect_erefs(docxml)
55
+ docxml.xpath(ns("//eref"))
56
+ .each_with_object({ citeas: {}, bibitemid: {} }) do |i, m|
57
+ m[:citeas][i["citeas"]] = true
58
+ m[:bibitemid][i["bibitemid"]] = true
59
+ end
60
+ end
61
+
62
+ def docid_xml(val)
63
+ "<docidentifier type='repository'>current-metanorma-collection/" \
64
+ "#{val}</docidentifier>"
65
+ end
66
+
67
+ def add_hidden_bibliography(xmldoc, refs)
68
+ ins = new_hidden_ref(xmldoc)
69
+ refs.each do |k, v|
70
+ url = @files.url(v, {})
71
+ ins << <<~XML
72
+ <bibitem id="#{k}">#{docid_xml(v)}<uri type='citation'>#{url}</uri></bibitem>
73
+ XML
74
+ end
75
+ end
76
+
77
+ private
78
+
79
+ def docid_prefix(docid)
80
+ type = docid["type"]
81
+ type == "metanorma-collection" and type = nil
82
+ @c.decode(@isodoc
83
+ .docid_prefix(type, docid.children.to_xml)).gsub(/\s/, " ")
84
+ end
85
+
86
+ def create_non_existing_directory(output_directory)
87
+ !File.exist?(output_directory) and
88
+ FileUtils.mkdir_p(output_directory)
89
+ end
90
+
91
+ def format_sort(formats)
92
+ ret = []
93
+ formats.include?(:xml) and ret << :xml
94
+ formats.include?(:presentation) and ret << :presentation
95
+ a = %i(presentation xml)
96
+ ret + formats.reject { |i| a.include? i }
97
+ end
98
+
99
+ # @param options [Hash]
100
+ # @raise [ArgumentError]
101
+ def check_options(options)
102
+ (options[:format].is_a?(Array) && (FORMATS & options[:format]).any?) or
103
+ raise ArgumentError, "Need to specify formats (xml,html,pdf,doc)"
104
+ end
105
+
106
+ def pdfconv
107
+ doctype = @doctype.to_sym
108
+ x = Asciidoctor.load nil, backend: doctype
109
+ x.converter.pdf_converter(PdfOptionsNode.new(doctype,
110
+ @compile_options))
111
+ end
112
+
113
+ def fail_update_bibitem(docid, identifier)
114
+ error = "[metanorma] Cannot find crossreference to document #{docid} " \
115
+ "in document #{identifier}."
116
+ @log&.add("Cross-References", nil, error)
117
+ ::Metanorma::Util.log(error, :warning)
118
+ end
119
+
120
+ def datauri_encode(docxml, directory)
121
+ docxml.xpath(ns("//image")).each do |i|
122
+ i["src"] = Vectory::Utils::datauri(i["src"], directory)
123
+ end
124
+ docxml
125
+ end
126
+
127
+ class PdfOptionsNode
128
+ def initialize(doctype, options)
129
+ p = Metanorma::Registry.instance.find_processor(doctype)
130
+ if ::Metanorma::Util::FontistHelper.has_custom_fonts?(p, options, {})
131
+ @fonts_manifest =
132
+ ::Metanorma::Util::FontistHelper.location_manifest(p, options)
133
+ end
134
+ end
135
+
136
+ def attr(key)
137
+ if key == "fonts-manifest" && @fonts_manifest
138
+ @fonts_manifest
139
+ end
140
+ end
141
+ end
142
+
143
+ def isodoc_create
144
+ isodoc = Util::load_isodoc(@doctype)
145
+ isodoc.i18n_init(@lang, @script, @locale) # read in internationalisation
146
+ isodoc.metadata_init(@lang, @script, @locale, isodoc.i18n)
147
+ isodoc.info(@xml, nil)
148
+ isodoc
149
+ end
150
+
151
+ # create the @meta class of isodoc, for populating Liquid,
152
+ # with "navigation" set to the index bar.
153
+ # extracted from the manifest
154
+ def isodoc_populate
155
+ @isodoc.info(@xml, nil)
156
+ { navigation: indexfile(@manifest), nav_object: index_object(@manifest),
157
+ docrefs: liquid_docrefs(@manifest),
158
+ "prefatory-content": isodoc_builder(@xml.at("//prefatory-content")),
159
+ "final-content": isodoc_builder(@xml.at("//final-content")),
160
+ doctitle: @bibdata.title.first.title.content,
161
+ docnumber: @bibdata.docidentifier.first.id }.each do |k, v|
162
+ v and @isodoc.meta.set(k, v)
163
+ end
164
+ end
165
+
166
+ def isodoc_builder(node)
167
+ node or return
168
+
169
+ # Kludging namespace back in because of Shale brain damage
170
+ doc = Nokogiri::XML(node.to_xml.sub(">", " xmlns='http://www.metanorma.org'>"))
171
+ Nokogiri::HTML::Builder.new(encoding: "UTF-8") do |b|
172
+ b.div do |div|
173
+ doc.root.children&.each { |n| @isodoc.parse(n, div) }
174
+ end
175
+ end.doc.root.to_html
176
+ end
177
+
178
+ def ns(xpath)
179
+ @isodoc.ns(xpath)
180
+ end
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,218 @@
1
+ require "yaml"
2
+ require_relative "../../util/util"
3
+ require_relative "../xrefprocess/xrefprocess"
4
+
5
+ module Metanorma
6
+ class Collection
7
+ class Sectionsplit
8
+ attr_accessor :filecache, :key
9
+
10
+ def initialize(opts)
11
+ @input_filename = opts[:input]
12
+ @base = opts[:base]
13
+ @output_filename = opts[:output]
14
+ @xml = opts[:xml]
15
+ @dir = opts[:dir]
16
+ @compile_opts = opts[:compile_opts] || {}
17
+ @fileslookup = opts[:fileslookup]
18
+ @ident = opts[:ident]
19
+ @isodoc = opts[:isodoc]
20
+ end
21
+
22
+ def ns(xpath)
23
+ @isodoc.ns(xpath)
24
+ end
25
+
26
+ def build_collection
27
+ collection_setup(@base, @dir)
28
+ files = sectionsplit # (@input_filename, @base, @dir, @compile_opts)
29
+ input_xml = Nokogiri::XML(File.read(@input_filename,
30
+ encoding: "UTF-8"), &:huge)
31
+ collection_manifest(@base, files, input_xml, @xml, @dir).render(
32
+ { format: %i(html), output_folder: "#{@output_filename}_collection",
33
+ coverpage: File.join(@dir, "cover.html") }.merge(@compile_opts),
34
+ )
35
+ end
36
+
37
+ def collection_manifest(filename, files, origxml, _presxml, dir)
38
+ File.open(File.join(dir, "#{filename}.html.yaml"), "w:UTF-8") do |f|
39
+ f.write(collectionyaml(files, origxml))
40
+ end
41
+ Metanorma::Collection.parse File.join(dir, "#{filename}.html.yaml")
42
+ end
43
+
44
+ def collection_setup(filename, dir)
45
+ FileUtils.mkdir_p "#{filename}_collection" if filename
46
+ FileUtils.mkdir_p dir
47
+ File.open(File.join(dir, "cover.html"), "w:UTF-8") do |f|
48
+ f.write(coll_cover)
49
+ end
50
+ end
51
+
52
+ def coll_cover
53
+ <<~COVER
54
+ <html><head><meta charset="UTF-8"/></head><body>
55
+ <h1>{{ doctitle }}</h1>
56
+ <h2>{{ docnumber }}</h2>
57
+ <nav>{{ navigation }}</nav>
58
+ </body></html>
59
+ COVER
60
+ end
61
+
62
+ SPLITSECTIONS =
63
+ [["//preface/*", "preface"], ["//sections/*", "sections"],
64
+ ["//annex", nil],
65
+ ["//bibliography/*[not(@hidden = 'true')]", "bibliography"],
66
+ ["//indexsect", nil], ["//colophon", nil]].freeze
67
+
68
+ # Input XML is Semantic
69
+ # def sectionsplit(filename, basename, dir, compile_options, fileslookup = nil, ident = nil)
70
+ def sectionsplit
71
+ xml = sectionsplit_prep(File.read(@input_filename), @base, @dir)
72
+ @key = Metanorma::Collection::XrefProcess::xref_preprocess(xml, @isodoc)
73
+ SPLITSECTIONS.each_with_object([]) do |n, ret|
74
+ conflate_floatingtitles(xml.xpath(ns(n[0]))).each do |s|
75
+ ret << sectionfile(xml, emptydoc(xml), "#{@base}.#{ret.size}", s,
76
+ n[1])
77
+ end
78
+ end
79
+ end
80
+
81
+ def block?(node)
82
+ %w(p table formula admonition ol ul dl figure quote sourcecode example
83
+ pre note pagebrreak hr bookmark requirement recommendation permission
84
+ svgmap inputform toc passthrough review imagemap).include?(node.name)
85
+ end
86
+
87
+ def conflate_floatingtitles(nodes)
88
+ holdover = false
89
+ nodes.each_with_object([]) do |x, m|
90
+ if holdover then m.last << x
91
+ else m << [x]
92
+ end
93
+ holdover = block?(x)
94
+ end
95
+ end
96
+
97
+ def sectionsplit_prep(file, filename, dir)
98
+ @splitdir = dir
99
+ xml1filename, type = sectionsplit_preprocess_semxml(file, filename)
100
+ Compile.new.compile(
101
+ xml1filename,
102
+ { format: :asciidoc, extension_keys: [:presentation], type: type }
103
+ .merge(@compile_opts),
104
+ )
105
+ Nokogiri::XML(File.read(xml1filename.sub(/\.xml$/, ".presentation.xml"),
106
+ encoding: "utf-8"), &:huge)
107
+ end
108
+
109
+ def sectionsplit_preprocess_semxml(file, filename)
110
+ xml = Nokogiri::XML(file, &:huge)
111
+ type = xml.root.name.sub("-standard", "").to_sym
112
+ sectionsplit_update_xrefs(xml)
113
+ xml1 = sectionsplit_write_semxml(filename, xml)
114
+ @filecache ||= []
115
+ @filecache << xml1
116
+ [xml1.path, type]
117
+ end
118
+
119
+ def sectionsplit_update_xrefs(xml)
120
+ if c = @fileslookup&.parent
121
+ n = c.nested
122
+ c.nested = true # so unresolved erefs are not deleted
123
+ c.update_xrefs(xml, @ident, {})
124
+ c.nested = n
125
+ end
126
+ end
127
+
128
+ def sectionsplit_write_semxml(filename, xml)
129
+ Tempfile.open([filename, ".xml"], encoding: "utf-8") do |f|
130
+ # f.write(@isodoc.to_xml(svg_preprocess(xml)))
131
+ f.write(@isodoc.to_xml(xml))
132
+ f
133
+ end
134
+ end
135
+
136
+ def emptydoc(xml)
137
+ out = xml.dup
138
+ out.xpath(
139
+ ns("//preface | //sections | //annex | //bibliography/clause | " \
140
+ "//bibliography/references[not(@hidden = 'true')] | //indexsect | " \
141
+ "//colophon"),
142
+ ).each(&:remove)
143
+ out
144
+ end
145
+
146
+ def sectionfile(fulldoc, xml, file, chunks, parentnode)
147
+ fname = create_sectionfile(fulldoc, xml.dup, file, chunks, parentnode)
148
+ { order: chunks.last["displayorder"].to_i, url: fname,
149
+ title: titlerender(chunks.last) }
150
+ end
151
+
152
+ def create_sectionfile(xml, out, file, chunks, parentnode)
153
+ ins = out.at(ns("//metanorma-extension")) || out.at(ns("//bibdata"))
154
+ sectionfile_insert(ins, chunks, parentnode)
155
+ Metanorma::Collection::XrefProcess::xref_process(out, xml, @key, @ident, @isodoc)
156
+ outname = "#{file}.xml"
157
+ File.open(File.join(@splitdir, outname), "w:UTF-8") do |f|
158
+ f.write(out)
159
+ end
160
+ outname
161
+ end
162
+
163
+ def sectionfile_insert(ins, chunks, parentnode)
164
+ if parentnode
165
+ ins.next = "<#{parentnode}/>"
166
+ chunks.each { |c| ins.next.add_child(c.dup) }
167
+ else chunks.each { |c| ins.next = c.dup }
168
+ end
169
+ end
170
+
171
+ def titlerender(section)
172
+ title = section.at(ns("./title")) or return "[Untitled]"
173
+ t = title.dup
174
+ t.xpath(ns(".//tab | .//br")).each { |x| x.replace(" ") }
175
+ t.xpath(ns(".//strong")).each { |x| x.replace(x.children) }
176
+ t.children.to_xml
177
+ end
178
+
179
+ def collectionyaml(files, xml)
180
+ ret = {
181
+ directives: ["presentation-xml", "bare-after-first"],
182
+ bibdata: {
183
+ title: {
184
+ type: "title-main", language: @lang,
185
+ content: xml.at(ns("//bibdata/title")).text
186
+ },
187
+ type: "collection",
188
+ docid: {
189
+ type: xml.at(ns("//bibdata/docidentifier/@type")).text,
190
+ id: xml.at(ns("//bibdata/docidentifier")).text,
191
+ },
192
+ },
193
+ manifest: {
194
+ level: "collection", title: "Collection",
195
+ docref: files.sort_by { |f| f[:order] }.each.map do |f|
196
+ { fileref: f[:url], identifier: f[:title] }
197
+ end
198
+ },
199
+ }
200
+ ::Metanorma::Util::recursive_string_keys(ret).to_yaml
201
+ end
202
+
203
+ def section_split_cover(col, ident, _one_doc_coll)
204
+ dir = File.dirname(col.file)
205
+ collection_setup(nil, dir)
206
+ r = ::Metanorma::Collection::Renderer
207
+ .new(col, dir, output_folder: "#{ident}_collection",
208
+ format: %i(html), coverpage: File.join(dir, "cover.html"))
209
+ r.coverpage
210
+ # filename = one_doc_coll ? "#{ident}_index.html" : "index.html"
211
+ filename = File.basename("#{ident}_index.html") # ident can be a directory with YAML indirection
212
+ FileUtils.mv File.join(r.outdir, "index.html"), File.join(dir, filename)
213
+ FileUtils.rm_rf r.outdir
214
+ filename
215
+ end
216
+ end
217
+ end
218
+ end
@@ -0,0 +1,37 @@
1
+ module Metanorma
2
+ class Collection
3
+ module Util
4
+ class DisambigFiles
5
+ def initialize
6
+ @seen_filenames = []
7
+ end
8
+
9
+ def strip_root(name)
10
+ name.sub(%r{^(\./)?(\.\./)+}, "")
11
+ end
12
+
13
+ def source2dest_filename(name, disambig = true)
14
+ n = strip_root(name)
15
+ dir = File.dirname(n)
16
+ base = File.basename(n)
17
+ if disambig && @seen_filenames.include?(base)
18
+ base = disambiguate_filename(base)
19
+ end
20
+ @seen_filenames << base
21
+ dir == "." ? base : File.join(dir, base)
22
+ end
23
+
24
+ def disambiguate_filename(base)
25
+ m = /^(?<start>.+\.)(?!0)(?<num>\d+)\.(?<suff>[^.]*)$/.match(base) ||
26
+ /^(?<start>.+\.)(?<suff>[^.]*)/.match(base) ||
27
+ /^(?<start>.+)$/.match(base)
28
+ i = m.names.include?("num") ? m["num"].to_i + 1 : 1
29
+ while @seen_filenames.include? base = "#{m['start']}#{i}.#{m['suff']}"
30
+ i += 1
31
+ end
32
+ base
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,72 @@
1
+ module Metanorma
2
+ class Collection
3
+ module Util
4
+ class << self
5
+ def gather_bibitems(xml)
6
+ xml.xpath("//xmlns:bibitem[@id]").each_with_object({}) do |b, m|
7
+ if m[b["id"]]
8
+ b.remove
9
+ next
10
+ # we can't update duplicate bibitem, processing updates wrong one
11
+ else
12
+ m[b["id"]] = b
13
+ end
14
+ end
15
+ end
16
+
17
+ def gather_bibitemids(xml)
18
+ xml.xpath("//*[@bibitemid]").each_with_object({}) do |e, m|
19
+ /^semantic__/.match?(e.name) and next
20
+ m[e["bibitemid"]] ||= []
21
+ m[e["bibitemid"]] << e
22
+ end
23
+ end
24
+
25
+ def gather_citeases(xml)
26
+ xml.xpath("//*[@citeas]").each_with_object({}) do |e, m|
27
+ /^semantic__/.match?(e.name) and next
28
+ m[e["citeas"]] ||= []
29
+ m[e["citeas"]] << e
30
+ end
31
+ end
32
+
33
+ def add_suffix_to_attributes(doc, suffix, tag_name, attr_name,
34
+ isodoc)
35
+ (suffix.nil? || suffix.empty?) and return
36
+ doc.xpath(isodoc.ns("//#{tag_name}[@#{attr_name}]")).each do |elem|
37
+ a = elem.attributes[attr_name].value
38
+ /_#{suffix}$/.match?(a) or
39
+ elem.attributes[attr_name].value = "#{a}_#{suffix}"
40
+ end
41
+ end
42
+
43
+ def hash_key_detect(directives, key, variable)
44
+ c = directives.detect { |x| x.key == key } or
45
+ return variable
46
+ c.value
47
+ end
48
+
49
+ def rel_path_resolve(dir, path)
50
+ path.nil? and return path
51
+ path.empty? and return path
52
+ p = Pathname.new(path)
53
+ p.absolute? ? path : File.join(dir, path)
54
+ end
55
+
56
+ def key(ident)
57
+ @c ||= HTMLEntities.new
58
+ @c.decode(ident).gsub(/(\p{Zs})+/, " ")
59
+ end
60
+
61
+ class Dummy
62
+ def attr(_key); end
63
+ end
64
+
65
+ def load_isodoc(doctype)
66
+ x = Asciidoctor.load nil, backend: doctype.to_sym
67
+ x.converter.html_converter(Dummy.new) # to obtain Isodoc class
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end