metanorma 1.7.7 → 2.0.0

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 +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 +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,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