metanorma 1.7.7 → 2.0.1

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