isodoc 1.0.26 → 1.1.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 (64) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/macos.yml +12 -8
  3. data/.github/workflows/ubuntu.yml +26 -16
  4. data/.github/workflows/windows.yml +12 -8
  5. data/isodoc.gemspec +2 -2
  6. data/lib/isodoc.rb +2 -0
  7. data/lib/isodoc/common.rb +0 -4
  8. data/lib/isodoc/convert.rb +18 -8
  9. data/lib/isodoc/function/blocks.rb +43 -54
  10. data/lib/isodoc/function/blocks_example_note.rb +108 -0
  11. data/lib/isodoc/function/cleanup.rb +14 -2
  12. data/lib/isodoc/function/i18n.rb +1 -0
  13. data/lib/isodoc/function/inline.rb +76 -82
  14. data/lib/isodoc/function/inline_simple.rb +72 -0
  15. data/lib/isodoc/function/lists.rb +12 -6
  16. data/lib/isodoc/function/references.rb +65 -57
  17. data/lib/isodoc/function/reqt.rb +14 -5
  18. data/lib/isodoc/function/section.rb +8 -11
  19. data/lib/isodoc/function/table.rb +4 -5
  20. data/lib/isodoc/function/terms.rb +3 -3
  21. data/lib/isodoc/function/to_word_html.rb +22 -13
  22. data/lib/isodoc/function/utils.rb +9 -3
  23. data/lib/isodoc/headlesshtml_convert.rb +7 -6
  24. data/lib/isodoc/html_convert.rb +2 -1
  25. data/lib/isodoc/html_function/footnotes.rb +1 -1
  26. data/lib/isodoc/html_function/html.rb +16 -1
  27. data/lib/isodoc/html_function/postprocess.rb +6 -5
  28. data/lib/isodoc/metadata.rb +6 -0
  29. data/lib/isodoc/pdf_convert.rb +8 -6
  30. data/lib/isodoc/presentation_xml_convert.rb +29 -0
  31. data/lib/isodoc/version.rb +1 -1
  32. data/lib/isodoc/word_convert.rb +2 -1
  33. data/lib/isodoc/word_function/body.rb +14 -48
  34. data/lib/isodoc/word_function/footnotes.rb +1 -1
  35. data/lib/isodoc/word_function/inline.rb +75 -0
  36. data/lib/isodoc/word_function/postprocess.rb +1 -0
  37. data/lib/isodoc/word_function/table.rb +3 -3
  38. data/lib/isodoc/xref.rb +59 -0
  39. data/lib/isodoc/{function → xref}/xref_anchor.rb +10 -21
  40. data/lib/isodoc/xref/xref_counter.rb +74 -0
  41. data/lib/isodoc/{function → xref}/xref_gen.rb +9 -22
  42. data/lib/isodoc/{function → xref}/xref_gen_seq.rb +41 -32
  43. data/lib/isodoc/{function → xref}/xref_sect_gen.rb +33 -23
  44. data/lib/isodoc/xslfo_convert.rb +16 -4
  45. data/spec/assets/i18n.yaml +4 -1
  46. data/spec/assets/odf.emf +0 -0
  47. data/spec/assets/odf.svg +4 -0
  48. data/spec/assets/odf1.svg +4 -0
  49. data/spec/isodoc/blocks_spec.rb +240 -59
  50. data/spec/isodoc/cleanup_spec.rb +139 -17
  51. data/spec/isodoc/footnotes_spec.rb +20 -5
  52. data/spec/isodoc/inline_spec.rb +296 -1
  53. data/spec/isodoc/lists_spec.rb +8 -8
  54. data/spec/isodoc/metadata_spec.rb +110 -3
  55. data/spec/isodoc/postproc_spec.rb +10 -14
  56. data/spec/isodoc/presentation_xml_spec.rb +20 -0
  57. data/spec/isodoc/ref_spec.rb +119 -50
  58. data/spec/isodoc/section_spec.rb +84 -18
  59. data/spec/isodoc/table_spec.rb +28 -28
  60. data/spec/isodoc/terms_spec.rb +7 -7
  61. data/spec/isodoc/xref_spec.rb +177 -57
  62. metadata +24 -17
  63. data/lib/isodoc/function/blocks_example.rb +0 -53
  64. data/lib/isodoc/function/xref_counter.rb +0 -50
@@ -0,0 +1,108 @@
1
+ module IsoDoc::Function
2
+ module Blocks
3
+ def example_label(node, div, name)
4
+ n = @xrefs.get[node["id"]]
5
+ div.p **{ class: "example-title" } do |p|
6
+ lbl = (n.nil? || n[:label].nil? || n[:label].empty?) ? @example_lbl :
7
+ l10n("#{@example_lbl} #{n[:label]}")
8
+ p << lbl
9
+ name and !lbl.nil? and p << "&nbsp;&mdash; "
10
+ name and name.children.each { |n| parse(n, div) }
11
+ end
12
+ end
13
+
14
+ EXAMPLE_TBL_ATTR =
15
+ { class: "example_label", style: "width:82.8pt;padding:0 0 0 0;\
16
+ margin-left:0pt;vertical-align:top;" }.freeze
17
+
18
+ def example_div_attr(node)
19
+ attr_code(id: node["id"], class: "example", style: keep_style(node))
20
+ end
21
+
22
+ # used if we are boxing examples
23
+ def example_div_parse(node, out)
24
+ out.div **example_div_attr(node) do |div|
25
+ example_label(node, div, node.at(ns("./name")))
26
+ node.children.each do |n|
27
+ parse(n, div) unless n.name == "name"
28
+ end
29
+ end
30
+ end
31
+
32
+ def example_table_attr(node)
33
+ attr_code(id: node["id"], class: "example",
34
+ style: "border-collapse:collapse;border-spacing:0;"\
35
+ "#{keep_style(node)}" )
36
+ end
37
+
38
+ EXAMPLE_TD_ATTR =
39
+ { style: "vertical-align:top;padding:0;", class: "example" }.freeze
40
+
41
+ def example_table_parse(node, out)
42
+ out.table **example_table_attr(node) do |t|
43
+ t.tr do |tr|
44
+ tr.td **EXAMPLE_TBL_ATTR do |td|
45
+ example_label(node, td, node.at(ns("./name")))
46
+ end
47
+ tr.td **EXAMPLE_TD_ATTR do |td|
48
+ node.children.each { |n| parse(n, td) unless n.name == "name" }
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def example_parse(node, out)
55
+ example_div_parse(node, out)
56
+ end
57
+
58
+ def note_label(node)
59
+ n = @xrefs.get[node["id"]]
60
+ return @note_lbl if n.nil? || n[:label].nil? || n[:label].empty?
61
+ l10n("#{@note_lbl} #{n[:label]}")
62
+ end
63
+
64
+ def note_p_parse(node, div)
65
+ div.p do |p|
66
+ p.span **{ class: "note_label" } do |s|
67
+ s << note_label(node)
68
+ end
69
+ insert_tab(p, 1)
70
+ node.first_element_child.children.each { |n| parse(n, p) }
71
+ end
72
+ node.element_children[1..-1].each { |n| parse(n, div) }
73
+ end
74
+
75
+ def note_parse1(node, div)
76
+ div.p do |p|
77
+ p.span **{ class: "note_label" } do |s|
78
+ s << note_label(node)
79
+ end
80
+ insert_tab(p, 1)
81
+ end
82
+ node.children.each { |n| parse(n, div) }
83
+ end
84
+
85
+ def keep_style(node)
86
+ ret = ""
87
+ node["keep-with-next"] == "true" and
88
+ ret += "page-break-after: avoid;"
89
+ node["keep-lines-together"] == "true" and
90
+ ret += "page-break-inside: avoid;"
91
+ return nil if ret.empty?
92
+ ret
93
+ end
94
+
95
+ def note_attrs(node)
96
+ attr_code(id: node["id"], class: "Note", style: keep_style(node))
97
+ end
98
+
99
+ def note_parse(node, out)
100
+ @note = true
101
+ out.div **note_attrs(node) do |div|
102
+ node.first_element_child.name == "p" ?
103
+ note_p_parse(node, div) : note_parse1(node, div)
104
+ end
105
+ @note = false
106
+ end
107
+ end
108
+ end
@@ -1,13 +1,25 @@
1
1
  module IsoDoc::Function
2
2
  module Cleanup
3
3
  def textcleanup(docxml)
4
+ docxml = termref_cleanup(passthrough_cleanup(docxml))
5
+ end
6
+
7
+ def termref_cleanup(docxml)
4
8
  docxml.
9
+ gsub(%r{\s*\[/TERMREF\]\s*</p>\s*<p>\s*\[TERMREF\]}, "; ").
5
10
  gsub(/\[TERMREF\]\s*/, l10n("[#{@source_lbl}: ")).
6
11
  gsub(/\s*\[MODIFICATION\]\s*\[\/TERMREF\]/, l10n(", #{@modified_lbl} [/TERMREF]")).
7
- gsub(/\s*\[\/TERMREF\]\s*/, l10n("]")).
12
+ gsub(%r{\s*\[\/TERMREF\]\s*}, l10n("]")).
8
13
  gsub(/\s*\[MODIFICATION\]/, l10n(", #{@modified_lbl} &mdash; "))
9
14
  end
10
15
 
16
+ def passthrough_cleanup(docxml)
17
+ docxml.split(%r{(<passthrough>|</passthrough>)}).each_slice(4).map do |a|
18
+ a.size > 2 and a[2] = HTMLEntities.new.decode(a[2])
19
+ [a[0], a[2]]
20
+ end.join
21
+ end
22
+
11
23
  def cleanup(docxml)
12
24
  comment_cleanup(docxml)
13
25
  footnote_cleanup(docxml)
@@ -118,7 +130,7 @@ module IsoDoc::Function
118
130
  end
119
131
 
120
132
  def footnote_cleanup(docxml)
121
- docxml.xpath('//a[@epub:type = "footnote"]/sup').each_with_index do |x, i|
133
+ docxml.xpath('//a[@class = "FootnoteRef"]/sup').each_with_index do |x, i|
122
134
  x.content = (i + 1).to_s
123
135
  end
124
136
  docxml
@@ -87,6 +87,7 @@ module IsoDoc::Function
87
87
 
88
88
  # TODO: move to localization file
89
89
  def eref_localities1(target, type, from, to, delim, lang = "en")
90
+ return "" if type == "anchor"
90
91
  return l10n(eref_localities1_zh(target, type, from, to, delim)) if lang == "zh"
91
92
  ret = delim
92
93
  loc = @locality[type] || type.sub(/^locality:/, "").capitalize
@@ -1,28 +1,7 @@
1
+ require_relative "inline_simple"
2
+
1
3
  module IsoDoc::Function
2
4
  module Inline
3
- def section_break(body)
4
- body.br
5
- end
6
-
7
- def page_break(out)
8
- out.br
9
- end
10
-
11
- def pagebreak_parse(_node, out)
12
- out.br
13
- end
14
-
15
- def hr_parse(node, out)
16
- out.hr
17
- end
18
-
19
- def br_parse(node, out)
20
- out.br
21
- end
22
-
23
- def index_parse(node, out)
24
- end
25
-
26
5
  def link_parse(node, out)
27
6
  out.a **attr_code(href: node["target"], title: node["alt"]) do |l|
28
7
  if node.text.empty?
@@ -38,29 +17,51 @@ module IsoDoc::Function
38
17
  end
39
18
 
40
19
  def prefix_container(container, linkend, _target)
41
- l10n(anchor(container, :xref) + ", " + linkend)
20
+ l10n(@xrefs.anchor(container, :xref) + ", " + linkend)
42
21
  end
43
22
 
44
23
  def anchor_linkend(node, linkend)
45
24
  if node["citeas"].nil? && node["bibitemid"]
46
- return anchor(node["bibitemid"] ,:xref) || "???"
25
+ return @xrefs.anchor(node["bibitemid"] ,:xref) || "???"
47
26
  elsif node["target"] && !/.#./.match(node["target"])
48
- linkend = anchor(node["target"], :xref)
49
- container = anchor(node["target"], :container, false)
27
+ linkend = @xrefs.anchor(node["target"], :xref)
28
+ container = @xrefs.anchor(node["target"], :container, false)
50
29
  (container && get_note_container_id(node) != container &&
51
- @anchors[node["target"]]) &&
30
+ @xrefs.get[node["target"]]) &&
52
31
  linkend = prefix_container(container, linkend, node["target"])
32
+ linkend = capitalise_xref(node, linkend)
53
33
  end
54
34
  linkend || "???"
55
35
  end
56
36
 
37
+ def capitalise_xref(node, linkend)
38
+ return linkend unless %w(Latn Cyrl Grek).include? @script
39
+ return linkend&.capitalize if node["case"] == "capital"
40
+ return linkend&.downcase if node["case"] == "lowercase"
41
+ return linkend if linkend[0,1].match(/\p{Upper}/)
42
+ prec = nearest_block_parent(node).xpath("./descendant-or-self::text()") &
43
+ node.xpath("./preceding::text()")
44
+ (prec.empty? || /(?!<[^.].)\.\s+$/.match(prec.map { |p| p.text }.join)) ?
45
+ linkend&.capitalize : linkend
46
+ end
47
+
48
+ def nearest_block_parent(node)
49
+ until %w(p title td th name formula
50
+ li dt dd sourcecode pre).include?(node.name)
51
+ node = node.parent
52
+ end
53
+ node
54
+ end
55
+
57
56
  def get_linkend(node)
58
- contents = node.children.select { |c| !%w{locality localityStack}.include? c.name }.
59
- select { |c| !c.text? || /\S/.match(c) }
57
+ contents = node.children.select do |c|
58
+ !%w{locality localityStack}.include? c.name
59
+ end.select { |c| !c.text? || /\S/.match(c) }
60
60
  !contents.empty? and
61
61
  return Nokogiri::XML::NodeSet.new(node.document, contents).to_xml
62
62
  link = anchor_linkend(node, docid_l10n(node["target"] || node["citeas"]))
63
- link + eref_localities(node.xpath(ns("./locality | ./localityStack")), link)
63
+ link + eref_localities(node.xpath(ns("./locality | ./localityStack")),
64
+ link)
64
65
  # so not <origin bibitemid="ISO7301" citeas="ISO 7301">
65
66
  # <locality type="section"><reference>3.1</reference></locality></origin>
66
67
  end
@@ -83,27 +84,44 @@ module IsoDoc::Function
83
84
  end
84
85
  else
85
86
  ret += eref_localities0(r, i, target, delim)
86
- end
87
+ end
87
88
  end
88
89
  ret
89
90
  end
90
91
 
91
92
  def eref_localities0(r, i, target, delim)
92
- if r["type"] == "whole" then l10n("#{delim} #{@whole_of_text}")
93
+ if r["type"] == "whole" then l10n("#{delim} #{@wholeoftext_lbl}")
93
94
  else
94
95
  eref_localities1(target, r["type"], r.at(ns("./referenceFrom")),
95
96
  r.at(ns("./referenceTo")), delim, @lang)
96
97
  end
97
98
  end
98
99
 
100
+ def suffix_url(url)
101
+ return url if %r{^http[s]?://}.match(url)
102
+ url.sub(/#{File.extname(url)}$/, ".html")
103
+ end
104
+
105
+ def eref_target(node)
106
+ href = "#" + node["bibitemid"]
107
+ url = node.at(ns("//bibitem[@id = '#{node['bibitemid']}']/"\
108
+ "uri[@type = 'citation']"))
109
+ return href unless url
110
+ href = suffix_url(url.text)
111
+ anchor = node&.at(ns(".//locality[@type = 'anchor']"))&.text
112
+ anchor and href += "##{anchor}"
113
+ href
114
+ end
115
+
99
116
  def eref_parse(node, out)
100
117
  linkend = get_linkend(node)
118
+ href = eref_target(node)
101
119
  if node["type"] == "footnote"
102
120
  out.sup do |s|
103
- s.a(**{ "href": "#" + node["bibitemid"] }) { |l| l << linkend }
121
+ s.a(**{ "href": href }) { |l| l << linkend }
104
122
  end
105
123
  else
106
- out.a(**{ "href": "#" + node["bibitemid"] }) { |l| l << linkend }
124
+ out.a(**{ "href": href }) { |l| l << linkend }
107
125
  end
108
126
  end
109
127
 
@@ -120,8 +138,9 @@ module IsoDoc::Function
120
138
  end
121
139
 
122
140
  def concept_parse(node, out)
123
- content = node.first_element_child.children.select { |c| !%w{locality localityStack}.include? c.name }.
124
- select { |c| !c.text? || /\S/.match(c) }
141
+ content = node.first_element_child.children.select do |c|
142
+ !%w{locality localityStack}.include? c.name
143
+ end.select { |c| !c.text? || /\S/.match(c) }
125
144
  if content.empty?
126
145
  out << "[Term defined in "
127
146
  parse(node.first_element_child, out)
@@ -133,7 +152,8 @@ module IsoDoc::Function
133
152
 
134
153
  def stem_parse(node, out)
135
154
  ooml = if node["type"] == "AsciiMath"
136
- "#{@openmathdelim}#{HTMLEntities.new.encode(node.text)}#{@closemathdelim}"
155
+ "#{@openmathdelim}#{HTMLEntities.new.encode(node.text)}"\
156
+ "#{@closemathdelim}"
137
157
  elsif node["type"] == "MathML" then node.first_element_child.to_s
138
158
  else
139
159
  HTMLEntities.new.encode(node.text)
@@ -175,57 +195,31 @@ module IsoDoc::Function
175
195
  out << text
176
196
  end
177
197
 
178
- def bookmark_parse(node, out)
179
- out.a **attr_code(id: node["id"])
180
- end
181
-
182
- def keyword_parse(node, out)
183
- out.span **{ class: "keyword" } do |s|
184
- node.children.each { |n| parse(n, s) }
185
- end
186
- end
187
-
188
- def em_parse(node, out)
189
- out.i do |e|
190
- node.children.each { |n| parse(n, e) }
191
- end
192
- end
193
-
194
- def strong_parse(node, out)
195
- out.b do |e|
196
- node.children.each { |n| parse(n, e) }
197
- end
198
- end
199
-
200
- def sup_parse(node, out)
201
- out.sup do |e|
202
- node.children.each { |n| parse(n, e) }
203
- end
204
- end
205
-
206
- def sub_parse(node, out)
207
- out.sub do |e|
208
- node.children.each { |n| parse(n, e) }
209
- end
210
- end
211
-
212
- def tt_parse(node, out)
213
- out.tt do |e|
214
- node.children.each { |n| parse(n, e) }
198
+ def error_parse(node, out)
199
+ text = node.to_xml.gsub(/</, "&lt;").gsub(/>/, "&gt;")
200
+ out.para do |p|
201
+ p.b(**{ role: "strong" }) { |e| e << text }
215
202
  end
216
203
  end
217
204
 
218
- def strike_parse(node, out)
219
- out.s do |e|
220
- node.children.each { |n| parse(n, e) }
205
+ def variant_parse(node, out)
206
+ if node["lang"] == @lang && node["script"] == @script
207
+ node.children.each { |n| parse(n, out) }
208
+ else
209
+ return if found_matching_variant_sibling(node)
210
+ return unless !node.at("./preceding-sibling::xmlns:variant")
211
+ node.children.each { |n| parse(n, out) }
221
212
  end
222
213
  end
223
214
 
224
- def error_parse(node, out)
225
- text = node.to_xml.gsub(/</, "&lt;").gsub(/>/, "&gt;")
226
- out.para do |p|
227
- p.b(**{ role: "strong" }) { |e| e << text }
215
+ def found_matching_variant_sibling(node)
216
+ prev = node.xpath("./preceding-sibling::xmlns:variant")
217
+ foll = node.xpath("./following-sibling::xmlns:variant")
218
+ found = false
219
+ (prev + foll).each do |n|
220
+ found = true if n["lang"] == @lang && n["script"] == @script
228
221
  end
222
+ found
229
223
  end
230
224
  end
231
225
  end
@@ -0,0 +1,72 @@
1
+ module IsoDoc::Function
2
+ module Inline
3
+ def section_break(body)
4
+ body.br
5
+ end
6
+
7
+ def page_break(out)
8
+ out.br
9
+ end
10
+
11
+ def pagebreak_parse(_node, out)
12
+ out.br
13
+ end
14
+
15
+ def hr_parse(node, out)
16
+ out.hr
17
+ end
18
+
19
+ def br_parse(node, out)
20
+ out.br
21
+ end
22
+
23
+ def index_parse(node, out)
24
+ end
25
+
26
+ def bookmark_parse(node, out)
27
+ out.a **attr_code(id: node["id"])
28
+ end
29
+
30
+ def keyword_parse(node, out)
31
+ out.span **{ class: "keyword" } do |s|
32
+ node.children.each { |n| parse(n, s) }
33
+ end
34
+ end
35
+
36
+ def em_parse(node, out)
37
+ out.i do |e|
38
+ node.children.each { |n| parse(n, e) }
39
+ end
40
+ end
41
+
42
+ def strong_parse(node, out)
43
+ out.b do |e|
44
+ node.children.each { |n| parse(n, e) }
45
+ end
46
+ end
47
+
48
+ def sup_parse(node, out)
49
+ out.sup do |e|
50
+ node.children.each { |n| parse(n, e) }
51
+ end
52
+ end
53
+
54
+ def sub_parse(node, out)
55
+ out.sub do |e|
56
+ node.children.each { |n| parse(n, e) }
57
+ end
58
+ end
59
+
60
+ def tt_parse(node, out)
61
+ out.tt do |e|
62
+ node.children.each { |n| parse(n, e) }
63
+ end
64
+ end
65
+
66
+ def strike_parse(node, out)
67
+ out.s do |e|
68
+ node.children.each { |n| parse(n, e) }
69
+ end
70
+ end
71
+ end
72
+ end