metanorma 1.7.7 → 2.0.1

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 (58) 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 +226 -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 +254 -0
  17. data/lib/metanorma/collection/renderer/fileprocess.rb +174 -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 +211 -0
  22. data/lib/metanorma/collection/sectionsplit/sectionsplit.rb +219 -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} +19 -12
  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 +50 -24
  44. data/lib/metanorma/collection.rb +0 -244
  45. data/lib/metanorma/collection_fileparse.rb +0 -257
  46. data/lib/metanorma/collection_fileprocess.rb +0 -168
  47. data/lib/metanorma/collection_manifest.rb +0 -168
  48. data/lib/metanorma/collection_render_utils.rb +0 -169
  49. data/lib/metanorma/collection_render_word.rb +0 -131
  50. data/lib/metanorma/collection_renderer.rb +0 -237
  51. data/lib/metanorma/collection_xref_process.rb +0 -217
  52. data/lib/metanorma/document.rb +0 -133
  53. data/lib/metanorma/files_lookup.rb +0 -224
  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/util.rb +0 -127
  58. 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,211 @@
1
+ module Metanorma
2
+ class Collection
3
+ class Renderer
4
+ def dir_name_cleanse(name)
5
+ path = Pathname.new(name)
6
+ clean_regex = /[<>:"|?*\p{Zs}]/
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 new_hidden_ref(xmldoc)
44
+ ins = xmldoc.at(ns("bibliography")) or
45
+ xmldoc.root << "<bibliography/>" and ins = xmldoc.at(ns("bibliography"))
46
+ ins.at(ns("./referenced[@hidden = 'true']")) or
47
+ ins.add_child("<references hidden='true' normative='false'/>").first
48
+ end
49
+
50
+ def strip_eref(eref)
51
+ eref.xpath(ns("./locality | ./localityStack")).each(&:remove)
52
+ eref.replace(eref.children)
53
+ end
54
+
55
+ def docid_to_citeas(bib)
56
+ docid = bib.at(ns("./docidentifier[@primary = 'true']")) ||
57
+ bib.at(ns("./docidentifier")) or return
58
+ docid_prefix(docid)
59
+ end
60
+
61
+ def collect_erefs(docxml)
62
+ docxml.xpath(ns("//eref"))
63
+ .each_with_object({ citeas: {}, bibitemid: {} }) do |i, m|
64
+ m[:citeas][i["citeas"]] = true
65
+ m[:bibitemid][i["bibitemid"]] = true
66
+ end
67
+ end
68
+
69
+ def docid_xml(val)
70
+ "<docidentifier type='repository'>current-metanorma-collection/" \
71
+ "#{val}</docidentifier>"
72
+ end
73
+
74
+ def add_hidden_bibliography(xmldoc, refs)
75
+ ins = new_hidden_ref(xmldoc)
76
+ refs.each do |k, v|
77
+ url = @files.url(v, {})
78
+ ins << <<~XML
79
+ <bibitem id="#{k}">#{docid_xml(v)}<uri type='citation'>#{url}</uri></bibitem>
80
+ XML
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def docid_prefix(docid)
87
+ type = docid["type"]
88
+ type == "metanorma-collection" and type = nil
89
+ @c.decode(@isodoc
90
+ .docid_prefix(type, docid.children.to_xml)).gsub(/\s/, " ")
91
+ end
92
+
93
+ def create_non_existing_directory(output_directory)
94
+ !File.exist?(output_directory) and
95
+ FileUtils.mkdir_p(output_directory)
96
+ end
97
+
98
+ def format_sort(formats)
99
+ ret = []
100
+ formats.include?(:xml) and ret << :xml
101
+ formats.include?(:presentation) and ret << :presentation
102
+ a = %i(presentation xml)
103
+ ret + formats.reject { |i| a.include? i }
104
+ end
105
+
106
+ # @param options [Hash]
107
+ # @raise [ArgumentError]
108
+ def check_options(options)
109
+ (options[:format].is_a?(Array) && (FORMATS & options[:format]).any?) or
110
+ raise ArgumentError, "Need to specify formats (xml,html,pdf,doc)"
111
+ end
112
+
113
+ def pdfconv
114
+ doctype = @doctype.to_sym
115
+ x = Asciidoctor.load nil, backend: doctype
116
+ x.converter.pdf_converter(PdfOptionsNode.new(doctype,
117
+ @compile_options))
118
+ end
119
+
120
+ def fail_update_bibitem(docid, identifier)
121
+ error = "[metanorma] Cannot find crossreference to document #{docid} " \
122
+ "in document #{identifier}."
123
+ @log&.add("Cross-References", nil, error)
124
+ ::Metanorma::Util.log(error, :warning)
125
+ end
126
+
127
+ def datauri_encode(docxml, directory)
128
+ docxml.xpath(ns("//image")).each do |i|
129
+ read_in_if_svg(i, directory.sub(%r{(?<!=/)$}, "/")) and i["src"] = nil
130
+ end
131
+ docxml.xpath(ns("//image")).each do |i| # rubocop:disable Style/CombinableLoops
132
+ i["src"] && !i["src"].empty? or next
133
+ i["src"] = Vectory::Utils::datauri(i["src"], directory)
134
+ end
135
+ docxml
136
+ end
137
+
138
+ def read_in_if_svg(img, localdir)
139
+ img["src"] or return false
140
+ img.elements.map(&:name).include?("svg") and return true
141
+ path = Vectory::Utils.svgmap_rewrite0_path(img["src"], localdir)
142
+ svg = svg_in_path?(path) or return false
143
+ img.children = (Nokogiri::XML(svg).root)
144
+ true
145
+ end
146
+
147
+ def svg_in_path?(path)
148
+ File.file?(path) or return false
149
+ types = MIME::Types.type_for(path) or return false
150
+ types.first == "image/svg+xml" or return false
151
+ svg = File.read(path, encoding: "utf-8") or return false
152
+ svg
153
+ end
154
+
155
+ class PdfOptionsNode
156
+ def initialize(doctype, options)
157
+ p = Metanorma::Registry.instance.find_processor(doctype)
158
+ if ::Metanorma::Util::FontistHelper.has_custom_fonts?(p, options, {})
159
+ @fonts_manifest =
160
+ ::Metanorma::Util::FontistHelper.location_manifest(p, options)
161
+ end
162
+ end
163
+
164
+ def attr(key)
165
+ if key == "fonts-manifest" && @fonts_manifest
166
+ @fonts_manifest
167
+ end
168
+ end
169
+ end
170
+
171
+ def isodoc_create
172
+ isodoc = Util::load_isodoc(@doctype)
173
+ isodoc.i18n_init(@lang, @script, @locale) # read in internationalisation
174
+ isodoc.metadata_init(@lang, @script, @locale, isodoc.i18n)
175
+ isodoc.info(@xml, nil)
176
+ isodoc
177
+ end
178
+
179
+ # create the @meta class of isodoc, for populating Liquid,
180
+ # with "navigation" set to the index bar.
181
+ # extracted from the manifest
182
+ def isodoc_populate
183
+ @isodoc.info(@xml, nil)
184
+ { navigation: indexfile(@manifest), nav_object: index_object(@manifest),
185
+ docrefs: liquid_docrefs(@manifest),
186
+ "prefatory-content": isodoc_builder(@xml.at("//prefatory-content")),
187
+ "final-content": isodoc_builder(@xml.at("//final-content")),
188
+ doctitle: @bibdata.title.first.title.content,
189
+ docnumber: @bibdata.docidentifier.first.id }.each do |k, v|
190
+ v and @isodoc.meta.set(k, v)
191
+ end
192
+ end
193
+
194
+ def isodoc_builder(node)
195
+ node or return
196
+
197
+ # Kludging namespace back in because of Shale brain damage
198
+ doc = Nokogiri::XML(node.to_xml.sub(">", " xmlns='http://www.metanorma.org'>"))
199
+ Nokogiri::HTML::Builder.new(encoding: "UTF-8") do |b|
200
+ b.div do |div|
201
+ doc.root.children&.each { |n| @isodoc.parse(n, div) }
202
+ end
203
+ end.doc.root.to_html
204
+ end
205
+
206
+ def ns(xpath)
207
+ @isodoc.ns(xpath)
208
+ end
209
+ end
210
+ end
211
+ end
@@ -0,0 +1,219 @@
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
70
+ xml = sectionsplit_prep(File.read(@input_filename), @base, @dir)
71
+ @key = Metanorma::Collection::XrefProcess::xref_preprocess(xml, @isodoc)
72
+ SPLITSECTIONS.each_with_object([]) do |n, ret|
73
+ conflate_floatingtitles(xml.xpath(ns(n[0]))).each do |s|
74
+ ret << sectionfile(xml, emptydoc(xml), "#{@base}.#{ret.size}", s,
75
+ n[1])
76
+ end
77
+ end
78
+ end
79
+
80
+ def block?(node)
81
+ %w(p table formula admonition ol ul dl figure quote sourcecode example
82
+ pre note pagebrreak hr bookmark requirement recommendation permission
83
+ svgmap inputform toc passthrough review imagemap).include?(node.name)
84
+ end
85
+
86
+ def conflate_floatingtitles(nodes)
87
+ holdover = false
88
+ nodes.each_with_object([]) do |x, m|
89
+ if holdover then m.last << x
90
+ else m << [x]
91
+ end
92
+ holdover = block?(x)
93
+ end
94
+ end
95
+
96
+ def sectionsplit_prep(file, filename, dir)
97
+ @splitdir = dir
98
+ xml1, type = sectionsplit_preprocess_semxml(file, filename)
99
+ flags = { format: :asciidoc, extension_keys: [:presentation],
100
+ type: type }.merge(@compile_opts)
101
+ Compile.new.compile(xml1, flags)
102
+ f = File.open(xml1.sub(/\.xml$/, ".presentation.xml"),
103
+ encoding: "utf-8")
104
+ r = Nokogiri::XML(f, &:huge)
105
+ r.xpath("//xmlns:svgmap1").each { |x| x.name = "svgmap" }
106
+ r
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
+ [xml1, type]
115
+ end
116
+
117
+ def sectionsplit_update_xrefs(xml)
118
+ if c = @fileslookup&.parent
119
+ n = c.nested
120
+ c.nested = true # so unresolved erefs are not deleted
121
+ c.update_xrefs(xml, @ident, {})
122
+ c.nested = n
123
+ xml.xpath("//xmlns:svgmap").each { |x| x.name = "svgmap1" }
124
+ # do not process svgmap until after files are split
125
+ end
126
+ end
127
+
128
+ def sectionsplit_write_semxml(filename, xml)
129
+ outname = Pathname.new("tmp_#{filename}").sub_ext(".xml").to_s
130
+ File.open(outname, "w:UTF-8") do |f|
131
+ f.write(@isodoc.to_xml(xml))
132
+ end
133
+ outname
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,
156
+ @ident, @isodoc)
157
+ outname = "#{file}.xml"
158
+ File.open(File.join(@splitdir, outname), "w:UTF-8") do |f|
159
+ f.write(out)
160
+ end
161
+ outname
162
+ end
163
+
164
+ def sectionfile_insert(ins, chunks, parentnode)
165
+ if parentnode
166
+ ins.next = "<#{parentnode}/>"
167
+ chunks.each { |c| ins.next.add_child(c.dup) }
168
+ else chunks.each { |c| ins.next = c.dup }
169
+ end
170
+ end
171
+
172
+ def titlerender(section)
173
+ title = section.at(ns("./title")) or return "[Untitled]"
174
+ t = title.dup
175
+ t.xpath(ns(".//tab | .//br")).each { |x| x.replace(" ") }
176
+ t.xpath(ns(".//strong")).each { |x| x.replace(x.children) }
177
+ t.children.to_xml
178
+ end
179
+
180
+ def collectionyaml(files, xml)
181
+ ret = {
182
+ directives: ["presentation-xml", "bare-after-first"],
183
+ bibdata: {
184
+ title: {
185
+ type: "title-main", language: @lang,
186
+ content: xml.at(ns("//bibdata/title")).text
187
+ },
188
+ type: "collection",
189
+ docid: {
190
+ type: xml.at(ns("//bibdata/docidentifier/@type")).text,
191
+ id: xml.at(ns("//bibdata/docidentifier")).text,
192
+ },
193
+ },
194
+ manifest: {
195
+ level: "collection", title: "Collection",
196
+ docref: files.sort_by { |f| f[:order] }.each.map do |f|
197
+ { fileref: f[:url], identifier: f[:title] }
198
+ end
199
+ },
200
+ }
201
+ ::Metanorma::Util::recursive_string_keys(ret).to_yaml
202
+ end
203
+
204
+ def section_split_cover(col, ident, _one_doc_coll)
205
+ dir = File.dirname(col.file)
206
+ collection_setup(nil, dir)
207
+ r = ::Metanorma::Collection::Renderer
208
+ .new(col, dir, output_folder: "#{ident}_collection",
209
+ format: %i(html), coverpage: File.join(dir, "cover.html"))
210
+ r.coverpage
211
+ # filename = one_doc_coll ? "#{ident}_index.html" : "index.html"
212
+ filename = File.basename("#{ident}_index.html") # ident can be a directory with YAML indirection
213
+ FileUtils.mv File.join(r.outdir, "index.html"), File.join(dir, filename)
214
+ FileUtils.rm_rf r.outdir
215
+ filename
216
+ end
217
+ end
218
+ end
219
+ 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