coradoc 0.3.0 → 1.0.0

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -0
  3. data/exe/reverse_adoc +24 -3
  4. data/lib/coradoc/document.rb +1 -0
  5. data/lib/coradoc/element/admonition.rb +2 -2
  6. data/lib/coradoc/element/attribute.rb +2 -2
  7. data/lib/coradoc/element/attribute_list.rb +94 -15
  8. data/lib/coradoc/element/audio.rb +13 -2
  9. data/lib/coradoc/element/author.rb +4 -2
  10. data/lib/coradoc/element/base.rb +70 -7
  11. data/lib/coradoc/element/block/core.rb +8 -4
  12. data/lib/coradoc/element/block/quote.rb +1 -1
  13. data/lib/coradoc/element/break.rb +1 -1
  14. data/lib/coradoc/element/document_attributes.rb +6 -6
  15. data/lib/coradoc/element/header.rb +4 -2
  16. data/lib/coradoc/element/image/block_image.rb +13 -2
  17. data/lib/coradoc/element/image/core.rb +34 -5
  18. data/lib/coradoc/element/image/inline_image.rb +2 -2
  19. data/lib/coradoc/element/inline/anchor.rb +4 -2
  20. data/lib/coradoc/element/inline/bold.rb +9 -4
  21. data/lib/coradoc/element/inline/cross_reference.rb +4 -2
  22. data/lib/coradoc/element/inline/hard_line_break.rb +1 -1
  23. data/lib/coradoc/element/inline/highlight.rb +11 -6
  24. data/lib/coradoc/element/inline/italic.rb +9 -4
  25. data/lib/coradoc/element/inline/link.rb +22 -6
  26. data/lib/coradoc/element/inline/monospace.rb +9 -4
  27. data/lib/coradoc/element/inline/quotation.rb +3 -1
  28. data/lib/coradoc/element/inline/subscript.rb +4 -2
  29. data/lib/coradoc/element/inline/superscript.rb +4 -2
  30. data/lib/coradoc/element/list/core.rb +9 -6
  31. data/lib/coradoc/element/list/definition.rb +19 -0
  32. data/lib/coradoc/element/list/ordered.rb +1 -1
  33. data/lib/coradoc/element/list/unordered.rb +1 -1
  34. data/lib/coradoc/element/list.rb +1 -0
  35. data/lib/coradoc/element/list_item.rb +8 -3
  36. data/lib/coradoc/element/list_item_definition.rb +32 -0
  37. data/lib/coradoc/element/paragraph.rb +4 -2
  38. data/lib/coradoc/element/revision.rb +4 -2
  39. data/lib/coradoc/element/section.rb +21 -4
  40. data/lib/coradoc/element/table.rb +27 -10
  41. data/lib/coradoc/element/text_element.rb +48 -8
  42. data/lib/coradoc/element/title.rb +26 -6
  43. data/lib/coradoc/element/video.rb +32 -5
  44. data/lib/coradoc/reverse_adoc/README.adoc +14 -8
  45. data/lib/coradoc/reverse_adoc/cleaner.rb +20 -8
  46. data/lib/coradoc/reverse_adoc/config.rb +35 -16
  47. data/lib/coradoc/reverse_adoc/converters/a.rb +17 -12
  48. data/lib/coradoc/reverse_adoc/converters/aside.rb +0 -4
  49. data/lib/coradoc/reverse_adoc/converters/audio.rb +0 -4
  50. data/lib/coradoc/reverse_adoc/converters/base.rb +48 -44
  51. data/lib/coradoc/reverse_adoc/converters/blockquote.rb +2 -11
  52. data/lib/coradoc/reverse_adoc/converters/br.rb +0 -4
  53. data/lib/coradoc/reverse_adoc/converters/bypass.rb +0 -4
  54. data/lib/coradoc/reverse_adoc/converters/code.rb +5 -42
  55. data/lib/coradoc/reverse_adoc/converters/div.rb +0 -4
  56. data/lib/coradoc/reverse_adoc/converters/dl.rb +55 -0
  57. data/lib/coradoc/reverse_adoc/converters/em.rb +5 -43
  58. data/lib/coradoc/reverse_adoc/converters/figure.rb +0 -4
  59. data/lib/coradoc/reverse_adoc/converters/h.rb +0 -4
  60. data/lib/coradoc/reverse_adoc/converters/head.rb +0 -4
  61. data/lib/coradoc/reverse_adoc/converters/hr.rb +0 -4
  62. data/lib/coradoc/reverse_adoc/converters/img.rb +21 -16
  63. data/lib/coradoc/reverse_adoc/converters/li.rb +0 -4
  64. data/lib/coradoc/reverse_adoc/converters/mark.rb +5 -11
  65. data/lib/coradoc/reverse_adoc/converters/markup.rb +27 -0
  66. data/lib/coradoc/reverse_adoc/converters/ol.rb +0 -4
  67. data/lib/coradoc/reverse_adoc/converters/p.rb +0 -4
  68. data/lib/coradoc/reverse_adoc/converters/pre.rb +0 -4
  69. data/lib/coradoc/reverse_adoc/converters/q.rb +0 -4
  70. data/lib/coradoc/reverse_adoc/converters/strong.rb +5 -41
  71. data/lib/coradoc/reverse_adoc/converters/sub.rb +6 -4
  72. data/lib/coradoc/reverse_adoc/converters/sup.rb +7 -5
  73. data/lib/coradoc/reverse_adoc/converters/table.rb +215 -4
  74. data/lib/coradoc/reverse_adoc/converters/td.rb +1 -7
  75. data/lib/coradoc/reverse_adoc/converters/text.rb +1 -38
  76. data/lib/coradoc/reverse_adoc/converters/tr.rb +0 -4
  77. data/lib/coradoc/reverse_adoc/converters/video.rb +0 -4
  78. data/lib/coradoc/reverse_adoc/converters.rb +21 -0
  79. data/lib/coradoc/reverse_adoc/html_converter.rb +109 -20
  80. data/lib/coradoc/reverse_adoc/plugin.rb +131 -0
  81. data/lib/coradoc/reverse_adoc/plugins/plateau.rb +174 -0
  82. data/lib/coradoc/reverse_adoc/postprocessor.rb +148 -0
  83. data/lib/coradoc/reverse_adoc.rb +3 -0
  84. data/lib/coradoc/version.rb +1 -1
  85. data/lib/reverse_adoc.rb +1 -1
  86. metadata +8 -3
  87. data/lib/coradoc/element/inline/image.rb +0 -26
@@ -19,13 +19,15 @@ module Coradoc::ReverseAdoc
19
19
  end
20
20
 
21
21
  def datauri2file(src)
22
+ return unless src
23
+
22
24
  %r{^data:image/(?:[^;]+);base64,(?<imgdata>.+)$} =~ src
23
25
 
24
26
  dest_dir = Pathname.new(Coradoc::ReverseAdoc.config.destination).dirname
25
27
  images_dir = dest_dir.join("images")
26
28
  FileUtils.mkdir_p(images_dir)
27
29
 
28
- ext, image_src_path = determine_image_src_path(src, imgdata)
30
+ ext, image_src_path, tempfile = determine_image_src_path(src, imgdata)
29
31
  image_dest_path = images_dir + "#{image_number}.#{ext}"
30
32
 
31
33
  # puts "image_dest_path: #{image_dest_path.to_s}"
@@ -35,6 +37,8 @@ module Coradoc::ReverseAdoc
35
37
  image_number_increment
36
38
 
37
39
  image_dest_path.relative_path_from(dest_dir)
40
+ ensure
41
+ tempfile&.close!
38
42
  end
39
43
 
40
44
  def determine_image_src_path(src, imgdata)
@@ -45,13 +49,13 @@ module Coradoc::ReverseAdoc
45
49
  end
46
50
 
47
51
  def copy_temp_file(imgdata)
48
- Tempfile.open(["radoc", ".jpg"]) do |f|
49
- f.binmode
50
- f.write(Base64.strict_decode64(imgdata))
51
- f.rewind
52
- ext = Marcel::MimeType.for(f).sub(%r{^[^/]+/}, "")
53
- [ext, f.path]
54
- end
52
+ f = Tempfile.open(["radoc", ".jpg"])
53
+ f.binmode
54
+ f.write(Base64.strict_decode64(imgdata))
55
+ f.rewind
56
+ ext = Marcel::MimeType.for(f).sub(%r{^[^/]+/}, "")
57
+ ext = "svg" if ext == "svg+xml"
58
+ [ext, f.path, f]
55
59
  end
56
60
 
57
61
  def to_coradoc(node, _state = {})
@@ -61,6 +65,9 @@ module Coradoc::ReverseAdoc
61
65
  width = node["width"]
62
66
  height = node["height"]
63
67
 
68
+ width = width.to_i if width&.match?(/\A\d+\z/)
69
+ height = height.to_i if height&.match?(/\A\d+\z/)
70
+
64
71
  title = extract_title(node)
65
72
 
66
73
  if Coradoc::ReverseAdoc.config.external_images
@@ -73,18 +80,16 @@ module Coradoc::ReverseAdoc
73
80
  if alt # && !alt.to_s.empty?
74
81
  attributes.add_positional(alt)
75
82
  elsif width || height
76
- attributes.add_positional("\"\"")
83
+ attributes.add_positional(nil)
77
84
  end
78
- # attributes.add_named("title", title) if title
85
+ attributes.add_named("title", title) if title && !title.empty?
79
86
  attributes.add_positional(width) if width
80
87
  attributes.add_positional(height) if height
81
88
 
82
- Coradoc::Element::Image::BlockImage.new(title, id, src,
83
- attributes: attributes)
84
- end
85
-
86
- def convert(node, state = {})
87
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
89
+ if src
90
+ Coradoc::Element::Image::BlockImage.new(title, id, src,
91
+ attributes: attributes)
92
+ end
88
93
  end
89
94
  end
90
95
 
@@ -6,10 +6,6 @@ module Coradoc::ReverseAdoc
6
6
  content = treat_children_coradoc(node, state)
7
7
  Coradoc::Element::ListItem.new(content, id: id)
8
8
  end
9
-
10
- def convert(node, state = {})
11
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
12
- end
13
9
  end
14
10
 
15
11
  register :li, Li.new
@@ -1,18 +1,12 @@
1
1
  module Coradoc::ReverseAdoc
2
2
  module Converters
3
- class Mark < Base
4
- def to_coradoc(node, state = {})
5
- content = treat_children(node, state.merge(already_strong: true))
6
-
7
- if content.strip.empty? || state[:already_strong]
8
- return content
9
- end
10
-
11
- Coradoc::Element::Inline::Highlight.new(content, constrained?(node))
3
+ class Mark < Markup
4
+ def coradoc_class
5
+ Coradoc::Element::Inline::Highlight
12
6
  end
13
7
 
14
- def convert(node, state = {})
15
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
8
+ def markup_ancestor_tag_names
9
+ %w[mark]
16
10
  end
17
11
  end
18
12
 
@@ -0,0 +1,27 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Markup < Base
4
+ def to_coradoc(node, state = {})
5
+ u_before = unconstrained_before?(node)
6
+ u_after = unconstrained_after?(node)
7
+
8
+ leading_whitespace, trailing_whitespace =
9
+ extract_leading_trailing_whitespace(node)
10
+
11
+ content = treat_children_coradoc(node, state)
12
+
13
+ if node_has_ancestor?(node, markup_ancestor_tag_names)
14
+ content
15
+ elsif node.children.empty?
16
+ leading_whitespace.to_s
17
+ else
18
+ u = (u_before && leading_whitespace.nil?) ||
19
+ (u_after && trailing_whitespace.nil?)
20
+
21
+ e = coradoc_class.new(content, unconstrained: u)
22
+ [leading_whitespace, e, trailing_whitespace]
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -23,10 +23,6 @@ module Coradoc::ReverseAdoc
23
23
  end
24
24
  end
25
25
 
26
- def convert(node, state = {})
27
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
28
- end
29
-
30
26
  def get_list_type(node, _state)
31
27
  case node.name
32
28
  when "ol"
@@ -12,10 +12,6 @@ module Coradoc::ReverseAdoc
12
12
 
13
13
  Coradoc::Element::Paragraph.new(content, options)
14
14
  end
15
-
16
- def convert(node, state = {})
17
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
18
- end
19
15
  end
20
16
 
21
17
  register :p, P.new
@@ -22,10 +22,6 @@ module Coradoc::ReverseAdoc
22
22
  )
23
23
  end
24
24
 
25
- def convert(node, state = {})
26
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
27
- end
28
-
29
25
  private
30
26
 
31
27
  def treat(node, _state)
@@ -5,10 +5,6 @@ module Coradoc::ReverseAdoc
5
5
  content = treat_children(node, state)
6
6
  Coradoc::Element::Inline::Quotation.new(content)
7
7
  end
8
-
9
- def convert(node, state = {})
10
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
11
- end
12
8
  end
13
9
 
14
10
  register :q, Q.new
@@ -1,48 +1,12 @@
1
1
  module Coradoc::ReverseAdoc
2
2
  module Converters
3
- class Strong < Base
4
- def to_coradoc(node, state = {})
5
- content = treat_children_coradoc(node,
6
- state.merge(already_strong: true))
7
-
8
- if Coradoc::Generator.gen_adoc(content).strip.empty?
9
- return ""
10
- end
11
-
12
- if node_has_ancestor?(node, ["strong", "b"])
13
- return content
14
- end
15
-
16
- u_before = unconstrained_before?(node)
17
- u_after = unconstrained_after?(node)
18
- node.text =~ /^(\s+)/
19
- leading_whitespace = $1
20
- has_leading_whitespace = !leading_whitespace.nil?
21
-
22
- if has_leading_whitespace
23
- first_text = node.at_xpath("./text()[1]")
24
- first_text.replace(first_text.text.lstrip)
25
- leading_whitespace = u_before ? " " : " " # ########################33 somethings wrong in here
26
- end
27
-
28
- node.text =~ /(\s+)$/
29
- trailing_whitespace = $1
30
- has_trailing_whitespace = !trailing_whitespace.nil?
31
-
32
- if has_trailing_whitespace
33
- last_text = node.at_xpath("./text()[last()]")
34
- last_text.replace(last_text.text.rstrip)
35
- trailing_whitespace = u_after ? "" : "" ###############################
36
- end
37
-
38
- u = !((!u_before || has_leading_whitespace) && (!u_after || has_trailing_whitespace))
39
- e = Coradoc::Element::Inline::Bold.new(content, u)
40
-
41
- [leading_whitespace, e, trailing_whitespace]
3
+ class Strong < Markup
4
+ def coradoc_class
5
+ Coradoc::Element::Inline::Bold
42
6
  end
43
7
 
44
- def convert(node, state = {})
45
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
8
+ def markup_ancestor_tag_names
9
+ %w[strong b]
46
10
  end
47
11
  end
48
12
 
@@ -2,12 +2,14 @@ module Coradoc::ReverseAdoc
2
2
  module Converters
3
3
  class Sub < Base
4
4
  def to_coradoc(node, state = {})
5
+ leading_whitespace, trailing_whitespace = extract_leading_trailing_whitespace(node)
6
+
5
7
  content = treat_children_coradoc(node, state)
6
- Coradoc::Element::Inline::Subscript.new(content)
7
- end
8
8
 
9
- def convert(node, state = {})
10
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
9
+ return content if Coradoc::Generator.gen_adoc(content).strip.empty?
10
+
11
+ e = Coradoc::Element::Inline::Subscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
11
13
  end
12
14
  end
13
15
 
@@ -2,12 +2,14 @@ module Coradoc::ReverseAdoc
2
2
  module Converters
3
3
  class Sup < Base
4
4
  def to_coradoc(node, state = {})
5
- content = treat_children(node, state)
6
- Coradoc::Element::Inline::Superscript.new(content)
7
- end
5
+ leading_whitespace, trailing_whitespace = extract_leading_trailing_whitespace(node)
6
+
7
+ content = treat_children_coradoc(node, state)
8
+
9
+ return content if Coradoc::Generator.gen_adoc(content).strip.empty?
8
10
 
9
- def convert(node, state = {})
10
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
11
+ e = Coradoc::Element::Inline::Superscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
11
13
  end
12
14
  end
13
15
 
@@ -9,10 +9,6 @@ module Coradoc::ReverseAdoc
9
9
  Coradoc::Element::Table.new(title, content, { id: id, attrs: attrs })
10
10
  end
11
11
 
12
- def convert(node, state = {})
13
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
14
- end
15
-
16
12
  def extract_title(node)
17
13
  title = node.at("./caption")
18
14
  return "" if title.nil?
@@ -57,11 +53,226 @@ module Coradoc::ReverseAdoc
57
53
  rules_attr = rules(node)
58
54
  attrs.add_named("rules", rules_attr) if rules_attr
59
55
 
56
+ cols = ensure_row_column_integrity_and_get_column_sizes(node)
57
+ attrs.add_named("cols", cols)
58
+
59
+ # Header first rows can't span multiple riws - drop header if they do.
60
+ header = node.at_xpath(".//tr")
61
+ unless header.xpath("./td | ./th").all? { |i| [nil, "1", ""].include? i["rowspan"] }
62
+ attrs.add_named("options", ["noheader"])
63
+ end
64
+
60
65
  # This line should be removed.
61
66
  return "" if attrs.empty?
62
67
 
63
68
  attrs
64
69
  end
70
+
71
+ def ensure_row_column_integrity_and_get_column_sizes(node)
72
+ rows = node.xpath(".//tr")
73
+ num_rows = rows.length
74
+ computed_columns_per_row = [0] * num_rows
75
+ # Both those variables may seem the same, but they have a crucial
76
+ # difference.
77
+ #
78
+ # cell_references don't necessarily contain an image of created
79
+ # table. New cells are pushed to it as needed, so if we have a
80
+ # one cell with colspan and rowspan of 2 on the previous row,
81
+ # those will always be first in the row, regardless of their
82
+ # position. This array is only used to warrant the table
83
+ # integrity.
84
+ #
85
+ # cell_matrix, on the other hand, can't be constructed outright.
86
+ # It will be incorrect as long as the table isn't fixed. So we
87
+ # can't use it to correct colspans/rowspans/missing rows. Once
88
+ # we have it constructed, only then we can calculate the correct
89
+ # column widths.
90
+ cell_references = [nil] * num_rows
91
+ cell_matrix = [nil] * num_rows
92
+
93
+ recompute = proc do
94
+ return ensure_row_column_integrity_and_get_column_sizes(node)
95
+ end
96
+
97
+ fits_in_cell_matrix = proc do |y,x,rowspan,colspan|
98
+ rowspan.times.all? do |yy|
99
+ colspan.times.all? do |xx|
100
+ !cell_matrix.dig(y+yy, x+xx)
101
+ end
102
+ end
103
+ end
104
+
105
+ rows.each_with_index do |row, i|
106
+ columns = row.xpath("./td | ./th")
107
+ column_id = 0
108
+
109
+ columns.each do |cell|
110
+ colspan = cell["colspan"]&.to_i || 1
111
+ rowspan = cell["rowspan"]&.to_i || 1
112
+
113
+ column_id += 1 until fits_in_cell_matrix.(i,column_id,rowspan,colspan)
114
+
115
+ rowspan.times do |j|
116
+ # Let's increase the table for particularly bad documents
117
+ computed_columns_per_row[i + j] ||= 0
118
+ computed_columns_per_row[i + j] += colspan
119
+
120
+ cell_references[i + j] ||= []
121
+ cell_matrix[i + j] ||= []
122
+ colspan.times do |k|
123
+ cell_references[i + j] << [cell, k > 0]
124
+ cell_matrix[i + j][column_id] = cell
125
+ column_id += 1
126
+ end
127
+ column_id -= colspan
128
+ end
129
+ column_id += colspan
130
+ end
131
+ end
132
+
133
+ ##### Fixups
134
+ cpr = computed_columns_per_row
135
+
136
+ # Some cell has too high colspan
137
+ if cpr.length > num_rows
138
+ cell_references[num_rows].each do |cell,|
139
+ next unless cell
140
+
141
+ cell["rowspan"] = cell["rowspan"].to_i - 1
142
+ end
143
+
144
+ # Let's recompute the numbers
145
+ recompute.()
146
+ end
147
+
148
+ if [cpr.first] * num_rows != cpr
149
+ # Colspan inconsistencies
150
+ modified = false
151
+ cpr_max = cpr.max
152
+ max_rows = cell_references.sort_by(&:length).reverse
153
+ max_rows.each do |row|
154
+ break if row.length != cpr_max
155
+
156
+ cell, spanning = row.last
157
+
158
+ if spanning
159
+ modified = true
160
+ cell["colspan"] = cell["colspan"].to_i - 1
161
+ end
162
+ end
163
+
164
+ recompute.() if modified
165
+
166
+ # We are out of colspans to fix. If we are at this point, this
167
+ # means there is an inconsistent number of TH/TDs simply.
168
+ # Here, the solution is to add empty TDs.
169
+ min_rows = cell_references.sort_by(&:length)
170
+ cpr_min = cpr.min
171
+ min_rows.each do |row|
172
+ break if row.length != cpr_min
173
+
174
+ row_obj = row.last.first.parent
175
+ doc = row_obj.document
176
+ row_obj.add_child(Nokogiri::XML::Node.new("td", doc))
177
+
178
+ modified = true
179
+ end
180
+
181
+ recompute.() if modified
182
+
183
+ ### We should have a correct document now and we should never
184
+ ### end up here.
185
+ warn "**** Couldn't fix table sizes for table on line #{node.line}"
186
+ end
187
+
188
+ # For column size computation, we must have an integral cell_matrix.
189
+ # Let's verify that all columns and rows are populated.
190
+ cell_matrix_correct = cell_matrix.length.times.all? do |y|
191
+ cell_matrix[y].length.times.all? do |x|
192
+ cell_matrix[y][x]
193
+ end
194
+ end
195
+
196
+ unless cell_matrix_correct
197
+ warn <<~WARNING.gsub("\n", " ")
198
+ **** Couldn't construct a valid image of a table on line
199
+ #{node.line}. We need that to reliably compute column
200
+ widths of that table. Please report a bug to metanorma/coradoc
201
+ repository.
202
+ WARNING
203
+ end
204
+
205
+ # Compute column sizes
206
+ column_sizes = []
207
+ cell_matrix.each do |row|
208
+ row.each_with_index do |(cell,_), i|
209
+ next unless !cell || [nil, "", "1"].include?(cell["colspan"])
210
+
211
+ column_sizes[i] ||= []
212
+ column_sizes[i] << cell["width"]
213
+ end
214
+ end
215
+
216
+ document_width = Coradoc::ReverseAdoc.config.doc_width.to_r
217
+
218
+ column_sizes += [nil] * (cpr.first - column_sizes.length)
219
+
220
+ sizes = column_sizes.map do |col|
221
+ col = [] if col.nil?
222
+
223
+ max = col.map do |i|
224
+ if i.nil?
225
+ 0r
226
+ elsif i.end_with?("%")
227
+ document_width * i.to_i / 100
228
+ else
229
+ i.to_r
230
+ end
231
+ end.max
232
+
233
+ if max.nil? || max.negative?
234
+ 0r
235
+ else
236
+ max
237
+ end
238
+ end
239
+
240
+ # The table seems bigger than the document... let's scale all
241
+ # values.
242
+ while sizes.map { |i|
243
+ i.zero? ? document_width / 3 / sizes.length : i
244
+ }.sum > document_width
245
+
246
+ sizes = sizes.map { |i| i * 4 / 5 }
247
+ end
248
+
249
+ rest = document_width - sizes.sum
250
+ unset = sizes.count(&:zero?)
251
+
252
+ # Fill in zeros with remainder space
253
+ sizes = sizes.map do |i|
254
+ if i.zero?
255
+ rest / unset
256
+ else
257
+ i
258
+ end
259
+ end
260
+
261
+ # Scale to integers
262
+ lcm = sizes.map(&:denominator).inject(1) { |i,j| i.lcm(j) }
263
+ sizes = sizes.map { |i| i * lcm }.map(&:to_i)
264
+
265
+ # Scale down by gcd
266
+ gcd = sizes.inject(sizes.first) { |i,j| i.gcd(j) }
267
+ sizes = sizes.map { |i| i / gcd }
268
+
269
+ # Try to generate a shorter definition
270
+ if [sizes.first] * sizes.length == sizes
271
+ sizes.length
272
+ else
273
+ sizes.join(",")
274
+ end
275
+ end
65
276
  end
66
277
 
67
278
  register :table, Table.new
@@ -11,11 +11,10 @@ module Coradoc::ReverseAdoc
11
11
  singlepara = node.elements.size == 1 && node.elements.first.name == "p"
12
12
  state[:tdsinglepara] = singlepara if singlepara
13
13
 
14
- adoccell = node.at(".//ul | .//ol | .//pre | .//blockquote | .//br | .//hr") ||
14
+ adoccell = node.at(".//ul | .//ol | .//pre | .//blockquote | .//br | .//hr | .//img[@src]") ||
15
15
  node.at(".//p") && !singlepara
16
16
 
17
17
  style = "a" if adoccell
18
- delim = adoccell ? "\n" : " "
19
18
  content = treat_children_coradoc(node, state)
20
19
  options = {}.tap do |hash|
21
20
  hash[:id] = id
@@ -23,16 +22,11 @@ module Coradoc::ReverseAdoc
23
22
  hash[:alignattr] = alignattr
24
23
  hash[:style] = style
25
24
  hash[:content] = content
26
- hash[:delim] = delim
27
25
  end
28
26
 
29
27
  Coradoc::Element::Table::Cell.new(options)
30
28
  end
31
29
 
32
- def convert(node, state = {})
33
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
34
- end
35
-
36
30
  def cellstyle(_node)
37
31
  ""
38
32
  end
@@ -4,11 +4,7 @@ module Coradoc::ReverseAdoc
4
4
  def to_coradoc(node, state = {})
5
5
  return treat_empty(node, state) if node.text.strip.empty?
6
6
 
7
- Coradoc::Element::TextElement.new(treat_text(node))
8
- end
9
-
10
- def convert(node, state = {})
11
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
7
+ Coradoc::Element::TextElement.new(node.text)
12
8
  end
13
9
 
14
10
  private
@@ -25,39 +21,6 @@ module Coradoc::ReverseAdoc
25
21
  ""
26
22
  end
27
23
  end
28
-
29
- def treat_text(node)
30
- text = node.text
31
- text = preserve_nbsp(text)
32
- text = remove_border_newlines(text)
33
- text = remove_inner_newlines(text)
34
- text = escape_keychars(text)
35
-
36
- text = preserve_keychars_within_backticks(text)
37
- escape_links(text)
38
- end
39
-
40
- def preserve_nbsp(text)
41
- text.gsub(/\u00A0/, "&nbsp;")
42
- end
43
-
44
- def escape_links(text)
45
- text.gsub(/<<([^>]*)>>/, "\\<<\\1>>")
46
- end
47
-
48
- def remove_border_newlines(text)
49
- text.gsub(/\A\n+/, "").gsub(/\n+\z/, "")
50
- end
51
-
52
- def remove_inner_newlines(text)
53
- text.tr("\n\t", " ").squeeze(" ")
54
- end
55
-
56
- def preserve_keychars_within_backticks(text)
57
- text.gsub(/`.*?`/) do |match|
58
- match.gsub('\_', "_").gsub('\*', "*")
59
- end
60
- end
61
24
  end
62
25
 
63
26
  register :text, Text.new
@@ -7,10 +7,6 @@ module Coradoc::ReverseAdoc
7
7
  Coradoc::Element::Table::Row.new(content, header)
8
8
  end
9
9
 
10
- def convert(node, state = {})
11
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
12
- end
13
-
14
10
  def table_header_row?(node)
15
11
  # node.element_children.all? {|child| child.name.to_sym == :th}
16
12
  node.previous_element.nil?
@@ -12,10 +12,6 @@ module Coradoc::ReverseAdoc
12
12
  attributes: attributes)
13
13
  end
14
14
 
15
- def convert(node, state = {})
16
- Coradoc::Generator.gen_adoc(to_coradoc(node, state))
17
- end
18
-
19
15
  def options(node)
20
16
  autoplay = node["autoplay"]
21
17
  loop_attr = node["loop"]
@@ -13,6 +13,27 @@ module Coradoc::ReverseAdoc
13
13
  @@converters[tag_name.to_sym] or default_converter(tag_name)
14
14
  end
15
15
 
16
+ # Note: process won't run plugin hooks
17
+ def self.process(node, state)
18
+ node = node.to_a if node.is_a? Nokogiri::XML::NodeSet
19
+ return node.map { |i| process(i, state) }.join if node.is_a? Array
20
+
21
+ lookup(node.name).convert(node, state)
22
+ end
23
+
24
+ def self.process_coradoc(node, state)
25
+ node = node.to_a if node.is_a? Nokogiri::XML::NodeSet
26
+ return node.map { |i| process_coradoc(i, state) } if node.is_a? Array
27
+
28
+ plugins = state[:plugin_instances] || {}
29
+ process = proc { lookup(node.name).to_coradoc(node, state) }
30
+ plugins.each do |i|
31
+ prev_process = process
32
+ process = proc { i.html_tree_run_hooks(node, state, &prev_process) }
33
+ end
34
+ process.(node, state)
35
+ end
36
+
16
37
  def self.default_converter(tag_name)
17
38
  case Coradoc::ReverseAdoc.config.unknown_tags.to_sym
18
39
  when :pass_through