coradoc 0.2.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/.docker/Dockerfile +1 -1
  3. data/.docker/docker-compose.yml +2 -2
  4. data/.editorconfig +15 -0
  5. data/CHANGELOG.md +4 -0
  6. data/README.md +4 -0
  7. data/Rakefile +10 -0
  8. data/coradoc.gemspec +11 -2
  9. data/exe/reverse_adoc +91 -0
  10. data/exe/w2a +72 -0
  11. data/lib/coradoc/document.rb +6 -6
  12. data/lib/coradoc/element/admonition.rb +8 -6
  13. data/lib/coradoc/element/attribute.rb +2 -2
  14. data/lib/coradoc/element/attribute_list.rb +94 -15
  15. data/lib/coradoc/element/audio.rb +14 -3
  16. data/lib/coradoc/element/author.rb +18 -14
  17. data/lib/coradoc/element/base.rb +69 -8
  18. data/lib/coradoc/element/block/core.rb +10 -6
  19. data/lib/coradoc/element/block/literal.rb +1 -1
  20. data/lib/coradoc/element/block/quote.rb +1 -1
  21. data/lib/coradoc/element/block/sourcecode.rb +2 -2
  22. data/lib/coradoc/element/break.rb +1 -1
  23. data/lib/coradoc/element/document_attributes.rb +6 -6
  24. data/lib/coradoc/element/header.rb +4 -2
  25. data/lib/coradoc/element/image/block_image.rb +13 -2
  26. data/lib/coradoc/element/image/core.rb +35 -5
  27. data/lib/coradoc/element/image/inline_image.rb +2 -2
  28. data/lib/coradoc/element/image.rb +0 -1
  29. data/lib/coradoc/element/inline/anchor.rb +4 -2
  30. data/lib/coradoc/element/inline/bold.rb +10 -4
  31. data/lib/coradoc/element/inline/cross_reference.rb +4 -2
  32. data/lib/coradoc/element/inline/hard_line_break.rb +1 -1
  33. data/lib/coradoc/element/inline/highlight.rb +12 -6
  34. data/lib/coradoc/element/inline/italic.rb +10 -4
  35. data/lib/coradoc/element/inline/link.rb +26 -10
  36. data/lib/coradoc/element/inline/monospace.rb +10 -4
  37. data/lib/coradoc/element/inline/quotation.rb +4 -1
  38. data/lib/coradoc/element/inline/subscript.rb +5 -2
  39. data/lib/coradoc/element/inline/superscript.rb +5 -2
  40. data/lib/coradoc/element/inline.rb +0 -1
  41. data/lib/coradoc/element/list/core.rb +10 -8
  42. data/lib/coradoc/element/list/definition.rb +19 -0
  43. data/lib/coradoc/element/list/ordered.rb +1 -1
  44. data/lib/coradoc/element/list/unordered.rb +1 -1
  45. data/lib/coradoc/element/list.rb +1 -1
  46. data/lib/coradoc/element/list_item.rb +9 -4
  47. data/lib/coradoc/element/list_item_definition.rb +32 -0
  48. data/lib/coradoc/element/paragraph.rb +5 -3
  49. data/lib/coradoc/element/revision.rb +20 -16
  50. data/lib/coradoc/element/section.rb +21 -4
  51. data/lib/coradoc/element/table.rb +36 -19
  52. data/lib/coradoc/element/text_element.rb +63 -17
  53. data/lib/coradoc/element/title.rb +27 -7
  54. data/lib/coradoc/element/video.rb +33 -6
  55. data/lib/coradoc/generator.rb +2 -2
  56. data/lib/coradoc/legacy_parser.rb +41 -41
  57. data/lib/coradoc/oscal.rb +2 -4
  58. data/lib/coradoc/parser/asciidoc/content.rb +15 -15
  59. data/lib/coradoc/parser/asciidoc/document_attributes.rb +1 -1
  60. data/lib/coradoc/parser/asciidoc/header.rb +6 -6
  61. data/lib/coradoc/parser/asciidoc/section.rb +1 -1
  62. data/lib/coradoc/reverse_adoc/LICENSE.txt +25 -0
  63. data/lib/coradoc/reverse_adoc/README.adoc +308 -0
  64. data/lib/coradoc/reverse_adoc/cleaner.rb +125 -0
  65. data/lib/coradoc/reverse_adoc/config.rb +73 -0
  66. data/lib/coradoc/reverse_adoc/converters/a.rb +47 -0
  67. data/lib/coradoc/reverse_adoc/converters/aside.rb +12 -0
  68. data/lib/coradoc/reverse_adoc/converters/audio.rb +25 -0
  69. data/lib/coradoc/reverse_adoc/converters/base.rb +104 -0
  70. data/lib/coradoc/reverse_adoc/converters/blockquote.rb +18 -0
  71. data/lib/coradoc/reverse_adoc/converters/br.rb +11 -0
  72. data/lib/coradoc/reverse_adoc/converters/bypass.rb +77 -0
  73. data/lib/coradoc/reverse_adoc/converters/code.rb +19 -0
  74. data/lib/coradoc/reverse_adoc/converters/div.rb +14 -0
  75. data/lib/coradoc/reverse_adoc/converters/dl.rb +55 -0
  76. data/lib/coradoc/reverse_adoc/converters/drop.rb +22 -0
  77. data/lib/coradoc/reverse_adoc/converters/em.rb +17 -0
  78. data/lib/coradoc/reverse_adoc/converters/figure.rb +21 -0
  79. data/lib/coradoc/reverse_adoc/converters/h.rb +38 -0
  80. data/lib/coradoc/reverse_adoc/converters/head.rb +19 -0
  81. data/lib/coradoc/reverse_adoc/converters/hr.rb +11 -0
  82. data/lib/coradoc/reverse_adoc/converters/ignore.rb +16 -0
  83. data/lib/coradoc/reverse_adoc/converters/img.rb +98 -0
  84. data/lib/coradoc/reverse_adoc/converters/li.rb +13 -0
  85. data/lib/coradoc/reverse_adoc/converters/mark.rb +15 -0
  86. data/lib/coradoc/reverse_adoc/converters/markup.rb +27 -0
  87. data/lib/coradoc/reverse_adoc/converters/math.rb +31 -0
  88. data/lib/coradoc/reverse_adoc/converters/ol.rb +60 -0
  89. data/lib/coradoc/reverse_adoc/converters/p.rb +19 -0
  90. data/lib/coradoc/reverse_adoc/converters/pass_through.rb +13 -0
  91. data/lib/coradoc/reverse_adoc/converters/pre.rb +51 -0
  92. data/lib/coradoc/reverse_adoc/converters/q.rb +12 -0
  93. data/lib/coradoc/reverse_adoc/converters/strong.rb +16 -0
  94. data/lib/coradoc/reverse_adoc/converters/sub.rb +18 -0
  95. data/lib/coradoc/reverse_adoc/converters/sup.rb +18 -0
  96. data/lib/coradoc/reverse_adoc/converters/table.rb +280 -0
  97. data/lib/coradoc/reverse_adoc/converters/td.rb +77 -0
  98. data/lib/coradoc/reverse_adoc/converters/text.rb +28 -0
  99. data/lib/coradoc/reverse_adoc/converters/th.rb +14 -0
  100. data/lib/coradoc/reverse_adoc/converters/tr.rb +18 -0
  101. data/lib/coradoc/reverse_adoc/converters/video.rb +25 -0
  102. data/lib/coradoc/reverse_adoc/converters.rb +53 -0
  103. data/lib/coradoc/reverse_adoc/errors.rb +10 -0
  104. data/lib/coradoc/reverse_adoc/html_converter.rb +150 -0
  105. data/lib/coradoc/reverse_adoc/plugin.rb +131 -0
  106. data/lib/coradoc/reverse_adoc/plugins/plateau.rb +174 -0
  107. data/lib/coradoc/reverse_adoc/postprocessor.rb +148 -0
  108. data/lib/coradoc/reverse_adoc.rb +30 -0
  109. data/lib/coradoc/transformer.rb +24 -14
  110. data/lib/coradoc/version.rb +1 -1
  111. data/lib/reverse_adoc.rb +20 -0
  112. metadata +184 -5
  113. data/lib/coradoc/element/inline/image.rb +0 -25
@@ -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
@@ -0,0 +1,31 @@
1
+ # Unless run with Coradoc::ReverseAdoc.config.mathml2asciimath,
2
+ # this is cheating: we're injecting MathML into Asciidoctor, but
3
+ # Asciidoctor only understands AsciiMath or LaTeX
4
+
5
+ require "mathml2asciimath"
6
+
7
+ module Coradoc::ReverseAdoc
8
+ module Converters
9
+ class Math < Base
10
+ # FIXIT
11
+ def to_coradoc(node, state = {})
12
+ convert(node, state)
13
+ end
14
+
15
+ def convert(node, _state = {})
16
+ stem = node.to_s.gsub(/\n/, " ")
17
+ stem = MathML2AsciiMath.m2a(stem) if Coradoc::ReverseAdoc.config.mathml2asciimath
18
+ unless stem.nil?
19
+ stem = stem.gsub(/\[/, "\\[").gsub(/\]/, "\\]").gsub(
20
+ /\(\(([^\)]+)\)\)/, "(\\1)"
21
+ )
22
+ end
23
+
24
+ # TODO: This is to be done in Coradoc
25
+ " stem:[" << stem << "] "
26
+ end
27
+ end
28
+
29
+ register :math, Math.new
30
+ end
31
+ end
@@ -0,0 +1,60 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Ol < Base
4
+ # FIXIT
5
+ def to_coradoc(node, state = {})
6
+ # convert(node, state)
7
+ id = node["id"]
8
+ ol_count = state.fetch(:ol_count, 0) + 1
9
+ attrs = ol_attrs(node)
10
+ items = treat_children_coradoc(node, state.merge(ol_count: ol_count))
11
+
12
+ options = {}.tap do |hash|
13
+ hash[:id] = id
14
+ hash[:ol_count] = ol_count
15
+ hash[:attrs] = attrs
16
+ end
17
+
18
+ case get_list_type(node, state)
19
+ when :ordered
20
+ Coradoc::Element::List::Ordered.new(items, options)
21
+ when :unordered
22
+ Coradoc::Element::List::Unordered.new(items, options)
23
+ end
24
+ end
25
+
26
+ def get_list_type(node, _state)
27
+ case node.name
28
+ when "ol"
29
+ :ordered
30
+ when "ul"
31
+ :unordered
32
+ end
33
+ end
34
+
35
+ def number_style(node)
36
+ case node["style"]
37
+ when "1" then "arabic"
38
+ when "A" then "upperalpha"
39
+ when "a" then "loweralpha"
40
+ when "I" then "upperroman"
41
+ when "i" then "lowerroman"
42
+ end
43
+ end
44
+
45
+ def ol_attrs(node)
46
+ attrs = Coradoc::Element::AttributeList.new
47
+ style = number_style(node)
48
+ attrs.add_positional(style) if style
49
+ attrs.add_positional("%reversed") if node["reversed"]
50
+ attrs.add_named("start", node["start"]) if node["start"]
51
+ attrs.add_named("type", node["type"]) if node["type"]
52
+ attrs
53
+ end
54
+ end
55
+
56
+ register :ol, Ol.new
57
+ register :ul, Ol.new
58
+ register :dir, Ol.new
59
+ end
60
+ end
@@ -0,0 +1,19 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class P < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ content = treat_children_coradoc(node, state)
7
+
8
+ options = {}.tap do |hash|
9
+ hash[:id] = id if id
10
+ hash[:tdsinglepara] = true if state[:tdsinglepara]
11
+ end
12
+
13
+ Coradoc::Element::Paragraph.new(content, options)
14
+ end
15
+ end
16
+
17
+ register :p, P.new
18
+ end
19
+ end
@@ -0,0 +1,13 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class PassThrough < Base
4
+ def to_coradoc(node, _state = {})
5
+ node.to_s
6
+ end
7
+
8
+ def convert(node, state = {})
9
+ to_coradoc(node, state)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,51 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Pre < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ lang = language(node)
7
+ content = treat_children(node, state)
8
+
9
+ unless lang
10
+ return Coradoc::Element::Block::Literal.new(
11
+ nil,
12
+ lines: content,
13
+ id: id,
14
+ )
15
+ end
16
+
17
+ Coradoc::Element::Block::SourceCode.new(
18
+ nil,
19
+ lines: content,
20
+ lang: lang,
21
+ id: id,
22
+ )
23
+ end
24
+
25
+ private
26
+
27
+ def treat(node, _state)
28
+ return "\n" if node.name == "br"
29
+
30
+ prefix = postfix = "\n\n" if node.name == "p"
31
+
32
+ "#{prefix}#{node.text}#{postfix}"
33
+ end
34
+
35
+ def language(node)
36
+ lang = language_from_highlight_class(node)
37
+ lang || language_from_confluence_class(node)
38
+ end
39
+
40
+ def language_from_highlight_class(node)
41
+ node.parent["class"].to_s[/highlight-([a-zA-Z0-9]+)/, 1]
42
+ end
43
+
44
+ def language_from_confluence_class(node)
45
+ node["class"].to_s[/brush:\s?(:?.*);/, 1]
46
+ end
47
+ end
48
+
49
+ register :pre, Pre.new
50
+ end
51
+ end
@@ -0,0 +1,12 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Q < Base
4
+ def to_coradoc(node, state = {})
5
+ content = treat_children(node, state)
6
+ Coradoc::Element::Inline::Quotation.new(content)
7
+ end
8
+ end
9
+
10
+ register :q, Q.new
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Strong < Markup
4
+ def coradoc_class
5
+ Coradoc::Element::Inline::Bold
6
+ end
7
+
8
+ def markup_ancestor_tag_names
9
+ %w[strong b]
10
+ end
11
+ end
12
+
13
+ register :strong, Strong.new
14
+ register :b, Strong.new
15
+ end
16
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Sub < Base
4
+ def to_coradoc(node, state = {})
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?
10
+
11
+ e = Coradoc::Element::Inline::Subscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
13
+ end
14
+ end
15
+
16
+ register :sub, Sub.new
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Sup < Base
4
+ def to_coradoc(node, state = {})
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?
10
+
11
+ e = Coradoc::Element::Inline::Superscript.new(content)
12
+ [leading_whitespace, e, trailing_whitespace]
13
+ end
14
+ end
15
+
16
+ register :sup, Sup.new
17
+ end
18
+ end
@@ -0,0 +1,280 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Table < Base
4
+ def to_coradoc(node, state = {})
5
+ id = node["id"]
6
+ title = extract_title(node)
7
+ attrs = style(node)
8
+ content = treat_children_coradoc(node, state)
9
+ Coradoc::Element::Table.new(title, content, { id: id, attrs: attrs })
10
+ end
11
+
12
+ def extract_title(node)
13
+ title = node.at("./caption")
14
+ return "" if title.nil?
15
+
16
+ treat_children(title, {})
17
+ end
18
+
19
+ def frame(node)
20
+ case node["frame"]
21
+ when "void"
22
+ "none"
23
+ when "hsides"
24
+ "topbot"
25
+ when "vsides"
26
+ "sides"
27
+ when "box", "border"
28
+ "all"
29
+ end
30
+ end
31
+
32
+ def rules(node)
33
+ case node["rules"]
34
+ when "all"
35
+ "all"
36
+ when "rows"
37
+ "rows"
38
+ when "cols"
39
+ "cols"
40
+ when "none"
41
+ "none"
42
+ end
43
+ end
44
+
45
+ def style(node)
46
+ attrs = Coradoc::Element::AttributeList.new
47
+ # Width is disabled on tables for now. (From #88)
48
+ # attrs.add_named("width", node["width"]) if node["width"]
49
+
50
+ frame_attr = frame(node)
51
+ attrs.add_named("frame", frame_attr) if frame_attr
52
+
53
+ rules_attr = rules(node)
54
+ attrs.add_named("rules", rules_attr) if rules_attr
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
+
65
+ # This line should be removed.
66
+ return "" if attrs.empty?
67
+
68
+ attrs
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
276
+ end
277
+
278
+ register :table, Table.new
279
+ end
280
+ end
@@ -0,0 +1,77 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Td < Base
4
+ def to_coradoc(node, state = {})
5
+ # convert(node, state)
6
+ id = node["id"]
7
+ colrowattr = colrow(node["colspan"], node["rowspan"])
8
+ alignattr = alignstyle(node)
9
+ style = cellstyle(node)
10
+
11
+ singlepara = node.elements.size == 1 && node.elements.first.name == "p"
12
+ state[:tdsinglepara] = singlepara if singlepara
13
+
14
+ adoccell = node.at(".//ul | .//ol | .//pre | .//blockquote | .//br | .//hr | .//img[@src]") ||
15
+ node.at(".//p") && !singlepara
16
+
17
+ style = "a" if adoccell
18
+ content = treat_children_coradoc(node, state)
19
+ options = {}.tap do |hash|
20
+ hash[:id] = id
21
+ hash[:colrowattr] = colrowattr
22
+ hash[:alignattr] = alignattr
23
+ hash[:style] = style
24
+ hash[:content] = content
25
+ end
26
+
27
+ Coradoc::Element::Table::Cell.new(options)
28
+ end
29
+
30
+ def cellstyle(_node)
31
+ ""
32
+ end
33
+
34
+ def colrow(colspan, rowspan)
35
+ if colspan && rowspan && colspan != "1" && rowspan != "1"
36
+ "#{colspan}.#{rowspan}+"
37
+ elsif colspan && colspan != "1"
38
+ "#{colspan}+"
39
+ elsif rowspan && rowspan != "1"
40
+ ".#{rowspan}+"
41
+ else
42
+ ""
43
+ end
44
+ end
45
+
46
+ def alignstyle(node)
47
+ align = node["align"]
48
+ valign = node["valign"]
49
+ a = case align
50
+ when "left" then "<"
51
+ when "center" then "^"
52
+ when "right" then ">"
53
+ else
54
+ ""
55
+ end
56
+ v = case valign
57
+ when "top" then "<"
58
+ when "middle" then "^"
59
+ when "bottom" then ">"
60
+ else
61
+ ""
62
+ end
63
+ if align && valign
64
+ "#{a}.#{v}"
65
+ elsif align
66
+ a
67
+ elsif valign
68
+ ".#{v}"
69
+ else
70
+ ""
71
+ end
72
+ end
73
+ end
74
+
75
+ register :td, Td.new
76
+ end
77
+ end
@@ -0,0 +1,28 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Text < Base
4
+ def to_coradoc(node, state = {})
5
+ return treat_empty(node, state) if node.text.strip.empty?
6
+
7
+ Coradoc::Element::TextElement.new(node.text)
8
+ end
9
+
10
+ private
11
+
12
+ def treat_empty(node, state)
13
+ parent = node.parent.name.to_sym
14
+ if %i[ol ul].include?(parent) # Otherwise the identation is broken
15
+ ""
16
+ elsif state[:tdsinglepara]
17
+ ""
18
+ elsif node.text == " " # Regular whitespace text node
19
+ " "
20
+ else
21
+ ""
22
+ end
23
+ end
24
+ end
25
+
26
+ register :text, Text.new
27
+ end
28
+ end
@@ -0,0 +1,14 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Th < Td
4
+ def cellstyle(node)
5
+ # this is the header row
6
+ return "" if node.parent.previous_element.nil?
7
+
8
+ "h"
9
+ end
10
+ end
11
+
12
+ register :th, Th.new
13
+ end
14
+ end
@@ -0,0 +1,18 @@
1
+ module Coradoc::ReverseAdoc
2
+ module Converters
3
+ class Tr < Base
4
+ def to_coradoc(node, state = {})
5
+ content = treat_children_coradoc(node, state)
6
+ header = table_header_row?(node)
7
+ Coradoc::Element::Table::Row.new(content, header)
8
+ end
9
+
10
+ def table_header_row?(node)
11
+ # node.element_children.all? {|child| child.name.to_sym == :th}
12
+ node.previous_element.nil?
13
+ end
14
+ end
15
+
16
+ register :tr, Tr.new
17
+ end
18
+ end