isodoc 3.4.7 → 3.4.8

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6bf206774db883d4bf837cfbdccbf9d620d76f2252313d5ba0c81f5979ff40bb
4
- data.tar.gz: d0c3dd48ec168524fef9799ce3455520d1d578b4e667cbb06a05f70e181c6d67
3
+ metadata.gz: e0934e0c0ea24dd7c91fe682c72855d5cd95c0dbf13bdedf43af86412c304885
4
+ data.tar.gz: 16ee08583e192dafd2d30ec886ef9eefd1703cefbeeefec2a22f042485721d2f
5
5
  SHA512:
6
- metadata.gz: 9f6b1ab7e0be186198065832292a83fe32f33ce6ea4c6fe2faae9307bb5e86c03fe3b1e7e67412dd832af7316528e7d798680715a621583112f1486ec32484da
7
- data.tar.gz: 56b8fb76537ea2b2ddec457558b905249bd8a699fb5a774eeb77c75e5366d00ff53d5977a7e5131b567785244c667083f46c3d7dd45e9fe5e70384bbf7f41305
6
+ metadata.gz: 2cfd83b6f9acc5496e2155cb52b4a4fc9e55d11ef8ba7b54405ed741de74e88b21cfd67b90def1e4515f397291c4eb6b3d7c133a8ff9b647c97b9246be041dd1
7
+ data.tar.gz: b873292c72723ae4359bf3aee7dafaf039f1d6bdc60df110c179b8150274e41c737122a6980a02819b7ca3b332a2e2d9d997e875822dc0ba4b90dd61f1d109e8
@@ -114,7 +114,7 @@ blockquote, q {
114
114
  }
115
115
  }
116
116
 
117
- .h2Annex {
117
+ .h2Annex {
118
118
  font-family: $headerfont;
119
119
  }
120
120
 
data/lib/isodoc/css.rb CHANGED
@@ -75,31 +75,29 @@ module IsoDoc
75
75
  end
76
76
 
77
77
  def convert_scss(filename, stylesheet, stripwordcss)
78
- load_scss_paths(filename)
78
+ load_paths = scss_load_paths(filename)
79
79
  Dir.mktmpdir do |dir|
80
80
  variables_file_path = File.join(dir, "variables.scss")
81
81
  File.write(variables_file_path, scss_fontheader(stripwordcss))
82
- SassC.load_paths << dir
83
82
  modified_stylesheet = %( @use "variables" as *;\n#{stylesheet})
84
- compile_scss(modified_stylesheet)
83
+ compile_scss(modified_stylesheet, load_paths + [dir])
85
84
  end
86
85
  end
87
86
 
88
- def compile_scss(modified_stylesheet)
87
+ def compile_scss(modified_stylesheet, load_paths = [])
89
88
  SassC::Engine
90
89
  .new(modified_stylesheet, quiet_deps: true, syntax: :scss,
90
+ load_paths: load_paths,
91
91
  importer: SasscImporter)
92
92
  .render.gsub(/__WORD__/, "")
93
93
  end
94
94
 
95
- def load_scss_paths(filename)
95
+ def scss_load_paths(filename)
96
96
  require "sassc-embedded"
97
97
  require "isodoc/sassc_importer"
98
98
  [File.join(Gem.loaded_specs["isodoc"].full_gem_path,
99
99
  "lib", "isodoc"),
100
- File.dirname(filename)].each do |name|
101
- SassC.load_paths << name
102
- end
100
+ File.dirname(filename)]
103
101
  end
104
102
 
105
103
  # stripwordcss if HTML stylesheet, !stripwordcss if DOC stylesheet
@@ -90,11 +90,17 @@ module IsoDoc
90
90
  # Extract rule sets from CSS string
91
91
  def extract_rule_sets(css_string)
92
92
  rule_sets = {}
93
- css_string = css_string.gsub(/\/\*.*?\*\//m, "") # Remove comments
94
- css_string.scan(/([^{]+)\{([^}]+)\}/m) do |selector, declarations|
93
+ # Safe C-style comment removal without polynomial backtracking:
94
+ # /\*[^*]*(?:\*+[^*/][^*]*)*\*+\/ matches /* ... */ in linear time
95
+ # by keeping the "star runs not followed by /" separate from non-star
96
+ # content, so no position can be matched by multiple paths.
97
+ css_string = css_string.gsub(%r{/\*[^*]*(?:\*+[^*/][^*]*)*\*+/}, "")
98
+ # [^{}]+ for selector and [^{}]* for declarations: both exclude braces,
99
+ # making the grouping unambiguous without the m flag.
100
+ css_string.scan(/([^{}]+)\{([^{}]*)\}/) do |selector, declarations|
95
101
  selector = selector.strip
96
102
  declarations_hash = {} # Extract declarations
97
- declarations.scan(/([^:;]+):([^;]+);?/) do |property, value|
103
+ declarations.scan(/([^:;]+):([^;]*);?/) do |property, value|
98
104
  declarations_hash[property.strip] = value.strip
99
105
  end
100
106
  rule_sets[selector] = declarations_hash
@@ -149,8 +155,10 @@ module IsoDoc
149
155
  "color" => DEFAULT_VALUES["color"],
150
156
  }
151
157
  value.split(/\s+/).each do |part|
152
- if width?(part) then components["width"] = part
153
- elsif style?(part) then components["style"] = part
158
+ if width?(part)
159
+ components["width"] = part
160
+ elsif style?(part)
161
+ components["style"] = part
154
162
  elsif color?(part)
155
163
  components["color"] = convert_color_to_rgb(part)
156
164
  end
@@ -150,7 +150,7 @@ module IsoDoc
150
150
  classtype = nil
151
151
  classtype = "MsoCommentText" if in_comment
152
152
  node["type"] == "floating-title" and
153
- classtype = "h#{node['depth']}"
153
+ classtype = "h#{node['depth'] || '1'}"
154
154
  classtype ||= node["class"]
155
155
  classtype
156
156
  end
@@ -180,13 +180,19 @@ module IsoDoc
180
180
  end
181
181
 
182
182
  def quote_parse(node, out)
183
- attrs = para_attrs(node)
184
- attrs[:class] = "Quote"
183
+ attrs = quote_attrs(node)
185
184
  out.div(**attr_code(attrs)) do |p|
186
185
  node.children.each { |n| parse(n, p) unless n.name == "source" }
187
186
  end
188
187
  end
189
188
 
189
+ def quote_attrs(node)
190
+ ret = para_attrs(node).merge(class: "Quote")
191
+ node["type"] == "newcontent" and
192
+ ret[:class] += " AmendNewcontent"
193
+ ret
194
+ end
195
+
190
196
  def passthrough_parse(node, out)
191
197
  node["formats"] &&
192
198
  !(node["formats"].split.include? @format.to_s) and return
@@ -8,8 +8,8 @@ module IsoDoc
8
8
  def passthrough_cleanup(docxml)
9
9
  docxml.split(%r{(<passthrough>|</passthrough>)}).each_slice(4)
10
10
  .map do |a|
11
- a.size > 2 and a[2] = HTMLEntities.new.decode(a[2])
12
- [a[0], a[2]]
11
+ a.size > 2 and a[2] = HTMLEntities.new.decode(a[2])
12
+ [a[0], a[2]]
13
13
  end.join
14
14
  end
15
15
 
@@ -84,8 +84,11 @@ module IsoDoc
84
84
  end
85
85
 
86
86
  def remove_bottom_border(cell)
87
+ # [^;]* (not +): the preceding property name is the unambiguous
88
+ # delimiter, so zero-or-more is equivalent and avoids polynomial
89
+ # backtracking on the value portion.
87
90
  cell["style"] =
88
- cell["style"].gsub(/border-bottom:[^;]+;/, "border-bottom:0pt;")
91
+ cell["style"].gsub(/border-bottom:[^;]*;/, "border-bottom:0pt;")
89
92
  end
90
93
 
91
94
  def table_get_or_make_tfoot(table)
@@ -112,7 +115,7 @@ module IsoDoc
112
115
  end
113
116
 
114
117
  TABLENOTE_CSS = "div[@class = 'Note' or @class = 'BlockSource' " \
115
- "or @class = 'TableFootnote' or @class = 'figdl' or @class = 'key']"
118
+ "or @class = 'TableFootnote' or @class = 'figdl' or @class = 'key']"
116
119
  .freeze
117
120
 
118
121
  def table_note_cleanup(docxml)
@@ -121,7 +124,7 @@ module IsoDoc
121
124
  insert_here = new_fullcolspan_row(t, tfoot)
122
125
  t.xpath("dl | p[@class = 'ListTitle'] | #{TABLENOTE_CSS}")
123
126
  .each do |d|
124
- d.parent = insert_here
127
+ d.parent = insert_here
125
128
  end
126
129
  end
127
130
  end
@@ -14,6 +14,7 @@ module IsoDoc
14
14
 
15
15
  def ul_parse(node, out)
16
16
  out.div(**attr_code(class: "ul_wrap")) do |div|
17
+ n = node.at(ns("./fmt-ul")) and return fmt_ul_parse(node, n, out)
17
18
  list_title_parse(node, div)
18
19
  div.ul(**attr_code(ul_attrs(node))) do |ul|
19
20
  node.children.each { |n| n.name == "fmt-name" or parse(n, ul) }
@@ -21,6 +22,13 @@ module IsoDoc
21
22
  end
22
23
  end
23
24
 
25
+ def fmt_ul_parse(elem, fmt_ul, out)
26
+ children_parse(fmt_ul, out)
27
+ output_elem = out.parent.elements.last
28
+ output_elem.parent["id"] = elem["id"]
29
+ s = keep_style(elem) and output_elem.parent["style"] = s
30
+ end
31
+
24
32
  OL_STYLE = {
25
33
  arabic: "1",
26
34
  roman: "i",
@@ -38,11 +46,13 @@ module IsoDoc
38
46
  { # type: node["type"] ? ol_style(node["type"].to_sym) : ol_depth(node),
39
47
  type: ol_style(node["type"]&.to_sym),
40
48
  start: node["start"],
41
- id: node["id"], style: keep_style(node) }
49
+ id: node["id"], style: keep_style(node)
50
+ }
42
51
  end
43
52
 
44
53
  def ol_parse(node, out)
45
54
  out.div(**attr_code(class: "ol_wrap")) do |div|
55
+ n = node.at(ns("./fmt-ol")) and return fmt_ul_parse(node, n, out)
46
56
  list_title_parse(node, div)
47
57
  div.ol(**attr_code(ol_attrs(node))) do |ol|
48
58
  node.children.each { |n| n.name == "fmt-name" or parse(n, ol) }
@@ -53,10 +63,10 @@ module IsoDoc
53
63
  def li_checkbox(node)
54
64
  if node["uncheckedcheckbox"] == "true"
55
65
  '<span class="zzMoveToFollowing">' \
56
- '<input type="checkbox" checked="checked"/></span>'
66
+ '<input type="checkbox" checked="checked"/></span>'
57
67
  elsif node["checkedcheckbox"] == "true"
58
68
  '<span class="zzMoveToFollowing">' \
59
- '<input type="checkbox"/></span>'
69
+ '<input type="checkbox"/></span>'
60
70
  else ""
61
71
  end
62
72
  end
@@ -70,7 +70,8 @@ module IsoDoc
70
70
  def clause_name(_node, title, div, header_class)
71
71
  header_class = {} if header_class.nil?
72
72
  div.h1(**attr_code(header_class)) do |h1|
73
- if title.is_a?(String) then h1 << title
73
+ if title.is_a?(String)
74
+ h1 << title
74
75
  elsif title
75
76
  children_parse(title, h1)
76
77
  clause_parse_subtitle(title, h1)
@@ -88,6 +89,7 @@ module IsoDoc
88
89
  end
89
90
 
90
91
  def variant_title(node, out)
92
+ # node["type"] == "toc" and return # used only for ToC!
91
93
  out.p(**attr_code(style: "display:none;",
92
94
  class: "variant-title-#{node['type']}")) do |p|
93
95
  children_parse(node, p)
@@ -1,4 +1,5 @@
1
1
  require "metanorma-utils"
2
+ require_relative "utils_img"
2
3
 
3
4
  module IsoDoc
4
5
  module Function
@@ -61,7 +62,7 @@ module IsoDoc
61
62
  end
62
63
 
63
64
  def to_xhtml_prep(xml)
64
- xml.gsub!(/<\?xml[^<>]*>/, "")
65
+ xml.gsub!(/\A<\?xml[^<>]*>\n?/, "")
65
66
  xml.include?("<!DOCTYPE ") || (xml = DOCTYPE_HDR + xml)
66
67
  numeric_escapes(xml)
67
68
  end
@@ -117,9 +118,12 @@ module IsoDoc
117
118
  end
118
119
 
119
120
  def header_strip(hdr)
121
+ # \s[^<>]* rather than \s[^<>]+: \s already guarantees one whitespace
122
+ # char after <p, so [^<>]* (zero or more) suffices for any attributes,
123
+ # and removes the polynomial interaction between \s and [^<>]+.
120
124
  h1 = to_xhtml_fragment(hdr.to_s.gsub(%r{<br\s*/>}, " ")
121
- .gsub(%r{</?p(\s[^<>]+)?>}, "")
122
- .gsub(/<\/?h[123456][^<>]*>/, "").gsub(/<\/?b[^<>]*>/, "").dup)
125
+ .gsub(%r{</?p(\s[^<>]*)?>}, "")
126
+ .gsub(/<\/?h[123456][^<>]*>/, "").dup)
123
127
  h1.traverse do |x|
124
128
  if x.name == "span" && x["style"]&.include?("mso-tab-count")
125
129
  x.replace(" ")
@@ -162,41 +166,6 @@ module IsoDoc
162
166
  .gsub("&lt;", "&#x3c;").gsub("&gt;", "&#x3e;").gsub("&amp;", "&#x26;")
163
167
  end
164
168
 
165
- def save_dataimage(uri, _relative_dir = true)
166
- %r{^data:(?<imgclass>image|application)/(?<imgtype>[^;]+);(?:charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri
167
- imgtype = "emf" if emf?("#{imgclass}/#{imgtype}")
168
- imgtype = imgtype.sub(/\+[a-z0-9]+$/, "") # svg+xml
169
- imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
170
- imgtype == "postscript" and imgtype = "eps"
171
- Tempfile.open(["image", ".#{imgtype}"],
172
- mode: File::BINARY | File::SHARE_DELETE) do |f|
173
- f.binmode
174
- f.write(Base64.strict_decode64(imgdata))
175
- @tempfile_cache << f # persist to the end
176
- f.path
177
- end
178
- end
179
-
180
- def save_svg(img)
181
- Tempfile.open(["image", ".svg"],
182
- mode: File::BINARY | File::SHARE_DELETE) do |f|
183
- f.write(img.to_xml)
184
- @tempfile_cache << f # persist to the end
185
- f.path
186
- end
187
- end
188
-
189
- def image_localfile(img)
190
- img.name == "svg" && !img["src"] and
191
- return save_svg(img)
192
- case img["src"]
193
- when /^data:/ then save_dataimage(img["src"], false)
194
- when %r{^([A-Z]:)?/} then img["src"]
195
- when nil then nil
196
- else File.join(@localdir, img["src"])
197
- end
198
- end
199
-
200
169
  LABELLED_ANCESTOR_ELEMENTS =
201
170
  %w(example requirement recommendation permission
202
171
  note table figure sourcecode).freeze
@@ -206,15 +175,6 @@ module IsoDoc
206
175
  .intersection(LABELLED_ANCESTOR_ELEMENTS - exceptions).empty?
207
176
  end
208
177
 
209
- def emf?(type)
210
- %w(application/emf application/x-emf image/x-emf image/x-mgx-emf
211
- application/x-msmetafile image/x-xbitmap image/emf).include? type
212
- end
213
-
214
- def eps?(type)
215
- %w(application/postscript image/x-eps).include? type
216
- end
217
-
218
178
  def cleanup_entities(text, is_xml: true)
219
179
  c = HTMLEntities.new
220
180
  if is_xml
@@ -234,10 +194,6 @@ module IsoDoc
234
194
  path[/\s/] ? "\"#{path}\"" : path
235
195
  end
236
196
 
237
- def imgfile_suffix(uri, suffix)
238
- "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.#{suffix}"
239
- end
240
-
241
197
  # Unescape &#x26; to & in href attributes only
242
198
  # This ensures URLs work correctly while preserving &#x26; in text content
243
199
  # This operates on the final string output after all Nokogiri processing
@@ -254,6 +210,34 @@ module IsoDoc
254
210
  end
255
211
  end
256
212
  end
213
+
214
+ # parse CSV-encoded key=value attribute
215
+ COMMA_PLACEHOLDER = "##COMMA##".freeze
216
+
217
+ # Temporarily replace commas inside quotes with a placeholder
218
+ def comma_placeholder(options)
219
+ processed = ""
220
+ in_quotes = false
221
+ options.each_char do |c|
222
+ c == "'" and in_quotes = !in_quotes
223
+ processed << if c == "," && in_quotes
224
+ COMMA_PLACEHOLDER
225
+ else c
226
+ end
227
+ end
228
+ processed
229
+ end
230
+
231
+ def csv_attribute_extract(options)
232
+ options.gsub!(/([a-z_]+)='/, %('\\1=))
233
+ processed = comma_placeholder(options)
234
+ CSV.parse_line(processed, quote_char: "'")
235
+ .each_with_object({}) do |x, acc|
236
+ x.gsub!(COMMA_PLACEHOLDER, ",")
237
+ m = /^(.+?)=(.+)?$/.match(x) or next
238
+ acc[m[1].to_sym] = m[2].sub(/^(["'])(.+)\1$/, "\\2")
239
+ end
240
+ end
257
241
  end
258
242
  end
259
243
  end
@@ -0,0 +1,55 @@
1
+ require "metanorma-utils"
2
+
3
+ module IsoDoc
4
+ module Function
5
+ module Utils
6
+ def save_dataimage(uri, _relative_dir = true)
7
+ %r{^data:(?<imgclass>image|application)/(?<imgtype>[^;]+);(?:charset=[^;]+;)?base64,(?<imgdata>.+)$} =~ uri
8
+ imgtype = "emf" if emf?("#{imgclass}/#{imgtype}")
9
+ imgtype = imgtype.sub(/\+[a-z0-9]+$/, "") # svg+xml
10
+ imgtype = "png" unless /^[a-z0-9]+$/.match? imgtype
11
+ imgtype == "postscript" and imgtype = "eps"
12
+ Tempfile.open(["image", ".#{imgtype}"],
13
+ mode: File::BINARY | File::SHARE_DELETE) do |f|
14
+ f.binmode
15
+ f.write(Base64.strict_decode64(imgdata))
16
+ @tempfile_cache << f # persist to the end
17
+ f.path
18
+ end
19
+ end
20
+
21
+ def save_svg(img)
22
+ Tempfile.open(["image", ".svg"],
23
+ mode: File::BINARY | File::SHARE_DELETE) do |f|
24
+ f.write(img.to_xml)
25
+ @tempfile_cache << f # persist to the end
26
+ f.path
27
+ end
28
+ end
29
+
30
+ def image_localfile(img)
31
+ img.name == "svg" && !img["src"] and
32
+ return save_svg(img)
33
+ case img["src"]
34
+ when /^data:/ then save_dataimage(img["src"], false)
35
+ when %r{^([A-Z]:)?/} then img["src"]
36
+ when nil then nil
37
+ else File.join(@localdir, img["src"])
38
+ end
39
+ end
40
+
41
+ def emf?(type)
42
+ %w(application/emf application/x-emf image/x-emf image/x-mgx-emf
43
+ application/x-msmetafile image/x-xbitmap image/emf).include? type
44
+ end
45
+
46
+ def eps?(type)
47
+ %w(application/postscript image/x-eps).include? type
48
+ end
49
+
50
+ def imgfile_suffix(uri, suffix)
51
+ "#{File.join(File.dirname(uri), File.basename(uri, '.*'))}.#{suffix}"
52
+ end
53
+ end
54
+ end
55
+ end
@@ -111,15 +111,18 @@ module IsoDoc
111
111
  end
112
112
 
113
113
  def compile_scss(filename)
114
- load_scss_paths(filename)
114
+ load_paths = load_scss_paths(filename)
115
115
  Dir.mktmpdir do |dir|
116
116
  File.write(File.join(dir, "variables.scss"), fonts_placeholder)
117
- SassC.load_paths << dir
118
117
  sheet_content = File.read(filename, encoding: "UTF-8")
119
- .gsub(%r<([a-z])\.([0-9])(?=[^{}]*{)>m, "\\1.__WORD__\\2")
118
+ # Drop the m flag and use [^{}\n]* so the lookahead is bounded to
119
+ # one line, preventing O(n^2) scanning across multi-line inputs.
120
+ .gsub(/([a-z])\.([0-9])(?=[^{}\n]*{)/, "\\1.__WORD__\\2")
120
121
  SassC::Engine.new(%<@use "variables" as *;\n#{sheet_content}>,
121
- syntax: :scss, quiet_deps: true, importer: SasscImporter)
122
- .render.gsub(/__WORD__/, "")
122
+ syntax: :scss, quiet_deps: true,
123
+ load_paths: load_paths + [dir],
124
+ importer: SasscImporter)
125
+ .render.gsub("__WORD__", "")
123
126
  end
124
127
  end
125
128
 
@@ -131,10 +134,7 @@ module IsoDoc
131
134
  "lib", "isodoc")
132
135
  else File.join("lib", "isodoc")
133
136
  end
134
- [isodoc_path,
135
- File.dirname(filename)].each do |name|
136
- SassC.load_paths << name
137
- end
137
+ [isodoc_path, File.dirname(filename)]
138
138
  end
139
139
 
140
140
  def compile_scss_task(current_task)
@@ -24,8 +24,8 @@ module IsoDoc
24
24
  end
25
25
 
26
26
  def html5(doc)
27
- doc.sub(%r{<!DOCTYPE html [^<>]+>}, "<!DOCTYPE html>")
28
- .sub(%r{<\?xml[^<>]+>}, "")
27
+ doc.sub(%r{\A<!DOCTYPE html [^<>]+>}, "<!DOCTYPE html>")
28
+ .sub(%r{\A<\?xml[^<>]+>}, "")
29
29
  end
30
30
 
31
31
  def html_cleanup(html)
@@ -6,14 +6,17 @@ module IsoDoc
6
6
  module HtmlFunction
7
7
  module Html
8
8
  def script_cdata(result)
9
- result.gsub(%r{<script([^<>]*)>\s*<!\[CDATA\[}m, "<script\\1>")
10
- .gsub(%r{\]\]>\s*</script>}, "</script>")
11
- .gsub(%r{<!\[CDATA\[\s*<script([^<>]*)>}m, "<script\\1>")
12
- .gsub(%r{</script>\s*\]\]>}, "</script>")
13
- .gsub(%r{<style([^<>]*)>\s*<!\[CDATA\[}m, "<style\\1>")
14
- .gsub(%r{\]\]>\s*</style>}, "</style>")
15
- .gsub(%r{<!\[CDATA\[\s*<style([^<>]*)>}m, "<style\\1>")
16
- .gsub(%r{</style>\s*\]\]>}, "</style>")
9
+ # Use explicit [ \t\n\r]* rather than \s* with the m flag:
10
+ # [^<>]* and [ \t\n\r]* are disjoint character classes (whitespace
11
+ # chars are not < or >), preventing polynomial backtracking.
12
+ result.gsub(%r{<script([^<>]*)>[ \t\n\r]*<!\[CDATA\[}, "<script\\1>")
13
+ .gsub(%r{\]\]>[ \t\n\r]*</script>}, "</script>")
14
+ .gsub(%r{<!\[CDATA\[[ \t\n\r]*<script([^<>]*)>}, "<script\\1>")
15
+ .gsub(%r{</script>[ \t\n\r]*\]\]>}, "</script>")
16
+ .gsub(%r{<style([^<>]*)>[ \t\n\r]*<!\[CDATA\[}, "<style\\1>")
17
+ .gsub(%r{\]\]>[ \t\n\r]*</style>}, "</style>")
18
+ .gsub(%r{<!\[CDATA\[[ \t\n\r]*<style([^<>]*)>}, "<style\\1>")
19
+ .gsub(%r{</style>[ \t\n\r]*\]\]>}, "</style>")
17
20
  end
18
21
 
19
22
  def htmlstylesheet(file)
@@ -31,7 +34,7 @@ module IsoDoc
31
34
  head << Nokogiri::HTML.fragment("<style>#{htmlstylesheet(@htmlstylesheet)}</style>")
32
35
  s = htmlstylesheet(@htmlstylesheet_override) and head << Nokogiri::HTML.fragment("<style>#{s}</style>")
33
36
  s = @meta.get[:code_css] and
34
- head << Nokogiri::HTML.fragment("<style>#{s.gsub(/sourcecode/,
37
+ head << Nokogiri::HTML.fragment("<style>#{s.gsub('sourcecode',
35
38
  'pre.sourcecode')}</style>")
36
39
  @bare and
37
40
  head << "<style>body {margin-left: 2em; margin-right: 2em;}</style>"
@@ -0,0 +1,97 @@
1
+ module IsoDoc
2
+ class PresentationXMLConvert < ::IsoDoc::Convert
3
+ def annex(docxml)
4
+ docxml.xpath(ns("//annex")).each do |f|
5
+ @xrefs.klass.single_term_clause?(f) and single_term_clause_retitle(f)
6
+ annex1(f)
7
+ @xrefs.klass.single_term_clause?(f) and single_term_clause_unnest(f)
8
+ end
9
+ @xrefs.parse_inclusions(clauses: true).parse(docxml)
10
+ end
11
+
12
+ def annex1(elem)
13
+ lbl = @xrefs.anchor(elem["id"], :label, false)
14
+ orig_title = annex1_title_fmt(elem)
15
+ %w(title variant-title).each do |k|
16
+ k == "variant-title" && (t = elem.at(ns("./variant-title"))) &&
17
+ t["type"] != "_toc_temp" and next
18
+ # we only prefix variant-title we have just copied from title
19
+ annex1_title_prefix(elem, lbl, k)
20
+ end
21
+ annex1_title_postprocess(elem, orig_title)
22
+ end
23
+
24
+ def annex1_title_fmt(elem)
25
+ t = elem.at(ns("./title")) or return nil
26
+ unless elem.at(ns("./variant-title[@type='toc']"))
27
+ t.next = to_xml(t)
28
+ v = t.next
29
+ v.name = "variant-title"
30
+ v["type"] = "_toc_temp"
31
+ end
32
+ orig_title = to_xml(t.children)
33
+ t.children = annex1_title_fmt_inline(t)
34
+ orig_title
35
+ end
36
+
37
+ def annex1_title_fmt_inline(title)
38
+ "<strong>#{to_xml(title.children)}</strong>"
39
+ end
40
+
41
+ def annex1_title_prefix(elem, lbl, tag)
42
+ delim, lbl = annex1_title_prefix_prep(elem, lbl, tag)
43
+ if unnumbered_clause?(elem)
44
+ prefix_name(elem, {}, nil, tag)
45
+ else
46
+ prefix_name(elem, { caption: delim }, lbl, tag,
47
+ fmt_xref_label: tag == "title")
48
+ end
49
+ end
50
+
51
+ def annex1_title_prefix_prep(elem, lbl, tag)
52
+ delim = annex_delim_override(elem)
53
+ if tag == "variant-title" # strip obligation, boldface
54
+ lbl &&= lbl.gsub("<strong>", "").gsub("</strong>", "")
55
+ # Use (?:<br/>)* (non-capturing) and [^<]* instead of .+? so the
56
+ # obligation-span content is matched without polynomial backtracking.
57
+ # fmt-obligation spans contain plain text, so [^<]* is equivalent.
58
+ .sub(%r{(?:<br/>)*<span class=.fmt-obligation.>[^<]*</span>}, "")
59
+ delim = "<tab/>"
60
+ end
61
+ [delim, lbl]
62
+ end
63
+
64
+ # only accept variant-title we have just copied from title and prefixed
65
+ # into fmt-variant-title: that replaces the variant-title we have inserted
66
+ def annex1_title_postprocess(elem, orig_title)
67
+ v, t, v1 = annex1_variant_title_postprocess_prep(elem)
68
+ t&.children = orig_title
69
+ v1&.name == "fmt-variant-title" or return
70
+ v&.remove
71
+ v1.name = "variant-title"
72
+ v1["type"] = "toc"
73
+ s = v1.at(ns(".//semx[@element = 'variant-title']")) or return
74
+ s["element"] = "title"
75
+ t and s["source"] = t["id"]
76
+ end
77
+
78
+ def annex1_variant_title_postprocess_prep(elem)
79
+ v = elem.at(ns("./variant-title[@type='_toc_temp']"))
80
+ t = elem.at(ns("./title"))
81
+ # fmt-variant-title generated from variant-title
82
+ v1 = v&.next_element ||
83
+ # fmt-variant-title generated only from lbl
84
+ elem.at(ns("./fmt-variant-title"))
85
+ [v, t, v1]
86
+ end
87
+
88
+ def annex_delim_override(elem)
89
+ m = elem.document.root.at(ns("//presentation-metadata/annex-delim"))
90
+ m ? to_xml(m.children) : annex_delim(elem)
91
+ end
92
+
93
+ def annex_delim(_elem)
94
+ "<br/><br/>"
95
+ end
96
+ end
97
+ end
@@ -1,10 +1,10 @@
1
1
  module IsoDoc
2
2
  class PresentationXMLConvert < ::IsoDoc::Convert
3
- def prefix_name(node, delims, label, elem)
3
+ def prefix_name(node, delims, label, elem, fmt_xref_label: true)
4
4
  sem_xml_descendant?(node) and return
5
5
  label, delims = prefix_name_defaults(node, delims, label, elem)
6
6
  name, ins, ids, number = prefix_name_prep(node, elem)
7
- ins.next = fmt_xref_label(label, number, ids)
7
+ fmt_xref_label and ins.next = fmt_xref_label(label, number, ids)
8
8
  # autonum can be empty, e.g single note in clause: "NOTE []"
9
9
  number and node["autonum"] = number.gsub(/<[^>]+>/, "")
10
10
  !node.at(ns("./fmt-#{elem}")) &&
@@ -40,6 +40,7 @@ module IsoDoc
40
40
  node.at(ns("./sentinel"))&.remove
41
41
  strip_duplicate_ids(node, node.at(ns("./#{elem}")),
42
42
  node.at(ns("./fmt-#{elem}")))
43
+ node.at(ns("./fmt-#{elem}"))
43
44
  end
44
45
 
45
46
  def transfer_id(old, new)
@@ -67,7 +68,7 @@ module IsoDoc
67
68
  # remove ids duplicated between sem_title and pres_title
68
69
  # index terms are assumed transferred to pres_title from sem_title
69
70
  def strip_duplicate_ids(_node, sem_title, pres_title)
70
- sem_title && pres_title or return
71
+ (sem_title && pres_title) or return
71
72
  ids = gather_all_ids(pres_title)
72
73
  sem_title.xpath(".//*[@id]").each do |x|
73
74
  ids.include?(x["id"]) or next
@@ -78,7 +79,7 @@ module IsoDoc
78
79
  end
79
80
 
80
81
  def semx(node, label, element = "autonum")
81
- id = node["id"] || node[:id] || elem["original-id"]
82
+ id = node["id"] || node[:id] || node["original-id"]
82
83
  /<semx element='[^']+' source='#{id}'/.match?(label) and return label
83
84
  l = stripsemx(label)
84
85
  %(<semx element='#{element}' source='#{id}'>#{l}</semx>)
@@ -202,7 +203,7 @@ module IsoDoc
202
203
  def sem_xml_descendant_inline?(_node, ancestor_names)
203
204
  %w[xref eref origin link name title newcontent]
204
205
  .any? do |name|
205
- ancestor_names.include?(name)
206
+ ancestor_names.include?(name)
206
207
  end and return true
207
208
  end
208
209
 
@@ -213,7 +214,7 @@ module IsoDoc
213
214
  block?(node) and return false
214
215
  %w[preferred admitted deprecated related definition source]
215
216
  .any? do |name|
216
- ancestor_names.include?(name)
217
+ ancestor_names.include?(name)
217
218
  end
218
219
  end
219
220