coradoc 0.3.0 → 1.0.0

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